diff --git a/.github/workflows/stream_flutter_workflow.yml b/.github/workflows/stream_flutter_workflow.yml index 7b5df2ba..95f48ee9 100644 --- a/.github/workflows/stream_flutter_workflow.yml +++ b/.github/workflows/stream_flutter_workflow.yml @@ -27,7 +27,7 @@ jobs: ./.github/workflows/scripts/install-tools.sh flutter pub global activate tuneup - name: 'Bootstrap Workspace' - run: melos bootstrap + run: melos bootstrap --verbose - name: 'Dart Analyze' run: | melos exec -c 3 --ignore="*example*" -- \ @@ -50,7 +50,7 @@ jobs: run: | ./.github/workflows/scripts/install-tools.sh - name: 'Bootstrap Workspace' - run: melos bootstrap + run: melos bootstrap --verbose - name: 'Dart' run: | melos exec -c 1 -- \ @@ -72,7 +72,7 @@ jobs: flutter pub global activate coverage flutter pub global activate remove_from_coverage - name: 'Bootstrap Workspace' - run: melos bootstrap + run: melos bootstrap --verbose - name: 'Dart Test' run: | cd packages/stream_chat diff --git a/melos.yaml b/melos.yaml index 6a6c74dc..3fcad03f 100644 --- a/melos.yaml +++ b/melos.yaml @@ -59,5 +59,4 @@ dev_dependencies: pedantic: 1.9.2 environment: - sdk: ">=2.7.0 <3.0.0" - flutter: ">=1.22.4 <2.0.0" \ No newline at end of file + sdk: ">=2.12.0 <3.0.0" \ No newline at end of file diff --git a/packages/stream_chat/CHANGELOG.md b/packages/stream_chat/CHANGELOG.md index d6d08a3c..2e6c3a78 100644 --- a/packages/stream_chat/CHANGELOG.md +++ b/packages/stream_chat/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.0.0-nullsafety.0 + +- Migrate this package to null safety +- Added typed filters + ## 1.5.3 - fix: `StreamChatClient.connect` returns quicker when you're using the persistence package diff --git a/packages/stream_chat/analysis_options.yaml b/packages/stream_chat/analysis_options.yaml index ddc691d0..c0e14133 100644 --- a/packages/stream_chat/analysis_options.yaml +++ b/packages/stream_chat/analysis_options.yaml @@ -3,7 +3,6 @@ analyzer: - lib/**/*.g.dart - lib/**/*.freezed.dart - example/* - - test/* linter: rules: - always_use_package_imports diff --git a/packages/stream_chat/build.yaml b/packages/stream_chat/build.yaml index ddbd70dd..d439bddb 100644 --- a/packages/stream_chat/build.yaml +++ b/packages/stream_chat/build.yaml @@ -4,5 +4,4 @@ targets: json_serializable: options: explicit_to_json: true - field_rename: snake - any_map: true + field_rename: snake \ No newline at end of file diff --git a/packages/stream_chat/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/stream_chat/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata index 1d526a16..919434a6 100644 --- a/packages/stream_chat/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ b/packages/stream_chat/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -2,6 +2,6 @@ + location = "self:"> diff --git a/packages/stream_chat/example/lib/main.dart b/packages/stream_chat/example/lib/main.dart index 2d0a529f..11e9779f 100644 --- a/packages/stream_chat/example/lib/main.dart +++ b/packages/stream_chat/example/lib/main.dart @@ -2,12 +2,9 @@ import 'package:flutter/material.dart'; import 'package:stream_chat/stream_chat.dart'; Future main() async { - /// Create a new instance of [StreamChatClient] passing the apikey obtained from your - /// project dashboard. - final client = StreamChatClient( - 'b67pax5b2wdq', - logLevel: Level.INFO, - ); + /// Create a new instance of [StreamChatClient] + /// by passing the apikey obtained from your project dashboard. + final client = StreamChatClient('b67pax5b2wdq', logLevel: Level.INFO); /// Set the current user. In a production scenario, this should be done using /// a backend to generate a user token using our server SDK. @@ -21,7 +18,7 @@ Future main() async { 'https://getstream.io/random_png/?id=cool-shadow-7&name=Cool+shadow', }, ), - 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiY29vbC1zaGFkb3ctNyJ9.gkOlCRb1qgy4joHPaxFwPOdXcGvSPvp6QY0S4mpRkVo', + '''eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiY29vbC1zaGFkb3ctNyJ9.gkOlCRb1qgy4joHPaxFwPOdXcGvSPvp6QY0S4mpRkVo''', ); /// Creates a channel using the type `messaging` and `godevs`. @@ -44,15 +41,16 @@ Future main() async { /// Example using Stream's Low Level Dart client. class StreamExample extends StatelessWidget { - /// To initialize this example, an instance of [client] and [channel] is required. + /// To initialize this example, an instance of + /// [client] and [channel] is required. const StreamExample({ - Key key, - @required this.client, - @required this.channel, + Key? key, + required this.client, + required this.channel, }) : super(key: key); - /// Instance of [StreamChatClient] we created earlier. This contains information about - /// our application and connection state. + /// Instance of [StreamChatClient] we created earlier. + /// This contains information about our application and connection state. final StreamChatClient client; /// The channel we'd like to observe and participate. @@ -71,28 +69,31 @@ class StreamExample extends StatelessWidget { /// containing the channel name and a [MessageView] displaying recent messages. class HomeScreen extends StatelessWidget { /// [HomeScreen] is constructed using the [Channel] we defined earlier. - const HomeScreen({Key key, @required this.channel}) : super(key: key); + const HomeScreen({ + Key? key, + required this.channel, + }) : super(key: key); /// Channel object containing the [Channel.id] we'd like to observe. final Channel channel; @override Widget build(BuildContext context) { - final messages = channel.state.channelStateStream; + final messages = channel.state!.channelStateStream; return Scaffold( appBar: AppBar( title: Text('Channel: ${channel.id}'), ), body: SafeArea( - child: StreamBuilder( + child: StreamBuilder( stream: messages, builder: ( BuildContext context, - AsyncSnapshot snapshot, + AsyncSnapshot snapshot, ) { if (snapshot.hasData && snapshot.data != null) { return MessageView( - messages: snapshot.data.messages.reversed.toList(), + messages: snapshot.data!.messages.reversed.toList(), channel: channel, ); } else if (snapshot.hasError) { @@ -104,8 +105,8 @@ class HomeScreen extends StatelessWidget { } return const Center( child: SizedBox( - width: 100.0, - height: 100.0, + width: 100, + height: 100, child: CircularProgressIndicator(), ), ); @@ -121,9 +122,9 @@ class HomeScreen extends StatelessWidget { class MessageView extends StatefulWidget { /// Message takes the latest list of messages and the current channel. const MessageView({ - Key key, - @required this.messages, - @required this.channel, + Key? key, + required this.messages, + required this.channel, }) : super(key: key); /// List of messages sent in the given channel. @@ -137,8 +138,8 @@ class MessageView extends StatefulWidget { } class _MessageViewState extends State { - TextEditingController _controller; - ScrollController _scrollController; + late final TextEditingController _controller; + late final ScrollController _scrollController; List get _messages => widget.messages; @@ -176,20 +177,20 @@ class _MessageViewState extends State { reverse: true, itemBuilder: (BuildContext context, int index) { final item = _messages[index]; - if (item.user.id == widget.channel.client.uid) { + if (item.user?.id == widget.channel.client.uid) { return Align( alignment: Alignment.centerRight, child: Padding( - padding: const EdgeInsets.all(8.0), - child: Text(item.text), + padding: const EdgeInsets.all(8), + child: Text(item.text ?? ''), ), ); } else { return Align( alignment: Alignment.centerLeft, child: Padding( - padding: const EdgeInsets.all(8.0), - child: Text(item.text), + padding: const EdgeInsets.all(8), + child: Text(item.text ?? ''), ), ); } @@ -197,7 +198,7 @@ class _MessageViewState extends State { ), ), Padding( - padding: const EdgeInsets.all(8.0), + padding: const EdgeInsets.all(8), child: Row( children: [ Expanded( @@ -245,7 +246,8 @@ class _MessageViewState extends State { } } -/// Helper extension for quickly retrieving the current user id from a [StreamChatClient]. +/// Helper extension for quickly retrieving +/// the current user id from a [StreamChatClient]. extension on StreamChatClient { - String get uid => state.user.id; + String get uid => state.user!.id; } diff --git a/packages/stream_chat/example/pubspec.yaml b/packages/stream_chat/example/pubspec.yaml index 2a8b7eb2..1b092b3e 100644 --- a/packages/stream_chat/example/pubspec.yaml +++ b/packages/stream_chat/example/pubspec.yaml @@ -1,21 +1,22 @@ name: example description: A new Flutter project. -publish_to: 'none' +publish_to: "none" version: 1.0.0+1 environment: - sdk: ">=2.7.0 <3.0.0" + sdk: '>=2.12.0 <3.0.0' dependencies: + cupertino_icons: ^1.0.0 flutter: sdk: flutter - cupertino_icons: ^1.0.0 - stream_chat: + stream_chat: path: ../ dev_dependencies: flutter_test: sdk: flutter + flutter: - uses-material-design: true \ No newline at end of file + uses-material-design: true diff --git a/packages/stream_chat/lib/src/api/channel.dart b/packages/stream_chat/lib/src/api/channel.dart index b424f6f1..1674e5eb 100644 --- a/packages/stream_chat/lib/src/api/channel.dart +++ b/packages/stream_chat/lib/src/api/channel.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:convert'; import 'dart:math'; +import 'package:collection/collection.dart' show IterableExtension; import 'package:dio/dio.dart'; import 'package:logging/logging.dart'; import 'package:rxdart/rxdart.dart'; @@ -18,52 +19,58 @@ class Channel { /// Create a channel client instance. Channel( this._client, - this.type, - this._id, - this._extraData, - ) : _cid = _id != null ? '$type:$_id' : null { + this._type, + this._id, { + Map extraData = const {}, + }) : _cid = _id != null ? '$_type:$_id' : null, + _extraData = extraData { _client.logger.info('New Channel instance not initialized created'); } /// Create a channel client instance from a [ChannelState] object - Channel.fromState(this._client, ChannelState channelState) { - _cid = channelState.channel.cid; - _id = channelState.channel.id; - type = channelState.channel.type; - + Channel.fromState(this._client, ChannelState channelState) + : assert( + channelState.channel != null, + 'No channel found inside channel state', + ), + _id = channelState.channel!.id, + _type = channelState.channel!.type, + _cid = channelState.channel!.cid, + _extraData = channelState.channel!.extraData { state = ChannelClientState(this, channelState); _initializedCompleter.complete(true); _client.logger.info('New Channel instance initialized created'); } /// This client state - ChannelClientState state; + ChannelClientState? state; /// The channel type - String type; + final String _type; - String _id; - String _cid; - Map _extraData; + String? _id; + String? _cid; + final Map _extraData; set extraData(Map extraData) { if (_initializedCompleter.isCompleted) { - throw Exception( - 'Once the channel is initialized you should use channel.update ' - 'to update channel data'); + throw StateError( + 'Once the channel is initialized you should use channel.update ' + 'to update channel data', + ); } - _extraData = extraData; + _extraData.addAll(extraData); } /// Returns true if the channel is muted bool get isMuted => _client.state.user?.channelMutes - ?.any((element) => element.channel.cid == cid) == + .any((element) => element.channel.cid == cid) == true; /// Returns true if the channel is muted as a stream - Stream get isMutedStream => _client.state.userStream?.map((event) => - event.channelMutes?.any((element) => element.channel.cid == cid) == true); + Stream? get isMutedStream => _client.state.userStream.map((event) => + event!.channelMutes.any((element) => element.channel.cid == cid) == true); /// True if the channel is a group bool get isGroup => memberCount != 2; @@ -72,85 +79,135 @@ class Channel { bool get isDistinct => id?.startsWith('!members') == true; /// Channel configuration - ChannelConfig get config => state?._channelState?.channel?.config; + ChannelConfig? get config { + _checkInitialized(); + return state?._channelState.channel?.config; + } /// Channel configuration as a stream - Stream get configStream => - state?.channelStateStream?.map((cs) => cs.channel?.config); + Stream? get configStream { + _checkInitialized(); + return state?.channelStateStream.map((cs) => cs.channel?.config); + } /// Channel user creator - User get createdBy => state?._channelState?.channel?.createdBy; + User? get createdBy { + _checkInitialized(); + return state?._channelState.channel?.createdBy; + } /// Channel user creator as a stream - Stream get createdByStream => - state?.channelStateStream?.map((cs) => cs.channel?.createdBy); + Stream? get createdByStream { + _checkInitialized(); + return state?.channelStateStream.map((cs) => cs.channel?.createdBy); + } /// Channel frozen status - bool get frozen => state?._channelState?.channel?.frozen; + bool? get frozen { + _checkInitialized(); + return state?._channelState.channel?.frozen; + } /// Channel frozen status as a stream - Stream get frozenStream => - state?.channelStateStream?.map((cs) => cs.channel?.frozen); + Stream? get frozenStream { + _checkInitialized(); + return state?.channelStateStream.map((cs) => cs.channel?.frozen); + } /// Channel creation date - DateTime get createdAt => state?._channelState?.channel?.createdAt; + DateTime? get createdAt { + _checkInitialized(); + return state?._channelState.channel?.createdAt; + } /// Channel creation date as a stream - Stream get createdAtStream => - state?.channelStateStream?.map((cs) => cs.channel?.createdAt); + Stream? get createdAtStream { + _checkInitialized(); + return state?.channelStateStream.map((cs) => cs.channel?.createdAt); + } /// Channel last message date - DateTime get lastMessageAt => state?._channelState?.channel?.lastMessageAt; + DateTime? get lastMessageAt { + _checkInitialized(); + + return state?._channelState.channel?.lastMessageAt; + } /// Channel last message date as a stream - Stream get lastMessageAtStream => - state?.channelStateStream?.map((cs) => cs.channel?.lastMessageAt); + Stream? get lastMessageAtStream { + _checkInitialized(); + + return state?.channelStateStream.map((cs) => cs.channel?.lastMessageAt); + } /// Channel updated date - DateTime get updatedAt => state?._channelState?.channel?.updatedAt; + DateTime? get updatedAt { + _checkInitialized(); + + return state?._channelState.channel?.updatedAt; + } /// Channel updated date as a stream - Stream get updatedAtStream => - state?.channelStateStream?.map((cs) => cs.channel?.updatedAt); + Stream? get updatedAtStream { + _checkInitialized(); + + return state?.channelStateStream.map((cs) => cs.channel?.updatedAt); + } /// Channel deletion date - DateTime get deletedAt => state?._channelState?.channel?.deletedAt; + DateTime? get deletedAt { + _checkInitialized(); + + return state?._channelState.channel?.deletedAt; + } /// Channel deletion date as a stream - Stream get deletedAtStream => - state?.channelStateStream?.map((cs) => cs.channel?.deletedAt); + Stream? get deletedAtStream { + _checkInitialized(); + + return state?.channelStateStream.map((cs) => cs.channel?.deletedAt); + } /// Channel member count - int get memberCount => state?._channelState?.channel?.memberCount; + int? get memberCount { + _checkInitialized(); + + return state?._channelState.channel?.memberCount; + } /// Channel member count as a stream - Stream get memberCountStream => - state?.channelStateStream?.map((cs) => cs.channel?.memberCount); + Stream? get memberCountStream { + _checkInitialized(); + + return state?.channelStateStream.map((cs) => cs.channel?.memberCount); + } /// Channel id - String get id => state?._channelState?.channel?.id ?? _id; + String? get id => state?._channelState.channel?.id ?? _id; - /// Channel id as a stream - Stream get idStream => - state?.channelStateStream?.map((cs) => cs.channel?.id ?? _id); + /// Channel type + String get type => state?._channelState.channel?.type ?? _type; /// Channel cid - String get cid => state?._channelState?.channel?.cid ?? _cid; + String? get cid => state?._channelState.channel?.cid ?? _cid; /// Channel team - String get team => state?._channelState?.channel?.team; - - /// Channel cid as a stream - Stream get cidStream => - state?.channelStateStream?.map((cs) => cs.channel?.cid ?? _cid); + String? get team { + _checkInitialized(); + return state?._channelState.channel?.team; + } /// Channel extra data Map get extraData => - state?._channelState?.channel?.extraData ?? _extraData; + state?._channelState.channel?.extraData ?? _extraData; /// Channel extra data as a stream - Stream> get extraDataStream => - state?.channelStateStream?.map((cs) => cs.channel?.extraData); + Stream> get extraDataStream { + _checkInitialized(); + return state!.channelStateStream.map( + (cs) => cs.channel?.extraData ?? _extraData, + ); + } /// The main Stream chat client StreamChatClient get client => _client; @@ -174,7 +231,7 @@ class Channel { /// Optionally, provide a [reason] for the cancellation. void cancelAttachmentUpload( String attachmentId, { - String reason, + String? reason, }) { final cancelToken = _cancelableAttachmentUploadRequest[attachmentId]; if (cancelToken == null) { @@ -195,9 +252,8 @@ class Channel { String messageId, Iterable attachmentIds, ) { - final message = state.messages.firstWhere( + final message = state!.messages.firstWhereOrNull( (it) => it.id == messageId, - orElse: () => null, ); if (message == null) { @@ -248,13 +304,13 @@ class Channel { Future future; if (isImage) { future = sendImage( - it.file, + it.file!, onSendProgress: onSendProgress, cancelToken: cancelToken, ).then((it) => it.file); } else { future = sendFile( - it.file, + it.file!, onSendProgress: onSendProgress, cancelToken: cancelToken, ).then((it) => it.file); @@ -283,7 +339,7 @@ class Channel { it.copyWith(uploadState: UploadState.failed(error: e.toString())), ); }).whenComplete(() { - throttledUpdateAttachment?.cancel(); + throttledUpdateAttachment.cancel(); _cancelableAttachmentUploadRequest.remove(it.id); }); })).whenComplete(() { @@ -297,43 +353,34 @@ class Channel { /// Waits for a [_messageAttachmentsUploadCompleter] to complete /// before actually sending the message. Future sendMessage(Message message) async { + _checkInitialized(); // Cancelling previous completer in case it's called again in the process // Eg. Updating the message while the previous call is in progress. _messageAttachmentsUploadCompleter .remove(message.id) ?.completeError('Message Cancelled'); - final quotedMessage = state?.messages?.firstWhere( - (m) => m.id == message?.quotedMessageId, - orElse: () => null, + final quotedMessage = state!.messages.firstWhereOrNull( + (m) => m.id == message.quotedMessageId, ); // ignore: parameter_assignments message = message.copyWith( - createdAt: message.createdAt ?? DateTime.now(), + createdAt: message.createdAt, user: _client.state.user, quotedMessage: quotedMessage, status: MessageSendingStatus.sending, - attachments: message.attachments?.map( + attachments: message.attachments.map( (it) { if (it.uploadState.isSuccess) return it; return it.copyWith(uploadState: const UploadState.preparing()); }, - )?.toList(), + ).toList(), ); - if (message.parentId != null && message.id == null) { - final parentMessage = - state.messages.firstWhere((m) => m.id == message.parentId); - - state?.addMessage(parentMessage.copyWith( - replyCount: parentMessage.replyCount + 1, - )); - } - - state?.addMessage(message); + state!.addMessage(message); try { - if (message.attachments?.any((it) => !it.uploadState.isSuccess) == true) { + if (message.attachments.any((it) => !it.uploadState.isSuccess) == true) { final attachmentsUploadCompleter = Completer(); _messageAttachmentsUploadCompleter[message.id] = attachmentsUploadCompleter; @@ -348,12 +395,12 @@ class Channel { message = await attachmentsUploadCompleter.future; } - final response = await _client.sendMessage(message, id, type); - state?.addMessage(response.message); + final response = await _client.sendMessage(message, id!, type); + state!.addMessage(response.message); return response; } catch (error) { - if (error is DioError && error.type != DioErrorType.RESPONSE) { - state?.retryQueue?.add([message]); + if (error is DioError && error.type != DioErrorType.response) { + state!.retryQueue?.add([message]); } rethrow; } @@ -372,19 +419,19 @@ class Channel { // ignore: parameter_assignments message = message.copyWith( status: MessageSendingStatus.updating, - updatedAt: message.updatedAt ?? DateTime.now(), - attachments: message.attachments?.map( + updatedAt: message.updatedAt, + attachments: message.attachments.map( (it) { if (it.uploadState.isSuccess) return it; return it.copyWith(uploadState: const UploadState.preparing()); }, - )?.toList(), + ).toList(), ); state?.addMessage(message); try { - if (message.attachments?.any((it) => !it.uploadState.isSuccess) == true) { + if (message.attachments.any((it) => !it.uploadState.isSuccess) == true) { final attachmentsUploadCompleter = Completer(); _messageAttachmentsUploadCompleter[message.id] = attachmentsUploadCompleter; @@ -400,12 +447,16 @@ class Channel { } final response = await _client.updateMessage(message); - state?.addMessage(response?.message?.copyWith( + + final m = response.message.copyWith( ownReactions: message.ownReactions, - )); + ); + + state?.addMessage(m); + return response; } catch (error) { - if (error is DioError && error.type != DioErrorType.RESPONSE) { + if (error is DioError && error.type != DioErrorType.response) { state?.retryQueue?.add([message]); } rethrow; @@ -417,7 +468,7 @@ class Channel { // Directly deleting the local messages which are not yet sent to server if (message.status == MessageSendingStatus.sending || message.status == MessageSendingStatus.failed) { - state.addMessage(message.copyWith( + state!.addMessage(message.copyWith( type: 'deleted', status: MessageSendingStatus.sent, )); @@ -446,7 +497,7 @@ class Channel { return response; } catch (error) { - if (error is DioError && error.type != DioErrorType.RESPONSE) { + if (error is DioError && error.type != DioErrorType.response) { state?.retryQueue?.add([message]); } rethrow; @@ -456,18 +507,18 @@ class Channel { /// Pins provided message Future pinMessage( Message message, - Object timeoutOrExpirationDate, + Object? timeoutOrExpirationDate, ) { assert(() { if (timeoutOrExpirationDate is! DateTime && - timeoutOrExpirationDate is! num && - timeoutOrExpirationDate != null) { + timeoutOrExpirationDate != null && + timeoutOrExpirationDate is! num) { throw ArgumentError('Invalid timeout or Expiration date'); } return true; }(), 'Check for invalid token or expiration date'); - DateTime pinExpires; + DateTime? pinExpires; if (timeoutOrExpirationDate is DateTime) { pinExpires = timeoutOrExpirationDate; } else if (timeoutOrExpirationDate is num) { @@ -490,63 +541,79 @@ class Channel { /// Send a file to this channel Future sendFile( AttachmentFile file, { - ProgressCallback onSendProgress, - CancelToken cancelToken, - }) => - _client.sendFile( - file, - id, - type, - onSendProgress: onSendProgress, - cancelToken: cancelToken, - ); + ProgressCallback? onSendProgress, + CancelToken? cancelToken, + }) { + _checkInitialized(); + return _client.sendFile( + file, + id!, + type, + onSendProgress: onSendProgress, + cancelToken: cancelToken, + ); + } /// Send an image to this channel Future sendImage( AttachmentFile file, { - ProgressCallback onSendProgress, - CancelToken cancelToken, - }) => - _client.sendImage( - file, - id, - type, - onSendProgress: onSendProgress, - cancelToken: cancelToken, - ); + ProgressCallback? onSendProgress, + CancelToken? cancelToken, + }) { + _checkInitialized(); + return _client.sendImage( + file, + id!, + type, + onSendProgress: onSendProgress, + cancelToken: cancelToken, + ); + } /// A message search. Future search({ - String query, - Map messageFilters, - List sort, - PaginationParams paginationParams, - }) => - _client.search( - { - 'cid': { - r'$in': [cid], - }, - }, - sort: sort, - query: query, - paginationParams: paginationParams, - messageFilters: messageFilters, - ); + String? query, + Filter? messageFilters, + List? sort, + PaginationParams? paginationParams, + }) { + _checkInitialized(); + return _client.search( + Filter.in_('cid', [cid!]), + sort: sort, + query: query, + paginationParams: paginationParams, + messageFilters: messageFilters, + ); + } /// Delete a file from this channel Future deleteFile( String url, { - CancelToken cancelToken, - }) => - _client.deleteFile(url, id, type, cancelToken: cancelToken); + CancelToken? cancelToken, + }) { + _checkInitialized(); + return _client.deleteFile( + url, + id!, + type, + cancelToken: cancelToken, + ); + } /// Delete an image from this channel Future deleteImage( String url, { - CancelToken cancelToken, - }) => - _client.deleteImage(url, id, type, cancelToken: cancelToken); + CancelToken? cancelToken, + }) { + _checkInitialized(); + return _client.deleteImage( + url, + id!, + type, + cancelToken: cancelToken, + ); + } /// Send an event on this channel Future sendEvent(Event event) { @@ -554,7 +621,7 @@ class Channel { return _client.post( '$_channelURL/event', data: {'event': event.toJson()}, - ).then((res) => _client.decode(res.data, EmptyResponse.fromJson)); + ).then((res) => _client.decode(res.data, EmptyResponse.fromJson)!); } /// Send a reaction to this channel @@ -562,23 +629,24 @@ class Channel { Future sendReaction( Message message, String type, { - Map extraData = const {}, + Map extraData = const {}, bool enforceUnique = false, }) async { + _checkInitialized(); final messageId = message.id; final now = DateTime.now(); final user = _client.state.user; final latestReactions = [...message.latestReactions ?? []]; if (enforceUnique) { - latestReactions.removeWhere((it) => it.userId == user.id); + latestReactions.removeWhere((it) => it.userId == user!.id); } final newReaction = Reaction( messageId: messageId, createdAt: now, type: type, - user: user, + user: user!, score: 1, extraData: extraData, ); @@ -589,7 +657,7 @@ class Channel { ..removeWhere((it) => it.userId != user.id); final newMessage = message.copyWith( - reactionCounts: {...message?.reactionCounts ?? {}} + reactionCounts: {...message.reactionCounts ?? {}} ..update(type, (value) { if (enforceUnique) return value; return value + 1; @@ -649,8 +717,8 @@ class Channel { r.type == reaction.type && r.messageId == reaction.messageId); - final ownReactions = [...latestReactions ?? []] - ..removeWhere((it) => it.userId != user.id); + final ownReactions = [...latestReactions] + ..removeWhere((it) => it.userId != user!.id); final newMessage = message.copyWith( reactionCounts: reactionCounts..removeWhere((_, value) => value == 0), @@ -675,7 +743,7 @@ class Channel { /// Edit the channel custom data Future update( Map channelData, [ - Message updateMessage, + Message? updateMessage, ]) async { final response = await _client.post(_channelURL, data: { if (updateMessage != null) @@ -705,14 +773,14 @@ class Channel { } /// Accept invitation to the channel - Future acceptInvite([Message message]) async { + Future acceptInvite([Message? message]) async { final res = await _client.post(_channelURL, data: {'accept_invite': true, 'message': message?.toJson()}); return _client.decode(res.data, AcceptInviteResponse.fromJson); } /// Reject invitation to the channel - Future rejectInvite([Message message]) async { + Future rejectInvite([Message? message]) async { final res = await _client.post(_channelURL, data: {'reject_invite': true, 'message': message?.toJson()}); return _client.decode(res.data, RejectInviteResponse.fromJson); @@ -721,7 +789,7 @@ class Channel { /// Add members to the channel Future addMembers( List memberIds, [ - Message message, + Message? message, ]) async { final res = await _client.post(_channelURL, data: { 'add_members': memberIds, @@ -733,7 +801,7 @@ class Channel { /// Invite members to the channel Future inviteMembers( List memberIds, [ - Message message, + Message? message, ]) async { final res = await _client.post(_channelURL, data: { 'invites': memberIds, @@ -745,7 +813,7 @@ class Channel { /// Remove members from the channel Future removeMembers( List memberIds, [ - Message message, + Message? message, ]) async { final res = await _client.post(_channelURL, data: { 'remove_members': memberIds, @@ -772,31 +840,30 @@ class Channel { final res = _client.decode(response.data, SendActionResponse.fromJson); if (res.message != null) { - state.addMessage(res.message); + state!.addMessage(res.message!); } else { - final oldIndex = state.messages?.indexWhere((m) => m.id == messageId); + final oldIndex = state!.messages.indexWhere((m) => m.id == messageId); - Message oldMessage; - if (oldIndex != null && oldIndex != -1) { - oldMessage = state.messages[oldIndex]; - state.updateChannelState(state._channelState.copyWith( - messages: state.messages..remove(oldMessage), + Message? oldMessage; + if (oldIndex != -1) { + oldMessage = state!.messages[oldIndex]; + state!.updateChannelState(state!._channelState.copyWith( + messages: state?.messages?..remove(oldMessage), )); } else { - oldMessage = state.threads.values + oldMessage = state!.threads.values .expand((messages) => messages) - .firstWhere((m) => m.id == messageId, orElse: () => null); + .firstWhereOrNull((m) => m.id == messageId); if (oldMessage?.parentId != null) { - final parentMessage = state.messages.firstWhere( - (element) => element.id == oldMessage.parentId, - orElse: () => null, + final parentMessage = state!.messages.firstWhereOrNull( + (element) => element.id == oldMessage!.parentId, ); if (parentMessage != null) { - state.addMessage(parentMessage.copyWith( - replyCount: parentMessage.replyCount - 1)); + state!.addMessage(parentMessage.copyWith( + replyCount: parentMessage.replyCount! - 1)); } - state.updateThreadInfo(oldMessage.parentId, - state.threads[oldMessage.parentId]..remove(oldMessage)); + state!.updateThreadInfo(oldMessage!.parentId!, + state!.threads[oldMessage.parentId!]!..remove(oldMessage)); } } @@ -809,9 +876,9 @@ class Channel { /// Mark all channel messages as read Future markRead() async { _checkInitialized(); - client.state.totalUnreadCount = - max(0, (client.state.totalUnreadCount ?? 0) - (state.unreadCount ?? 0)); - state._unreadCountController.add(0); + client.state.totalUnreadCount = max( + 0, (client.state.totalUnreadCount ?? 0) - (state!.unreadCount ?? 0)); + state!._unreadCountController.add(0); final response = await _client.post('$_channelURL/read', data: {}); return _client.decode(response.data, EmptyResponse.fromJson); } @@ -845,7 +912,10 @@ class Channel { void _initState(ChannelState channelState) { state = ChannelClientState(this, channelState); - client.state.channels[cid] = this; + + if (cid != null) { + client.state.channels[cid!] = this; + } if (!_initializedCompleter.isCompleted) { _initializedCompleter.complete(true); } @@ -857,7 +927,7 @@ class Channel { '$_channelURL/stop-watching', data: {}, ); - return _client.decode(response?.data, EmptyResponse.fromJson); + return _client.decode(response.data, EmptyResponse.fromJson); } /// List the message replies for a parent message @@ -918,7 +988,9 @@ class Channel { GetMessagesByIdResponse.fromJson, ); - state?.updateChannelState(ChannelState(messages: res.messages)); + final messages = res.messages; + + state?.updateChannelState(ChannelState(messages: messages)); return res; } @@ -952,15 +1024,13 @@ class Channel { /// in the offline storage Future query({ Map options = const {}, - PaginationParams messagesPagination, - PaginationParams membersPagination, - PaginationParams watchersPagination, + PaginationParams? messagesPagination, + PaginationParams? membersPagination, + PaginationParams? watchersPagination, bool preferOffline = false, }) async { var path = '/channels/$type'; - if (id != null) { - path = '$path/$id'; - } + if (id != null) path = '$path/$id'; path = '$path/query'; final payload = Map.from({ @@ -968,7 +1038,7 @@ class Channel { }) ..addAll(options); - if (_extraData != null) { + if (_extraData.isNotEmpty) { payload['data'] = _extraData; } @@ -984,11 +1054,11 @@ class Channel { if (preferOffline && cid != null) { final updatedState = - await _client.chatPersistenceClient?.getChannelStateByCid( - cid, + (await _client.chatPersistenceClient?.getChannelStateByCid( + cid!, messagePagination: messagesPagination, - ); - if (updatedState != null && updatedState.messages.isNotEmpty) { + ))!; + if (updatedState.messages.isNotEmpty) { if (state == null) { _initState(updatedState); } else { @@ -1003,8 +1073,8 @@ class Channel { final updatedState = _client.decode(response.data, ChannelState.fromJson); if (_id == null) { - _id = updatedState.channel.id; - _cid = updatedState.channel.cid; + _id = updatedState.channel!.id; + _cid = updatedState.channel!.cid; } state?.updateChannelState(updatedState); @@ -1013,8 +1083,8 @@ class Channel { if (!_client.persistenceEnabled) { rethrow; } - return _client.chatPersistenceClient?.getChannelStateByCid( - cid, + return _client.chatPersistenceClient!.getChannelStateByCid( + cid!, messagePagination: messagesPagination, ); } @@ -1022,9 +1092,9 @@ class Channel { /// Query channel members Future queryMembers({ - Map filter, - List sort, - PaginationParams pagination, + Filter? filter, + List? sort, + PaginationParams? pagination, }) async { final payload = { 'sort': sort, @@ -1038,8 +1108,8 @@ class Channel { if (id != null) { payload['id'] = id; - } else if (state?.members?.isNotEmpty == true) { - payload['members'] = state.members; + } else if (state?.members.isNotEmpty == true) { + payload['members'] = state!.members; } final rawRes = await _client.get('/members', queryParameters: { @@ -1050,7 +1120,7 @@ class Channel { } /// Mutes the channel - Future mute({Duration expiration}) async { + Future mute({Duration? expiration}) async { final response = await _client.post('/moderation/mute/channel', data: { 'channel_cid': cid, if (expiration != null) 'expiration': expiration.inMilliseconds, @@ -1121,8 +1191,11 @@ class Channel { .post('$_channelURL/hide', data: {'clear_history': clearHistory}); if (clearHistory == true) { - state.truncate(); - await _client.chatPersistenceClient?.deleteMessageByCid(_cid); + state!.truncate(); + final cid = _cid; + if (cid != null) { + await _client.chatPersistenceClient?.deleteMessageByCid(cid); + } } return _client.decode(response.data, EmptyResponse.fromJson); @@ -1139,10 +1212,10 @@ class Channel { /// channel. Pass an eventType as parameter in order to filter just a type /// of event Stream on([ - String eventType, - String eventType2, - String eventType3, - String eventType4, + String? eventType, + String? eventType2, + String? eventType3, + String? eventType4, ]) => _client .on( @@ -1153,11 +1226,11 @@ class Channel { ) .where((e) => e.cid == cid); - DateTime _lastTypingEvent; + DateTime? _lastTypingEvent; /// First of the [EventType.typingStart] and [EventType.typingStop] events /// based on the users keystrokes. Call this on every keystroke. - Future keyStroke([String parentId]) async { + Future keyStroke([String? parentId]) async { if (config?.typingEvents == false) { return; } @@ -1166,7 +1239,7 @@ class Channel { final now = DateTime.now(); if (_lastTypingEvent == null || - now.difference(_lastTypingEvent).inSeconds >= 2) { + now.difference(_lastTypingEvent!).inSeconds >= 2) { _lastTypingEvent = now; await sendEvent(Event( type: EventType.typingStart, @@ -1176,7 +1249,7 @@ class Channel { } /// Sets last typing to null and sends the typing.stop event - Future stopTyping([String parentId]) async { + Future stopTyping([String? parentId]) async { if (config?.typingEvents == false) { return; } @@ -1191,15 +1264,15 @@ class Channel { /// Call this method to dispose the channel client void dispose() { - state.dispose(); + state?.dispose(); } void _checkInitialized() { - if (!_initializedCompleter.isCompleted) { - throw Exception( - "Channel $cid hasn't been initialized yet. Make sure to call .watch()" - ' or to instantiate the client using [Channel.fromState]'); - } + assert( + _initializedCompleter.isCompleted, + "Channel $cid hasn't been initialized yet. Make sure to call .watch()" + ' or to instantiate the client using [Channel.fromState]', + ); } } @@ -1211,7 +1284,7 @@ class ChannelClientState { ChannelState channelState, //ignore: unnecessary_parenthesis ) : _debouncedUpdatePersistenceChannelState = ((ChannelState state) => - _channel?._client?.chatPersistenceClient + _channel._client.chatPersistenceClient ?.updateChannelState(state)) .debounced(const Duration(seconds: 1)) { retryQueue = RetryQueue( @@ -1252,13 +1325,13 @@ class ChannelClientState { _startCleaningPinnedMessages(); _channel._client.chatPersistenceClient - ?.getChannelThreads(_channel.cid) - ?.then((threads) { + ?.getChannelThreads(_channel.cid!) + .then((threads) { _threads = threads; - })?.then((_) { + }).then((_) { _channel._client.chatPersistenceClient - ?.getChannelStateByCid(_channel.cid) - ?.then((state) { + ?.getChannelStateByCid(_channel.cid!) + .then((state) { // Replacing the persistence state members with the latest // `channelState.members` as they may have changes over the time. updateChannelState(state.copyWith(members: channelState.members)); @@ -1270,21 +1343,20 @@ class ChannelClientState { final _subscriptions = []; void _computeInitialUnread() { - final userRead = channelState?.read?.firstWhere( - (r) => r.user.id == _channel._client.state?.user?.id, - orElse: () => null, + final userRead = channelState.read.firstWhereOrNull( + (r) => r.user.id == _channel._client.state.user?.id, ); if (userRead != null) { - _unreadCountController.add(userRead.unreadMessages ?? 0); + _unreadCountController.add(userRead.unreadMessages); } } void _checkExpiredAttachmentMessages(ChannelState channelState) { final expiredAttachmentMessagesId = channelState.messages - ?.where((m) => + .where((m) => !_updatedMessagesIds.contains(m.id) && - m.attachments?.isNotEmpty == true && - m.attachments?.any((e) { + m.attachments.isNotEmpty == true && + m.attachments.any((e) { final url = e.imageUrl ?? e.assetUrl; if (url == null || !url.contains('')) { return false; @@ -1295,13 +1367,13 @@ class ChannelClientState { return false; } final expiration = - DateTime.parse(uri.queryParameters['Expires']); + DateTime.parse(uri.queryParameters['Expires']!); return expiration.isBefore(DateTime.now()); }) == true) - ?.map((e) => e.id) - ?.toList(); - if (expiredAttachmentMessagesId?.isNotEmpty == true) { + .map((e) => e.id) + .toList(); + if (expiredAttachmentMessagesId.isNotEmpty == true) { _channel.getMessagesById(expiredAttachmentMessagesId); _updatedMessagesIds.addAll(expiredAttachmentMessagesId); } @@ -1313,7 +1385,7 @@ class ChannelClientState { updateChannelState(channelState.copyWith( members: [ ...channelState.members, - member, + member!, ], )); })); @@ -1324,14 +1396,14 @@ class ChannelClientState { final user = e.user; updateChannelState(channelState.copyWith( members: List.from( - channelState.members..removeWhere((m) => m.userId == user.id)), + channelState.members..removeWhere((m) => m.userId == user!.id)), )); })); } void _listenChannelUpdated() { _subscriptions.add(_channel.on(EventType.channelUpdated).listen((Event e) { - final channel = e.channel; + final channel = e.channel!; updateChannelState(channelState.copyWith( channel: channel, members: channel.members, @@ -1343,7 +1415,7 @@ class ChannelClientState { _subscriptions.add(_channel .on(EventType.channelTruncated, EventType.notificationChannelTruncated) .listen((event) async { - final channel = event.channel; + final channel = event.channel!; await _channel._client.chatPersistenceClient ?.deleteMessageByCid(channel.cid); truncate(); @@ -1354,7 +1426,7 @@ class ChannelClientState { /// This flag should be managed by UI sdks. /// When false, any new message (received by WebSocket event /// - [EventType.messageNew]) will not be pushed on to message list. - bool get isUpToDate => _isUpToDateController.value; + bool get isUpToDate => _isUpToDateController.value ?? true; set isUpToDate(bool isUpToDate) => _isUpToDateController.add(isUpToDate); @@ -1365,7 +1437,7 @@ class ChannelClientState { BehaviorSubject.seeded(true); /// The retry queue associated to this channel - RetryQueue retryQueue; + RetryQueue? retryQueue; /// Retry failed message Future retryFailedMessages() async { @@ -1373,7 +1445,6 @@ class ChannelClientState { [...messages, ...threads.values.expand((v) => v)] .where( (message) => - message.status != null && message.status != MessageSendingStatus.sent && message.createdAt.isBefore( DateTime.now().subtract( @@ -1385,14 +1456,14 @@ class ChannelClientState { ) .toList(); - retryQueue.add(failedMessages); + retryQueue!.add(failedMessages); } void _listenReactionDeleted() { _subscriptions.add(_channel.on(EventType.reactionDeleted).listen((event) { - final userId = _channel.client.state.user.id; - final message = event.message.copyWith( - ownReactions: [...event.message.latestReactions] + final userId = _channel.client.state.user!.id; + final message = event.message!.copyWith( + ownReactions: [...event.message!.latestReactions!] ..removeWhere((it) => it.userId != userId), ); addMessage(message); @@ -1401,9 +1472,9 @@ class ChannelClientState { void _listenReactions() { _subscriptions.add(_channel.on(EventType.reactionNew).listen((event) { - final userId = _channel.client.state.user.id; - final message = event.message.copyWith( - ownReactions: [...event.message.latestReactions] + final userId = _channel.client.state.user!.id; + final message = event.message!.copyWith( + ownReactions: [...event.message!.latestReactions!] ..removeWhere((it) => it.userId != userId), ); addMessage(message); @@ -1417,9 +1488,9 @@ class ChannelClientState { EventType.reactionUpdated, ) .listen((event) { - final userId = _channel.client.state.user.id; - final message = event.message.copyWith( - ownReactions: [...event.message.latestReactions] + final userId = _channel.client.state.user!.id; + final message = event.message!.copyWith( + ownReactions: [...event.message!.latestReactions!] ..removeWhere((it) => it.userId != userId), ); addMessage(message); @@ -1427,7 +1498,7 @@ class ChannelClientState { if (message.pinned == true) { _channelState = _channelState.copyWith( pinnedMessages: [ - ..._channelState.pinnedMessages ?? [], + ..._channelState.pinnedMessages, message, ], ); @@ -1437,7 +1508,7 @@ class ChannelClientState { void _listenMessageDeleted() { _subscriptions.add(_channel.on(EventType.messageDeleted).listen((event) { - final message = event.message; + final message = event.message!; addMessage(message); })); } @@ -1449,14 +1520,14 @@ class ChannelClientState { EventType.notificationMessageNew, ) .listen((event) { - final message = event.message; + final message = event.message!; if (isUpToDate || (message.parentId != null && message.showInChannel != true)) { addMessage(message); } if (_countMessageAsUnread(message)) { - _unreadCountController.add(_unreadCountController.value + 1); + _unreadCountController.add(_unreadCountController.value! + 1); } })); } @@ -1467,7 +1538,7 @@ class ChannelClientState { final newMessages = List.from(_channelState.messages); final oldIndex = newMessages.indexWhere((m) => m.id == message.id); if (oldIndex != -1) { - Message m; + Message? m; if (message.quotedMessageId != null && message.quotedMessage == null) { final oldMessage = newMessages[oldIndex]; m = message.copyWith( @@ -1481,19 +1552,19 @@ class ChannelClientState { _channelState = _channelState.copyWith( messages: newMessages, - channel: _channelState.channel.copyWith( + channel: _channelState.channel?.copyWith( lastMessageAt: message.createdAt, ), ); } if (message.parentId != null) { - updateThreadInfo(message.parentId, [message]); + updateThreadInfo(message.parentId!, [message]); } } void _listenReadEvents() { - if (_channel.config?.readEvents == false) { + if (_channelState.channel?.config.readEvents == false) { return; } @@ -1505,18 +1576,19 @@ class ChannelClientState { ) .listen( (event) { - final readList = List.from(_channelState?.read ?? []); + final readList = List.from(_channelState.read); final userReadIndex = - read?.indexWhere((r) => r.user.id == event.user.id); + read?.indexWhere((r) => r.user.id == event.user!.id); if (userReadIndex != null && userReadIndex != -1) { final userRead = readList.removeAt(userReadIndex); - if (userRead.user?.id == _channel._client.state.user.id) { + if (userRead.user.id == _channel._client.state.user!.id) { _unreadCountController.add(0); } readList.add(Read( - user: event.user, - lastRead: event.createdAt, + user: event.user!, + lastRead: event.createdAt!, + unreadMessages: event.totalUnreadCount!, )); _channelState = _channelState.copyWith(read: readList); } @@ -1529,44 +1601,44 @@ class ChannelClientState { List get messages => _channelState.messages; /// Channel message list as a stream - Stream> get messagesStream => + Stream?> get messagesStream => channelStateStream.map((cs) => cs.messages); /// Channel pinned message list - List get pinnedMessages => _channelState.pinnedMessages?.toList(); + List? get pinnedMessages => _channelState.pinnedMessages.toList(); /// Channel pinned message list as a stream - Stream> get pinnedMessagesStream => - channelStateStream.map((cs) => cs.pinnedMessages?.toList()); + Stream?> get pinnedMessagesStream => + channelStateStream.map((cs) => cs.pinnedMessages.toList()); /// Get channel last message - Message get lastMessage => _channelState.messages?.isNotEmpty == true + Message? get lastMessage => _channelState.messages.isNotEmpty == true ? _channelState.messages.last : null; /// Get channel last message - Stream get lastMessageStream => messagesStream - .map((event) => event?.isNotEmpty == true ? event.last : null); + Stream get lastMessageStream => messagesStream + .map((event) => event?.isNotEmpty == true ? event!.last : null); /// Channel members list List get members => _channelState.members - .map((e) => e.copyWith(user: _channel.client.state.users[e.user.id])) + .map((e) => e.copyWith(user: _channel.client.state.users[e.user!.id])) .toList(); /// Channel members list as a stream Stream> get membersStream => CombineLatestStream.combine2< - List, Map, List>( + List?, Map, List>( channelStateStream.map((cs) => cs.members), _channel.client.state.usersStream, (members, users) => - members.map((e) => e.copyWith(user: users[e.user.id])).toList(), + members!.map((e) => e!.copyWith(user: users[e.user!.id])).toList(), ); /// Channel watcher count - int get watcherCount => _channelState.watcherCount; + int? get watcherCount => _channelState.watcherCount; /// Channel watcher count as a stream - Stream get watcherCountStream => + Stream get watcherCountStream => channelStateStream.map((cs) => cs.watcherCount); /// Channel watchers list @@ -1575,18 +1647,18 @@ class ChannelClientState { .toList(); /// Channel watchers list as a stream - Stream> get watchersStream => - CombineLatestStream.combine2, Map, List>( + Stream> get watchersStream => CombineLatestStream.combine2< + List?, Map, List>( channelStateStream.map((cs) => cs.watchers), _channel.client.state.usersStream, - (watchers, users) => watchers.map((e) => users[e.id] ?? e).toList(), + (watchers, users) => watchers!.map((e) => users[e.id] ?? e).toList(), ); /// Channel read list - List get read => _channelState.read; + List? get read => _channelState.read; /// Channel read list as a stream - Stream> get readStream => channelStateStream.map((cs) => cs.read); + Stream?> get readStream => channelStateStream.map((cs) => cs.read); final BehaviorSubject _unreadCountController = BehaviorSubject.seeded(0); @@ -1594,18 +1666,17 @@ class ChannelClientState { Stream get unreadCountStream => _unreadCountController.stream; /// Unread count getter - int get unreadCount => _unreadCountController.value; + int? get unreadCount => _unreadCountController.value; bool _countMessageAsUnread(Message message) { - final userId = _channel.client.state?.user?.id; - final userIsMuted = _channel.client.state?.user?.mutes?.firstWhere( - (m) => m.user?.id == message.user.id, - orElse: () => null, + final userId = _channel.client.state.user?.id; + final userIsMuted = _channel.client.state.user?.mutes.firstWhereOrNull( + (m) => m.user.id == message.user?.id, ) != null; return message.silent != true && message.shadowed != true && - message.user.id != userId && + message.user?.id != userId && !userIsMuted; } @@ -1618,12 +1689,12 @@ class ChannelClientState { ...newThreads[parentId] ?.where( (newMessage) => !messages.any((m) => m.id == newMessage.id)) - ?.toList() ?? + .toList() ?? [], ...messages, ]; - newThreads[parentId].sort(_sortByCreatedAt); + newThreads[parentId]!.sort(_sortByCreatedAt); } else { newThreads[parentId] = messages; } @@ -1643,40 +1714,37 @@ class ChannelClientState { /// Update channelState with updated information void updateChannelState(ChannelState updatedState) { final newMessages = [ - ...updatedState?.messages ?? [], - ..._channelState?.messages - ?.where((m) => - updatedState.messages - ?.any((newMessage) => newMessage.id == m.id) != - true) - ?.toList() ?? - [], + ...updatedState.messages, + ..._channelState.messages + .where((m) => + updatedState.messages + .any((newMessage) => newMessage.id == m.id) != + true) + .toList(), ]..sort(_sortByCreatedAt); final newWatchers = [ - ...updatedState?.watchers ?? [], - ..._channelState?.watchers - ?.where((w) => - updatedState.watchers - ?.any((newWatcher) => newWatcher.id == w.id) != - true) - ?.toList() ?? - [], + ...updatedState.watchers, + ..._channelState.watchers + .where((w) => + updatedState.watchers + .any((newWatcher) => newWatcher.id == w.id) != + true) + .toList(), ]; final newMembers = [ - ...updatedState?.members ?? [], + ...updatedState.members, ]; final newReads = [ - ...updatedState?.read ?? [], - ..._channelState?.read - ?.where((r) => - updatedState.read - ?.any((newRead) => newRead.user.id == r.user.id) != - true) - ?.toList() ?? - [], + ...updatedState.read, + ..._channelState.read + .where((r) => + updatedState.read + .any((newRead) => newRead.user.id == r.user.id) != + true) + .toList(), ]; _checkExpiredAttachmentMessages(updatedState); @@ -1692,37 +1760,29 @@ class ChannelClientState { ); } - int _sortByCreatedAt(a, b) { - if (a.createdAt == null) { - return 1; - } - - if (b.createdAt == null) { - return -1; - } - - return a.createdAt.compareTo(b.createdAt); - } + int _sortByCreatedAt(Message a, Message b) => + a.createdAt.compareTo(b.createdAt); /// The channel state related to this client - ChannelState get _channelState => _channelStateController.value; + ChannelState get _channelState => _channelStateController.value!; /// The channel state related to this client as a stream Stream get channelStateStream => _channelStateController.stream; /// The channel state related to this client - ChannelState get channelState => _channelStateController.value; - BehaviorSubject _channelStateController; + ChannelState get channelState => _channelStateController.value!; + late BehaviorSubject _channelStateController; final Debounce _debouncedUpdatePersistenceChannelState; set _channelState(ChannelState v) { _channelStateController.add(v); - _debouncedUpdatePersistenceChannelState?.call([v]); + _debouncedUpdatePersistenceChannelState.call([v]); } /// The channel threads related to this channel - Map> get threads => _threadsController.value; + Map> get threads => + _threadsController.value!.map((key, value) => MapEntry(key, value)); /// The channel threads related to this channel as a stream Stream>> get threadsStream => @@ -1732,14 +1792,14 @@ class ChannelClientState { set _threads(Map> v) { _channel._client.chatPersistenceClient?.updateMessages( - _channel.cid, + _channel.cid!, v.values.expand((v) => v).toList(), ); _threadsController.add(v); } /// Channel related typing users last value - List get typingEvents => _typingEventsController.value; + List get typingEvents => _typingEventsController.value!; /// Channel related typing users stream Stream> get typingEventsStream => _typingEventsController.stream; @@ -1750,7 +1810,7 @@ class ChannelClientState { final Map _typings = {}; void _listenTypingEvents() { - if (_channel.config?.typingEvents == false) { + if (_channelState.channel?.config.typingEvents == false) { return; } @@ -1758,9 +1818,12 @@ class ChannelClientState { ..add( _channel.on(EventType.typingStart).listen( (event) { - if (event.user.id != _channel.client.state.user.id) { - _typings[event.user] = DateTime.now(); - _typingEventsController.add(_typings.keys.toList()); + if (event.user != null) { + final user = event.user!; + if (user.id != _channel.client.state.user?.id) { + _typings[user] = DateTime.now(); + _typingEventsController.add(_typings.keys.toList()); + } } }, ), @@ -1768,9 +1831,12 @@ class ChannelClientState { ..add( _channel.on(EventType.typingStop).listen( (event) { - if (event.user.id != _channel.client.state.user.id) { - _typings.remove(event.user); - _typingEventsController.add(_typings.keys.toList()); + if (event.user != null) { + final user = event.user!; + if (user.id != _channel.client.state.user?.id) { + _typings.remove(event.user); + _typingEventsController.add(_typings.keys.toList()); + } } }, ), @@ -1780,12 +1846,12 @@ class ChannelClientState { .on() .where((event) => event.user != null && - members?.any((m) => m.userId == event.user.id) == true) + members.any((m) => m.userId == event.user!.id) == true) .listen( (event) { final newMembers = List.from(members); final oldMemberIndex = - newMembers.indexWhere((m) => m.userId == event.user.id); + newMembers.indexWhere((m) => m.userId == event.user!.id); if (oldMemberIndex > -1) { final oldMember = newMembers.removeAt(oldMemberIndex); updateChannelState(ChannelState( @@ -1802,10 +1868,10 @@ class ChannelClientState { ); } - Timer _cleaningTimer; + late Timer _cleaningTimer; void _startCleaning() { - if (_channel.config?.typingEvents == false) { + if (_channelState.channel?.config.typingEvents == false) { return; } @@ -1813,7 +1879,7 @@ class ChannelClientState { final now = DateTime.now(); if (_channel._lastTypingEvent != null && - now.difference(_channel._lastTypingEvent).inSeconds > 1) { + now.difference(_channel._lastTypingEvent!).inSeconds > 1) { _channel.stopTyping(); } @@ -1821,15 +1887,14 @@ class ChannelClientState { }); } - Timer _pinnedMessagesTimer; + late Timer _pinnedMessagesTimer; void _startCleaningPinnedMessages() { _pinnedMessagesTimer = Timer.periodic(const Duration(seconds: 30), (_) { final now = DateTime.now(); var expiredMessages = channelState.pinnedMessages - ?.where((m) => m.pinExpires?.isBefore(now) == true) - ?.toList() ?? - []; + .where((m) => m.pinExpires?.isBefore(now) == true) + .toList(); if (expiredMessages.isNotEmpty) { expiredMessages = expiredMessages .map((m) => m.copyWith( @@ -1839,7 +1904,7 @@ class ChannelClientState { .toList(); updateChannelState(_channelState.copyWith( - pinnedMessages: pinnedMessages.where(_pinIsValid()).toList(), + pinnedMessages: pinnedMessages!.where(_pinIsValid()).toList(), messages: expiredMessages, )); } @@ -1863,9 +1928,9 @@ class ChannelClientState { /// Call this method to dispose this object void dispose() { - _debouncedUpdatePersistenceChannelState?.cancel(); + _debouncedUpdatePersistenceChannelState.cancel(); _unreadCountController.close(); - retryQueue.dispose(); + retryQueue!.dispose(); _subscriptions.forEach((s) => s.cancel()); _channelStateController.close(); _isUpToDateController.close(); @@ -1878,5 +1943,5 @@ class ChannelClientState { bool Function(Message) _pinIsValid() { final now = DateTime.now(); - return (Message m) => m.pinExpires.isAfter(now); + return (Message m) => m.pinExpires!.isAfter(now); } diff --git a/packages/stream_chat/lib/src/api/requests.dart b/packages/stream_chat/lib/src/api/requests.dart index 51151fca..e78a80ec 100644 --- a/packages/stream_chat/lib/src/api/requests.dart +++ b/packages/stream_chat/lib/src/api/requests.dart @@ -34,7 +34,7 @@ class SortOption { /// Sorting field Comparator required for offline sorting @JsonKey(ignore: true) - final Comparator comparator; + final Comparator? comparator; /// Serialize model to json Map toJson() => _$SortOptionToJson(this); @@ -70,31 +70,31 @@ class PaginationParams { /// Filter on ids greater than the given value. @JsonKey(name: 'id_gt') - final String greaterThan; + final String? greaterThan; /// Filter on ids greater than or equal to the given value. @JsonKey(name: 'id_gte') - final String greaterThanOrEqual; + final String? greaterThanOrEqual; /// Filter on ids smaller than the given value. @JsonKey(name: 'id_lt') - final String lessThan; + final String? lessThan; /// Filter on ids smaller than or equal to the given value. @JsonKey(name: 'id_lte') - final String lessThanOrEqual; + final String? lessThanOrEqual; /// Serialize model to json Map toJson() => _$PaginationParamsToJson(this); /// Creates a copy of [PaginationParams] with specified attributes overridden. PaginationParams copyWith({ - int limit, - int offset, - String greaterThan, - String greaterThanOrEqual, - String lessThan, - String lessThanOrEqual, + int? limit, + int? offset, + String? greaterThan, + String? greaterThanOrEqual, + String? lessThan, + String? lessThanOrEqual, }) => PaginationParams( limit: limit ?? this.limit, @@ -106,6 +106,7 @@ class PaginationParams { ); @override + @JsonKey(ignore: true) int get hashCode => runtimeType.hashCode ^ limit.hashCode ^ diff --git a/packages/stream_chat/lib/src/api/requests.g.dart b/packages/stream_chat/lib/src/api/requests.g.dart index f020c1ea..1ec7b5fc 100644 --- a/packages/stream_chat/lib/src/api/requests.g.dart +++ b/packages/stream_chat/lib/src/api/requests.g.dart @@ -13,7 +13,10 @@ Map _$SortOptionToJson(SortOption instance) => }; Map _$PaginationParamsToJson(PaginationParams instance) { - final val = {}; + final val = { + 'limit': instance.limit, + 'offset': instance.offset, + }; void writeNotNull(String key, dynamic value) { if (value != null) { @@ -21,8 +24,6 @@ Map _$PaginationParamsToJson(PaginationParams instance) { } } - writeNotNull('limit', instance.limit); - writeNotNull('offset', instance.offset); writeNotNull('id_gt', instance.greaterThan); writeNotNull('id_gte', instance.greaterThanOrEqual); writeNotNull('id_lt', instance.lessThan); diff --git a/packages/stream_chat/lib/src/api/responses.dart b/packages/stream_chat/lib/src/api/responses.dart index 431a57e7..92493e8a 100644 --- a/packages/stream_chat/lib/src/api/responses.dart +++ b/packages/stream_chat/lib/src/api/responses.dart @@ -13,14 +13,15 @@ import 'package:stream_chat/src/models/user.dart'; part 'responses.g.dart'; class _BaseResponse { - String duration; + String? duration; } /// Model response for [StreamChatClient.resync] api call @JsonSerializable(createToJson: false) class SyncResponse extends _BaseResponse { /// The list of events - List events; + @JsonKey(defaultValue: []) + late List events; /// Create a new instance from a json static SyncResponse fromJson(Map json) => @@ -31,7 +32,8 @@ class SyncResponse extends _BaseResponse { @JsonSerializable(createToJson: false) class QueryChannelsResponse extends _BaseResponse { /// List of channels state returned by the query - List channels; + @JsonKey(defaultValue: []) + late List channels; /// Create a new instance from a json static QueryChannelsResponse fromJson(Map json) => @@ -41,8 +43,8 @@ class QueryChannelsResponse extends _BaseResponse { /// Model response for [StreamChatClient.queryChannels] api call @JsonSerializable(createToJson: false) class TranslateMessageResponse extends _BaseResponse { - /// List of channels state returned by the query - TranslatedMessage message; + /// Translated message + late TranslatedMessage message; /// Create a new instance from a json static TranslateMessageResponse fromJson(Map json) => @@ -53,7 +55,8 @@ class TranslateMessageResponse extends _BaseResponse { @JsonSerializable(createToJson: false) class QueryMembersResponse extends _BaseResponse { /// List of channels state returned by the query - List members; + @JsonKey(defaultValue: []) + late List members; /// Create a new instance from a json static QueryMembersResponse fromJson(Map json) => @@ -64,7 +67,8 @@ class QueryMembersResponse extends _BaseResponse { @JsonSerializable(createToJson: false) class QueryUsersResponse extends _BaseResponse { /// List of users returned by the query - List users; + @JsonKey(defaultValue: []) + late List users; /// Create a new instance from a json static QueryUsersResponse fromJson(Map json) => @@ -75,7 +79,8 @@ class QueryUsersResponse extends _BaseResponse { @JsonSerializable(createToJson: false) class QueryReactionsResponse extends _BaseResponse { /// List of reactions returned by the query - List reactions; + @JsonKey(defaultValue: []) + late List reactions; /// Create a new instance from a json static QueryReactionsResponse fromJson(Map json) => @@ -86,7 +91,8 @@ class QueryReactionsResponse extends _BaseResponse { @JsonSerializable(createToJson: false) class QueryRepliesResponse extends _BaseResponse { /// List of messages returned by the api call - List messages; + @JsonKey(defaultValue: []) + late List messages; /// Create a new instance from a json static QueryRepliesResponse fromJson(Map json) => @@ -97,7 +103,8 @@ class QueryRepliesResponse extends _BaseResponse { @JsonSerializable(createToJson: false) class ListDevicesResponse extends _BaseResponse { /// List of user devices - List devices; + @JsonKey(defaultValue: []) + late List devices; /// Create a new instance from a json static ListDevicesResponse fromJson(Map json) => @@ -108,7 +115,7 @@ class ListDevicesResponse extends _BaseResponse { @JsonSerializable(createToJson: false) class SendFileResponse extends _BaseResponse { /// The url of the uploaded file - String file; + late String file; /// Create a new instance from a json static SendFileResponse fromJson(Map json) => @@ -119,7 +126,7 @@ class SendFileResponse extends _BaseResponse { @JsonSerializable(createToJson: false) class SendImageResponse extends _BaseResponse { /// The url of the uploaded file - String file; + late String file; /// Create a new instance from a json static SendImageResponse fromJson(Map json) => @@ -130,10 +137,10 @@ class SendImageResponse extends _BaseResponse { @JsonSerializable(createToJson: false) class SendReactionResponse extends _BaseResponse { /// Message returned by the api call - Message message; + late Message message; /// The reaction created by the api call - Reaction reaction; + late Reaction reaction; /// Create a new instance from a json static SendReactionResponse fromJson(Map json) => @@ -144,10 +151,10 @@ class SendReactionResponse extends _BaseResponse { @JsonSerializable(createToJson: false) class ConnectGuestUserResponse extends _BaseResponse { /// Guest user access token - String accessToken; + late String accessToken; /// Guest user - User user; + late User user; /// Create a new instance from a json static ConnectGuestUserResponse fromJson(Map json) => @@ -158,7 +165,8 @@ class ConnectGuestUserResponse extends _BaseResponse { @JsonSerializable(createToJson: false) class UpdateUsersResponse extends _BaseResponse { /// Updated users - Map users; + @JsonKey(defaultValue: {}) + late Map users; /// Create a new instance from a json static UpdateUsersResponse fromJson(Map json) => @@ -169,7 +177,7 @@ class UpdateUsersResponse extends _BaseResponse { @JsonSerializable(createToJson: false) class UpdateMessageResponse extends _BaseResponse { /// Message returned by the api call - Message message; + late Message message; /// Create a new instance from a json static UpdateMessageResponse fromJson(Map json) => @@ -180,7 +188,7 @@ class UpdateMessageResponse extends _BaseResponse { @JsonSerializable(createToJson: false) class SendMessageResponse extends _BaseResponse { /// Message returned by the api call - Message message; + late Message message; /// Create a new instance from a json static SendMessageResponse fromJson(Map json) => @@ -191,17 +199,17 @@ class SendMessageResponse extends _BaseResponse { @JsonSerializable(createToJson: false) class GetMessageResponse extends _BaseResponse { /// Message returned by the api call - Message message; + late Message message; /// Channel of the message - ChannelModel channel; + ChannelModel? channel; /// Create a new instance from a json static GetMessageResponse fromJson(Map json) { final res = _$GetMessageResponseFromJson(json); - final jsonChannel = res.message?.extraData?.remove('channel'); + final jsonChannel = res.message.extraData.remove('channel'); if (jsonChannel != null) { - res.channel = ChannelModel.fromJson(jsonChannel); + res.channel = ChannelModel.fromJson(jsonChannel as Map); } return res; } @@ -211,7 +219,8 @@ class GetMessageResponse extends _BaseResponse { @JsonSerializable(createToJson: false) class SearchMessagesResponse extends _BaseResponse { /// List of messages returned by the api call - List results; + @JsonKey(defaultValue: []) + late List results; /// Create a new instance from a json static SearchMessagesResponse fromJson(Map json) => @@ -222,7 +231,8 @@ class SearchMessagesResponse extends _BaseResponse { @JsonSerializable(createToJson: false) class GetMessagesByIdResponse extends _BaseResponse { /// Message returned by the api call - List messages; + @JsonKey(defaultValue: []) + late List messages; /// Create a new instance from a json static GetMessagesByIdResponse fromJson(Map json) => @@ -233,13 +243,13 @@ class GetMessagesByIdResponse extends _BaseResponse { @JsonSerializable(createToJson: false) class UpdateChannelResponse extends _BaseResponse { /// Updated channel - ChannelModel channel; + late ChannelModel channel; /// Channel members - List members; + List? members; /// Message returned by the api call - Message message; + Message? message; /// Create a new instance from a json static UpdateChannelResponse fromJson(Map json) => @@ -250,10 +260,10 @@ class UpdateChannelResponse extends _BaseResponse { @JsonSerializable(createToJson: false) class PartialUpdateChannelResponse extends _BaseResponse { /// Updated channel - ChannelModel channel; + late ChannelModel channel; /// Channel members - List members; + List? members; /// Create a new instance from a json static PartialUpdateChannelResponse fromJson(Map json) => @@ -264,13 +274,14 @@ class PartialUpdateChannelResponse extends _BaseResponse { @JsonSerializable(createToJson: false) class InviteMembersResponse extends _BaseResponse { /// Updated channel - ChannelModel channel; + late ChannelModel channel; /// Channel members - List members; + @JsonKey(defaultValue: []) + late List members; /// Message returned by the api call - Message message; + Message? message; /// Create a new instance from a json static InviteMembersResponse fromJson(Map json) => @@ -281,13 +292,14 @@ class InviteMembersResponse extends _BaseResponse { @JsonSerializable(createToJson: false) class RemoveMembersResponse extends _BaseResponse { /// Updated channel - ChannelModel channel; + late ChannelModel channel; /// Channel members - List members; + @JsonKey(defaultValue: []) + late List members; /// Message returned by the api call - Message message; + Message? message; /// Create a new instance from a json static RemoveMembersResponse fromJson(Map json) => @@ -298,7 +310,7 @@ class RemoveMembersResponse extends _BaseResponse { @JsonSerializable(createToJson: false) class SendActionResponse extends _BaseResponse { /// Message returned by the api call - Message message; + Message? message; /// Create a new instance from a json static SendActionResponse fromJson(Map json) => @@ -309,13 +321,14 @@ class SendActionResponse extends _BaseResponse { @JsonSerializable(createToJson: false) class AddMembersResponse extends _BaseResponse { /// Updated channel - ChannelModel channel; + late ChannelModel channel; /// Channel members - List members; + @JsonKey(defaultValue: []) + late List members; /// Message returned by the api call - Message message; + Message? message; /// Create a new instance from a json static AddMembersResponse fromJson(Map json) => @@ -326,13 +339,14 @@ class AddMembersResponse extends _BaseResponse { @JsonSerializable(createToJson: false) class AcceptInviteResponse extends _BaseResponse { /// Updated channel - ChannelModel channel; + late ChannelModel channel; /// Channel members - List members; + @JsonKey(defaultValue: []) + late List members; /// Message returned by the api call - Message message; + Message? message; /// Create a new instance from a json static AcceptInviteResponse fromJson(Map json) => @@ -343,13 +357,14 @@ class AcceptInviteResponse extends _BaseResponse { @JsonSerializable(createToJson: false) class RejectInviteResponse extends _BaseResponse { /// Updated channel - ChannelModel channel; + late ChannelModel channel; /// Channel members - List members; + @JsonKey(defaultValue: []) + late List members; /// Message returned by the api call - Message message; + Message? message; /// Create a new instance from a json static RejectInviteResponse fromJson(Map json) => @@ -368,19 +383,23 @@ class EmptyResponse extends _BaseResponse { @JsonSerializable(createToJson: false) class ChannelStateResponse extends _BaseResponse { /// Updated channel - ChannelModel channel; + late ChannelModel channel; /// List of messages returned by the api call - List messages; + @JsonKey(defaultValue: []) + late List messages; /// Channel members - List members; + @JsonKey(defaultValue: []) + late List members; /// Number of users watching the channel - int watcherCount; + @JsonKey(defaultValue: 0) + late int watcherCount; /// List of read states - List read; + @JsonKey(defaultValue: []) + late List read; /// Create a new instance from a json static ChannelStateResponse fromJson(Map json) => diff --git a/packages/stream_chat/lib/src/api/responses.g.dart b/packages/stream_chat/lib/src/api/responses.g.dart index 1f290d6e..2358593d 100644 --- a/packages/stream_chat/lib/src/api/responses.g.dart +++ b/packages/stream_chat/lib/src/api/responses.g.dart @@ -6,394 +6,274 @@ part of 'responses.dart'; // JsonSerializableGenerator // ************************************************************************** -SyncResponse _$SyncResponseFromJson(Map json) { +SyncResponse _$SyncResponseFromJson(Map json) { return SyncResponse() - ..duration = json['duration'] as String - ..events = (json['events'] as List) - ?.map((e) => e == null - ? null - : Event.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList(); + ..duration = json['duration'] as String? + ..events = (json['events'] as List?) + ?.map((e) => Event.fromJson(e as Map)) + .toList() ?? + []; } -QueryChannelsResponse _$QueryChannelsResponseFromJson(Map json) { +QueryChannelsResponse _$QueryChannelsResponseFromJson( + Map json) { return QueryChannelsResponse() - ..duration = json['duration'] as String - ..channels = (json['channels'] as List) - ?.map((e) => e == null ? null : ChannelState.fromJson(e as Map)) - ?.toList(); + ..duration = json['duration'] as String? + ..channels = (json['channels'] as List?) + ?.map((e) => ChannelState.fromJson(e as Map)) + .toList() ?? + []; } -TranslateMessageResponse _$TranslateMessageResponseFromJson(Map json) { +TranslateMessageResponse _$TranslateMessageResponseFromJson( + Map json) { return TranslateMessageResponse() - ..duration = json['duration'] as String - ..message = json['message'] == null - ? null - : TranslatedMessage.fromJson((json['message'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )); + ..duration = json['duration'] as String? + ..message = + TranslatedMessage.fromJson(json['message'] as Map); } -QueryMembersResponse _$QueryMembersResponseFromJson(Map json) { +QueryMembersResponse _$QueryMembersResponseFromJson(Map json) { return QueryMembersResponse() - ..duration = json['duration'] as String - ..members = (json['members'] as List) - ?.map((e) => e == null - ? null - : Member.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList(); + ..duration = json['duration'] as String? + ..members = (json['members'] as List?) + ?.map((e) => Member.fromJson(e as Map)) + .toList() ?? + []; } -QueryUsersResponse _$QueryUsersResponseFromJson(Map json) { +QueryUsersResponse _$QueryUsersResponseFromJson(Map json) { return QueryUsersResponse() - ..duration = json['duration'] as String - ..users = (json['users'] as List) - ?.map((e) => e == null - ? null - : User.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList(); + ..duration = json['duration'] as String? + ..users = (json['users'] as List?) + ?.map((e) => User.fromJson(e as Map)) + .toList() ?? + []; } -QueryReactionsResponse _$QueryReactionsResponseFromJson(Map json) { +QueryReactionsResponse _$QueryReactionsResponseFromJson( + Map json) { return QueryReactionsResponse() - ..duration = json['duration'] as String - ..reactions = (json['reactions'] as List) - ?.map((e) => e == null - ? null - : Reaction.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList(); + ..duration = json['duration'] as String? + ..reactions = (json['reactions'] as List?) + ?.map((e) => Reaction.fromJson(e as Map)) + .toList() ?? + []; } -QueryRepliesResponse _$QueryRepliesResponseFromJson(Map json) { +QueryRepliesResponse _$QueryRepliesResponseFromJson(Map json) { return QueryRepliesResponse() - ..duration = json['duration'] as String - ..messages = (json['messages'] as List) - ?.map((e) => e == null - ? null - : Message.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList(); + ..duration = json['duration'] as String? + ..messages = (json['messages'] as List?) + ?.map((e) => Message.fromJson(e as Map)) + .toList() ?? + []; } -ListDevicesResponse _$ListDevicesResponseFromJson(Map json) { +ListDevicesResponse _$ListDevicesResponseFromJson(Map json) { return ListDevicesResponse() - ..duration = json['duration'] as String - ..devices = (json['devices'] as List) - ?.map((e) => e == null - ? null - : Device.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList(); + ..duration = json['duration'] as String? + ..devices = (json['devices'] as List?) + ?.map((e) => Device.fromJson(e as Map)) + .toList() ?? + []; } -SendFileResponse _$SendFileResponseFromJson(Map json) { +SendFileResponse _$SendFileResponseFromJson(Map json) { return SendFileResponse() - ..duration = json['duration'] as String + ..duration = json['duration'] as String? ..file = json['file'] as String; } -SendImageResponse _$SendImageResponseFromJson(Map json) { +SendImageResponse _$SendImageResponseFromJson(Map json) { return SendImageResponse() - ..duration = json['duration'] as String + ..duration = json['duration'] as String? ..file = json['file'] as String; } -SendReactionResponse _$SendReactionResponseFromJson(Map json) { +SendReactionResponse _$SendReactionResponseFromJson(Map json) { return SendReactionResponse() - ..duration = json['duration'] as String - ..message = json['message'] == null - ? null - : Message.fromJson((json['message'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )) - ..reaction = json['reaction'] == null - ? null - : Reaction.fromJson((json['reaction'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )); + ..duration = json['duration'] as String? + ..message = Message.fromJson(json['message'] as Map) + ..reaction = Reaction.fromJson(json['reaction'] as Map); } -ConnectGuestUserResponse _$ConnectGuestUserResponseFromJson(Map json) { +ConnectGuestUserResponse _$ConnectGuestUserResponseFromJson( + Map json) { return ConnectGuestUserResponse() - ..duration = json['duration'] as String + ..duration = json['duration'] as String? ..accessToken = json['access_token'] as String - ..user = json['user'] == null - ? null - : User.fromJson((json['user'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )); + ..user = User.fromJson(json['user'] as Map); } -UpdateUsersResponse _$UpdateUsersResponseFromJson(Map json) { +UpdateUsersResponse _$UpdateUsersResponseFromJson(Map json) { return UpdateUsersResponse() - ..duration = json['duration'] as String - ..users = (json['users'] as Map)?.map( - (k, e) => MapEntry( - k as String, - e == null - ? null - : User.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))), - ); + ..duration = json['duration'] as String? + ..users = (json['users'] as Map?)?.map( + (k, e) => MapEntry(k, User.fromJson(e as Map)), + ) ?? + {}; } -UpdateMessageResponse _$UpdateMessageResponseFromJson(Map json) { +UpdateMessageResponse _$UpdateMessageResponseFromJson( + Map json) { return UpdateMessageResponse() - ..duration = json['duration'] as String - ..message = json['message'] == null - ? null - : Message.fromJson((json['message'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )); + ..duration = json['duration'] as String? + ..message = Message.fromJson(json['message'] as Map); } -SendMessageResponse _$SendMessageResponseFromJson(Map json) { +SendMessageResponse _$SendMessageResponseFromJson(Map json) { return SendMessageResponse() - ..duration = json['duration'] as String - ..message = json['message'] == null - ? null - : Message.fromJson((json['message'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )); + ..duration = json['duration'] as String? + ..message = Message.fromJson(json['message'] as Map); } -GetMessageResponse _$GetMessageResponseFromJson(Map json) { +GetMessageResponse _$GetMessageResponseFromJson(Map json) { return GetMessageResponse() - ..duration = json['duration'] as String - ..message = json['message'] == null - ? null - : Message.fromJson((json['message'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )) + ..duration = json['duration'] as String? + ..message = Message.fromJson(json['message'] as Map) ..channel = json['channel'] == null ? null - : ChannelModel.fromJson((json['channel'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )); + : ChannelModel.fromJson(json['channel'] as Map); } -SearchMessagesResponse _$SearchMessagesResponseFromJson(Map json) { +SearchMessagesResponse _$SearchMessagesResponseFromJson( + Map json) { return SearchMessagesResponse() - ..duration = json['duration'] as String - ..results = (json['results'] as List) - ?.map((e) => e == null ? null : GetMessageResponse.fromJson(e as Map)) - ?.toList(); + ..duration = json['duration'] as String? + ..results = (json['results'] as List?) + ?.map((e) => GetMessageResponse.fromJson(e as Map)) + .toList() ?? + []; } -GetMessagesByIdResponse _$GetMessagesByIdResponseFromJson(Map json) { +GetMessagesByIdResponse _$GetMessagesByIdResponseFromJson( + Map json) { return GetMessagesByIdResponse() - ..duration = json['duration'] as String - ..messages = (json['messages'] as List) - ?.map((e) => e == null - ? null - : Message.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList(); + ..duration = json['duration'] as String? + ..messages = (json['messages'] as List?) + ?.map((e) => Message.fromJson(e as Map)) + .toList() ?? + []; } -UpdateChannelResponse _$UpdateChannelResponseFromJson(Map json) { +UpdateChannelResponse _$UpdateChannelResponseFromJson( + Map json) { return UpdateChannelResponse() - ..duration = json['duration'] as String - ..channel = json['channel'] == null - ? null - : ChannelModel.fromJson((json['channel'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )) - ..members = (json['members'] as List) - ?.map((e) => e == null - ? null - : Member.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList() + ..duration = json['duration'] as String? + ..channel = ChannelModel.fromJson(json['channel'] as Map) + ..members = (json['members'] as List?) + ?.map((e) => Member.fromJson(e as Map)) + .toList() ..message = json['message'] == null ? null - : Message.fromJson((json['message'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )); + : Message.fromJson(json['message'] as Map); } -PartialUpdateChannelResponse _$PartialUpdateChannelResponseFromJson(Map json) { +PartialUpdateChannelResponse _$PartialUpdateChannelResponseFromJson( + Map json) { return PartialUpdateChannelResponse() - ..duration = json['duration'] as String - ..channel = json['channel'] == null - ? null - : ChannelModel.fromJson((json['channel'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )) - ..members = (json['members'] as List) - ?.map((e) => e == null - ? null - : Member.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList(); + ..duration = json['duration'] as String? + ..channel = ChannelModel.fromJson(json['channel'] as Map) + ..members = (json['members'] as List?) + ?.map((e) => Member.fromJson(e as Map)) + .toList(); } -InviteMembersResponse _$InviteMembersResponseFromJson(Map json) { +InviteMembersResponse _$InviteMembersResponseFromJson( + Map json) { return InviteMembersResponse() - ..duration = json['duration'] as String - ..channel = json['channel'] == null - ? null - : ChannelModel.fromJson((json['channel'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )) - ..members = (json['members'] as List) - ?.map((e) => e == null - ? null - : Member.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList() + ..duration = json['duration'] as String? + ..channel = ChannelModel.fromJson(json['channel'] as Map) + ..members = (json['members'] as List?) + ?.map((e) => Member.fromJson(e as Map)) + .toList() ?? + [] ..message = json['message'] == null ? null - : Message.fromJson((json['message'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )); + : Message.fromJson(json['message'] as Map); } -RemoveMembersResponse _$RemoveMembersResponseFromJson(Map json) { +RemoveMembersResponse _$RemoveMembersResponseFromJson( + Map json) { return RemoveMembersResponse() - ..duration = json['duration'] as String - ..channel = json['channel'] == null - ? null - : ChannelModel.fromJson((json['channel'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )) - ..members = (json['members'] as List) - ?.map((e) => e == null - ? null - : Member.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList() + ..duration = json['duration'] as String? + ..channel = ChannelModel.fromJson(json['channel'] as Map) + ..members = (json['members'] as List?) + ?.map((e) => Member.fromJson(e as Map)) + .toList() ?? + [] ..message = json['message'] == null ? null - : Message.fromJson((json['message'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )); + : Message.fromJson(json['message'] as Map); } -SendActionResponse _$SendActionResponseFromJson(Map json) { +SendActionResponse _$SendActionResponseFromJson(Map json) { return SendActionResponse() - ..duration = json['duration'] as String + ..duration = json['duration'] as String? ..message = json['message'] == null ? null - : Message.fromJson((json['message'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )); + : Message.fromJson(json['message'] as Map); } -AddMembersResponse _$AddMembersResponseFromJson(Map json) { +AddMembersResponse _$AddMembersResponseFromJson(Map json) { return AddMembersResponse() - ..duration = json['duration'] as String - ..channel = json['channel'] == null - ? null - : ChannelModel.fromJson((json['channel'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )) - ..members = (json['members'] as List) - ?.map((e) => e == null - ? null - : Member.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList() + ..duration = json['duration'] as String? + ..channel = ChannelModel.fromJson(json['channel'] as Map) + ..members = (json['members'] as List?) + ?.map((e) => Member.fromJson(e as Map)) + .toList() ?? + [] ..message = json['message'] == null ? null - : Message.fromJson((json['message'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )); + : Message.fromJson(json['message'] as Map); } -AcceptInviteResponse _$AcceptInviteResponseFromJson(Map json) { +AcceptInviteResponse _$AcceptInviteResponseFromJson(Map json) { return AcceptInviteResponse() - ..duration = json['duration'] as String - ..channel = json['channel'] == null - ? null - : ChannelModel.fromJson((json['channel'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )) - ..members = (json['members'] as List) - ?.map((e) => e == null - ? null - : Member.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList() + ..duration = json['duration'] as String? + ..channel = ChannelModel.fromJson(json['channel'] as Map) + ..members = (json['members'] as List?) + ?.map((e) => Member.fromJson(e as Map)) + .toList() ?? + [] ..message = json['message'] == null ? null - : Message.fromJson((json['message'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )); + : Message.fromJson(json['message'] as Map); } -RejectInviteResponse _$RejectInviteResponseFromJson(Map json) { +RejectInviteResponse _$RejectInviteResponseFromJson(Map json) { return RejectInviteResponse() - ..duration = json['duration'] as String - ..channel = json['channel'] == null - ? null - : ChannelModel.fromJson((json['channel'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )) - ..members = (json['members'] as List) - ?.map((e) => e == null - ? null - : Member.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList() + ..duration = json['duration'] as String? + ..channel = ChannelModel.fromJson(json['channel'] as Map) + ..members = (json['members'] as List?) + ?.map((e) => Member.fromJson(e as Map)) + .toList() ?? + [] ..message = json['message'] == null ? null - : Message.fromJson((json['message'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )); + : Message.fromJson(json['message'] as Map); } -EmptyResponse _$EmptyResponseFromJson(Map json) { - return EmptyResponse()..duration = json['duration'] as String; +EmptyResponse _$EmptyResponseFromJson(Map json) { + return EmptyResponse()..duration = json['duration'] as String?; } -ChannelStateResponse _$ChannelStateResponseFromJson(Map json) { +ChannelStateResponse _$ChannelStateResponseFromJson(Map json) { return ChannelStateResponse() - ..duration = json['duration'] as String - ..channel = json['channel'] == null - ? null - : ChannelModel.fromJson((json['channel'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )) - ..messages = (json['messages'] as List) - ?.map((e) => e == null - ? null - : Message.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList() - ..members = (json['members'] as List) - ?.map((e) => e == null - ? null - : Member.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList() - ..watcherCount = json['watcher_count'] as int - ..read = (json['read'] as List) - ?.map((e) => e == null - ? null - : Read.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList(); + ..duration = json['duration'] as String? + ..channel = ChannelModel.fromJson(json['channel'] as Map) + ..messages = (json['messages'] as List?) + ?.map((e) => Message.fromJson(e as Map)) + .toList() ?? + [] + ..members = (json['members'] as List?) + ?.map((e) => Member.fromJson(e as Map)) + .toList() ?? + [] + ..watcherCount = json['watcher_count'] as int? ?? 0 + ..read = (json['read'] as List?) + ?.map((e) => Read.fromJson(e as Map)) + .toList() ?? + []; } diff --git a/packages/stream_chat/lib/src/api/retry_policy.dart b/packages/stream_chat/lib/src/api/retry_policy.dart index a4d1e7bd..f0f2c67b 100644 --- a/packages/stream_chat/lib/src/api/retry_policy.dart +++ b/packages/stream_chat/lib/src/api/retry_policy.dart @@ -1,4 +1,3 @@ -import 'package:meta/meta.dart'; import 'package:stream_chat/src/client.dart'; import 'package:stream_chat/src/exceptions.dart'; @@ -6,30 +5,30 @@ import 'package:stream_chat/src/exceptions.dart'; class RetryPolicy { /// Instantiate a new RetryPolicy RetryPolicy({ - @required this.shouldRetry, - @required this.retryTimeout, - this.attempt, + required this.shouldRetry, + required this.retryTimeout, + this.attempt = 0, }); /// The number of attempts tried so far int attempt = 0; /// This function evaluates if we should retry the failure - final bool Function(StreamChatClient client, int attempt, ApiError apiError) + final bool Function(StreamChatClient client, int attempt, ApiError? apiError) shouldRetry; /// In the case that we want to retry a failed request the retryTimeout /// method is called to determine the timeout final Duration Function( - StreamChatClient client, int attempt, ApiError apiError) retryTimeout; + StreamChatClient client, int attempt, ApiError? apiError) retryTimeout; /// Creates a copy of [RetryPolicy] with specified attributes overridden. RetryPolicy copyWith({ - bool Function(StreamChatClient client, int attempt, ApiError apiError) + bool Function(StreamChatClient client, int attempt, ApiError? apiError)? shouldRetry, - Duration Function(StreamChatClient client, int attempt, ApiError apiError) + Duration Function(StreamChatClient client, int attempt, ApiError? apiError)? retryTimeout, - int attempt, + int? attempt, }) => RetryPolicy( retryTimeout: retryTimeout ?? this.retryTimeout, diff --git a/packages/stream_chat/lib/src/api/retry_queue.dart b/packages/stream_chat/lib/src/api/retry_queue.dart index ac0e14d7..c13db9c6 100644 --- a/packages/stream_chat/lib/src/api/retry_queue.dart +++ b/packages/stream_chat/lib/src/api/retry_queue.dart @@ -2,7 +2,6 @@ import 'dart:async'; import 'package:collection/collection.dart'; import 'package:logging/logging.dart'; -import 'package:meta/meta.dart'; import 'package:stream_chat/src/api/channel.dart'; import 'package:stream_chat/src/api/retry_policy.dart'; import 'package:stream_chat/src/event_type.dart'; @@ -14,7 +13,7 @@ import 'package:stream_chat/stream_chat.dart'; class RetryQueue { /// Instantiate a new RetryQueue object RetryQueue({ - @required this.channel, + required this.channel, this.logger, }) { _retryPolicy = channel.client.retryPolicy; @@ -28,14 +27,14 @@ class RetryQueue { final Channel channel; /// The logger associated to this queue - final Logger logger; + final Logger? logger; final _subscriptions = []; void _listenConnectionRecovered() { _subscriptions .add(channel.client.on(EventType.connectionRecovered).listen((event) { - if (!_isRetrying && event.online) { + if (!_isRetrying && event.online!) { _startRetrying(); } })); @@ -43,12 +42,13 @@ class RetryQueue { final HeapPriorityQueue _messageQueue = HeapPriorityQueue(_byDate); bool _isRetrying = false; - RetryPolicy _retryPolicy; + RetryPolicy? _retryPolicy; /// Add a list of messages void add(List messages) { logger?.info('added ${messages.length} messages'); final messageList = _messageQueue.toList(); + _messageQueue.addAll(messages .where((element) => !messageList.any((m) => m.id == element.id))); @@ -60,7 +60,7 @@ class RetryQueue { Future _startRetrying() async { logger?.info('start retrying'); _isRetrying = true; - final retryPolicy = _retryPolicy.copyWith(attempt: 0); + final retryPolicy = _retryPolicy!.copyWith(attempt: 0); while (_messageQueue.isNotEmpty) { final message = _messageQueue.first; @@ -72,9 +72,9 @@ class RetryQueue { logger?.info('now ${_messageQueue.length} messages in the queue'); retryPolicy.attempt = 0; } catch (error) { - ApiError apiError; + ApiError? apiError; if (error is DioError) { - if (error.type == DioErrorType.RESPONSE) { + if (error.type == DioErrorType.response) { _messageQueue.remove(message); return; } @@ -84,7 +84,7 @@ class RetryQueue { ); } else if (error is ApiError) { apiError = error; - if (apiError.status?.toString()?.startsWith('4') == true) { + if (apiError.status?.toString().startsWith('4') == true) { _messageQueue.remove(message); return; } @@ -101,6 +101,7 @@ class RetryQueue { } retryPolicy.attempt++; + final timeout = retryPolicy.retryTimeout( channel.client, retryPolicy.attempt, @@ -112,13 +113,13 @@ class RetryQueue { _isRetrying = false; } - void _sendFailedEvent(Message message) { - final newStatus = message.status == MessageSendingStatus.sending + void _sendFailedEvent(Message? message) { + final newStatus = message!.status == MessageSendingStatus.sending ? MessageSendingStatus.failed : (message.status == MessageSendingStatus.updating ? MessageSendingStatus.failed_update : MessageSendingStatus.failed_delete); - channel.state.addMessage(message.copyWith( + channel.state!.addMessage(message.copyWith( status: newStatus, )); } @@ -141,20 +142,24 @@ class RetryQueue { final messageList = _messageQueue.toList(); if (event.message != null) { final messageIndex = - messageList.indexWhere((m) => m.id == event.message.id); + messageList.indexWhere((m) => m.id == event.message!.id); if (messageIndex == -1 && [ MessageSendingStatus.failed_update, MessageSendingStatus.failed, MessageSendingStatus.failed_delete, - ].contains(event.message.status)) { + ].contains(event.message!.status)) { logger?.info('add message from events'); - add([event.message]); + final m = event.message; + + if (m != null) { + add([m]); + } } else if (messageIndex != -1 && [ MessageSendingStatus.sent, null, - ].contains(event.message.status)) { + ].contains(event.message!.status)) { _messageQueue.remove(messageList[messageIndex]); } } @@ -171,10 +176,14 @@ class RetryQueue { final date1 = _getMessageDate(m1); final date2 = _getMessageDate(m2); + if (date1 == null || date2 == null) { + return 0; + } + return date1.compareTo(date2); } - static DateTime _getMessageDate(Message m1) { + static DateTime? _getMessageDate(Message m1) { switch (m1.status) { case MessageSendingStatus.failed_delete: case MessageSendingStatus.deleting: diff --git a/packages/stream_chat/lib/src/api/web_socket_channel_html.dart b/packages/stream_chat/lib/src/api/web_socket_channel_html.dart index 9ddd83e5..ab821ca9 100644 --- a/packages/stream_chat/lib/src/api/web_socket_channel_html.dart +++ b/packages/stream_chat/lib/src/api/web_socket_channel_html.dart @@ -3,5 +3,5 @@ import 'package:web_socket_channel/web_socket_channel.dart'; /// Html version of websocket implementation /// Used in Flutter web version -WebSocketChannel connectWebSocket(String url, {Iterable protocols}) => +WebSocketChannel connectWebSocket(String url, {Iterable? protocols}) => HtmlWebSocketChannel.connect(url, protocols: protocols); diff --git a/packages/stream_chat/lib/src/api/web_socket_channel_io.dart b/packages/stream_chat/lib/src/api/web_socket_channel_io.dart index ed37ba7f..8402ca0c 100644 --- a/packages/stream_chat/lib/src/api/web_socket_channel_io.dart +++ b/packages/stream_chat/lib/src/api/web_socket_channel_io.dart @@ -3,5 +3,5 @@ import 'package:web_socket_channel/web_socket_channel.dart'; /// IO version of websocket implementation /// Used in Flutter mobile version -WebSocketChannel connectWebSocket(String url, {Iterable protocols}) => +WebSocketChannel connectWebSocket(String url, {Iterable? protocols}) => IOWebSocketChannel.connect(url, protocols: protocols); diff --git a/packages/stream_chat/lib/src/api/web_socket_channel_stub.dart b/packages/stream_chat/lib/src/api/web_socket_channel_stub.dart index 7e2e47bd..e2efaee6 100644 --- a/packages/stream_chat/lib/src/api/web_socket_channel_stub.dart +++ b/packages/stream_chat/lib/src/api/web_socket_channel_stub.dart @@ -3,7 +3,7 @@ import 'package:web_socket_channel/web_socket_channel.dart'; /// Stub version of websocket implementation /// Used just for conditional library import WebSocketChannel connectWebSocket(String url, - {Iterable protocols, - Map headers, - Duration pingInterval}) => + {Iterable? protocols, + Map? headers, + Duration? pingInterval}) => throw UnimplementedError(); diff --git a/packages/stream_chat/lib/src/api/websocket.dart b/packages/stream_chat/lib/src/api/websocket.dart index 0f78b1bf..b5bbbb69 100644 --- a/packages/stream_chat/lib/src/api/websocket.dart +++ b/packages/stream_chat/lib/src/api/websocket.dart @@ -16,8 +16,8 @@ typedef EventHandler = void Function(Event); /// Typedef used for connecting to a websocket. Method returns a /// [WebSocketChannel] and accepts a connection [url] and an optional /// [Iterable] of `protocols`. -typedef ConnectWebSocket = WebSocketChannel Function(String url, - {Iterable protocols}); +typedef ConnectWebSocket = WebSocketChannel Function(String? url, + {Iterable? protocols}); // TODO: parse error even // TODO: if parsing an error into an event fails we should not hide the @@ -27,11 +27,11 @@ class WebSocket { /// Creates a new websocket /// To connect the WS call [connect] WebSocket({ - @required this.baseUrl, - this.user, - this.connectParams, - this.connectPayload, - this.handler, + required this.baseUrl, + required this.user, + required this.handler, + this.connectParams = const {}, + this.connectPayload = const {}, this.logger, this.connectFunc, this.reconnectionMonitorInterval = 1, @@ -78,12 +78,12 @@ class WebSocket { final EventHandler handler; /// A WS specific logger instance - final Logger logger; + final Logger? logger; /// Connection function /// Used only for testing purpose @visibleForTesting - final ConnectWebSocket connectFunc; + final ConnectWebSocket? connectFunc; /// Interval of the reconnection monitor timer /// This checks that it received a new event in the last @@ -107,43 +107,43 @@ class WebSocket { _connectionStatusController.add(status); /// The current connection status value - ConnectionStatus get connectionStatus => _connectionStatusController.value; + ConnectionStatus? get connectionStatus => _connectionStatusController.value; /// This notifies of connection status changes Stream get connectionStatusStream => _connectionStatusController.stream; - String _path; + late String _path; int _retryAttempt = 1; - WebSocketChannel _channel; - Timer _healthCheck, _reconnectionMonitor; - DateTime _lastEventAt; + late WebSocketChannel _channel; + Timer? _healthCheck, _reconnectionMonitor; + DateTime? _lastEventAt; bool _manuallyDisconnected = false; bool _connecting = false; bool _reconnecting = false; Event _decodeEvent(String source) => Event.fromJson(json.decode(source)); - Completer _connectionCompleter = Completer(); + Completer _connectionCompleter = Completer(); /// Connect the WS using the parameters passed in the constructor - Future connect() { + Future connect() async { _manuallyDisconnected = false; if (_connecting) { - logger.severe('already connecting'); + logger?.severe('already connecting'); return null; } _connecting = true; _connectionStatus = ConnectionStatus.connecting; - logger.info('connecting to $_path'); + logger?.info('connecting to $_path'); _channel = connectFunc?.call(_path) ?? WebSocketChannel.connect(Uri.parse(_path)); _channel.stream.listen( - (data) { + (data) async { final jsonData = json.decode(data); if (jsonData['error'] != null) { return _onConnectionError(jsonData['error']); @@ -153,9 +153,7 @@ class WebSocket { onError: (error, stacktrace) { _onConnectionError(error, stacktrace); }, - onDone: () { - _onDone(); - }, + onDone: _onDone, ); return _connectionCompleter.future; } @@ -166,7 +164,7 @@ class WebSocket { return; } - logger.info('connection closed | closeCode: ${_channel.closeCode} | ' + logger?.info('connection closed | closeCode: ${_channel.closeCode} | ' 'closedReason: ${_channel.closeReason}'); if (!_reconnecting) { @@ -180,10 +178,10 @@ class WebSocket { } final event = _decodeEvent(data); - logger.info('received new event: $data'); + logger?.info('received new event: $data'); if (_lastEventAt == null) { - logger.info('connection estabilished'); + logger?.info('connection estabilished'); _connecting = false; _reconnecting = false; _lastEventAt = DateTime.now(); @@ -204,9 +202,9 @@ class WebSocket { } Future _onConnectionError(error, [stacktrace]) async { - logger..severe('error connecting')..severe(error); + logger?..severe('error connecting')..severe(error); if (stacktrace != null) { - logger.severe(stacktrace); + logger?.severe(stacktrace); } _connecting = false; @@ -225,7 +223,7 @@ class WebSocket { void _reconnectionTimer(_) { final now = DateTime.now(); if (_lastEventAt != null && - now.difference(_lastEventAt).inSeconds > reconnectionMonitorTimeout) { + now.difference(_lastEventAt!).inSeconds > reconnectionMonitorTimeout) { _channel.sink.close(); } } @@ -244,18 +242,18 @@ class WebSocket { return; } if (_connecting) { - logger.info('already connecting'); + logger?.info('already connecting'); return; } - logger.info('reconnecting..'); + logger?.info('reconnecting..'); _cancelTimers(); try { await connect(); } catch (e) { - logger.log(Level.SEVERE, e.toString()); + logger?.log(Level.SEVERE, e.toString()); } await Future.delayed( Duration(seconds: min(_retryAttempt * 5, 25)), @@ -267,7 +265,7 @@ class WebSocket { } Future _reconnect() async { - logger.info('reconnect'); + logger?.info('reconnect'); if (!_reconnecting) { _reconnecting = true; _connectionStatus = ConnectionStatus.connecting; @@ -279,20 +277,20 @@ class WebSocket { void _cancelTimers() { _lastEventAt = null; if (_healthCheck != null) { - _healthCheck.cancel(); + _healthCheck!.cancel(); } if (_reconnectionMonitor != null) { - _reconnectionMonitor.cancel(); + _reconnectionMonitor!.cancel(); } } void _healthCheckTimer(_) { - logger.info('sending health.check'); + logger?.info('sending health.check'); _channel.sink.add("{'type': 'health.check'}"); } void _startHealthCheck() { - logger.info('start health check monitor'); + logger?.info('start health check monitor'); _healthCheck = Timer.periodic( Duration(seconds: healthCheckInterval), @@ -311,13 +309,13 @@ class WebSocket { if (_manuallyDisconnected) { return; } - logger.info('disconnecting'); + logger?.info('disconnecting'); _connectionCompleter = Completer(); _cancelTimers(); _reconnecting = false; _manuallyDisconnected = true; _connectionStatus = ConnectionStatus.disconnected; await _connectionStatusController.close(); - return _channel.sink.close(); + await _channel.sink.close(); } } diff --git a/packages/stream_chat/lib/src/attachment_file_uploader.dart b/packages/stream_chat/lib/src/attachment_file_uploader.dart index 7d9e8c40..d00dd4e7 100644 --- a/packages/stream_chat/lib/src/attachment_file_uploader.dart +++ b/packages/stream_chat/lib/src/attachment_file_uploader.dart @@ -1,8 +1,8 @@ import 'package:dio/dio.dart'; import 'package:stream_chat/src/api/responses.dart'; import 'package:stream_chat/src/client.dart'; -import 'package:stream_chat/src/models/attachment_file.dart'; import 'package:stream_chat/src/extensions/string_extension.dart'; +import 'package:stream_chat/src/models/attachment_file.dart'; /// Class responsible for uploading images and files to a given channel abstract class AttachmentFileUploader { @@ -15,8 +15,8 @@ abstract class AttachmentFileUploader { AttachmentFile image, String channelId, String channelType, { - ProgressCallback onSendProgress, - CancelToken cancelToken, + ProgressCallback? onSendProgress, + CancelToken? cancelToken, }); /// Uploads a [file] to the given channel. @@ -28,8 +28,8 @@ abstract class AttachmentFileUploader { AttachmentFile file, String channelId, String channelType, { - ProgressCallback onSendProgress, - CancelToken cancelToken, + ProgressCallback? onSendProgress, + CancelToken? cancelToken, }); /// Deletes a image using its [url] from the given channel. @@ -40,7 +40,7 @@ abstract class AttachmentFileUploader { String url, String channelId, String channelType, { - CancelToken cancelToken, + CancelToken? cancelToken, }); /// Deletes a file using its [url] from the given channel. @@ -51,7 +51,7 @@ abstract class AttachmentFileUploader { String url, String channelId, String channelType, { - CancelToken cancelToken, + CancelToken? cancelToken, }); } @@ -67,22 +67,22 @@ class StreamAttachmentFileUploader implements AttachmentFileUploader { AttachmentFile file, String channelId, String channelType, { - ProgressCallback onSendProgress, - CancelToken cancelToken, + ProgressCallback? onSendProgress, + CancelToken? cancelToken, }) async { - final filename = file.path?.split('/')?.last ?? file.name; - final mimeType = filename.mimeType; + final filename = file.path?.split('/').last ?? file.name; + final mimeType = filename?.mimeType; - MultipartFile multiPartFile; + MultipartFile? multiPartFile; if (file.path != null) { multiPartFile = await MultipartFile.fromFile( - file.path, + file.path!, filename: filename, contentType: mimeType, ); } else if (file.bytes != null) { multiPartFile = MultipartFile.fromBytes( - file.bytes, + file.bytes!, filename: filename, contentType: mimeType, ); @@ -104,22 +104,22 @@ class StreamAttachmentFileUploader implements AttachmentFileUploader { AttachmentFile file, String channelId, String channelType, { - ProgressCallback onSendProgress, - CancelToken cancelToken, + ProgressCallback? onSendProgress, + CancelToken? cancelToken, }) async { - final filename = file.path?.split('/')?.last ?? file.name; - final mimeType = filename.mimeType; + final filename = file.path?.split('/').last ?? file.name; + final mimeType = filename?.mimeType; - MultipartFile multiPartFile; + MultipartFile? multiPartFile; if (file.path != null) { multiPartFile = await MultipartFile.fromFile( - file.path, + file.path!, filename: filename, contentType: mimeType, ); } else if (file.bytes != null) { multiPartFile = MultipartFile.fromBytes( - file.bytes, + file.bytes!, filename: filename, contentType: mimeType, ); @@ -141,7 +141,7 @@ class StreamAttachmentFileUploader implements AttachmentFileUploader { String url, String channelId, String channelType, { - CancelToken cancelToken, + CancelToken? cancelToken, }) async { final response = await _client.delete( '/channels/$channelType/$channelId/image', @@ -156,7 +156,7 @@ class StreamAttachmentFileUploader implements AttachmentFileUploader { String url, String channelId, String channelType, { - CancelToken cancelToken, + CancelToken? cancelToken, }) async { final response = await _client.delete( '/channels/$channelType/$channelId/file', diff --git a/packages/stream_chat/lib/src/client.dart b/packages/stream_chat/lib/src/client.dart index 0dc79d65..a2bc6f77 100644 --- a/packages/stream_chat/lib/src/client.dart +++ b/packages/stream_chat/lib/src/client.dart @@ -22,6 +22,7 @@ import 'package:stream_chat/src/models/attachment_file.dart'; import 'package:stream_chat/src/models/channel_model.dart'; import 'package:stream_chat/src/models/channel_state.dart'; import 'package:stream_chat/src/models/event.dart'; +import 'package:stream_chat/src/models/filter.dart'; import 'package:stream_chat/src/models/message.dart'; import 'package:stream_chat/src/models/own_user.dart'; import 'package:stream_chat/src/models/user.dart'; @@ -38,7 +39,7 @@ typedef DecoderFunction = T Function(Map); /// A function which can be used to request a Stream Chat API token from your /// own backend server. Function requires a single [userId]. -typedef TokenProvider = Future Function(String userId); +typedef TokenProvider = Future Function(String? userId); /// Provider used to send push notifications. enum PushProvider { @@ -82,58 +83,59 @@ class StreamChatClient { this.tokenProvider, this.baseURL = _defaultBaseURL, this.logLevel = Level.WARNING, - this.logHandlerFunction, + LogHandlerFunction? logHandlerFunction, Duration connectTimeout = const Duration(seconds: 6), Duration receiveTimeout = const Duration(seconds: 6), - Dio httpClient, - RetryPolicy retryPolicy, + Dio? httpClient, + RetryPolicy? retryPolicy, this.attachmentFileUploader, }) { _retryPolicy = retryPolicy ?? RetryPolicy( retryTimeout: - (StreamChatClient client, int attempt, ApiError error) => + (StreamChatClient client, int attempt, ApiError? error) => Duration(seconds: 1 * attempt), - shouldRetry: (StreamChatClient client, int attempt, ApiError error) => - attempt < 5, + shouldRetry: + (StreamChatClient client, int attempt, ApiError? error) => + attempt < 5, ); attachmentFileUploader ??= StreamAttachmentFileUploader(this); state = ClientState(this); - _setupLogger(); + _setupLogger(logHandlerFunction); _setupDio(httpClient, receiveTimeout, connectTimeout); logger.info('instantiating new client'); } - set chatPersistenceClient(ChatPersistenceClient value) { + set chatPersistenceClient(ChatPersistenceClient? value) { _originalChatPersistenceClient = value; } - ChatPersistenceClient _originalChatPersistenceClient; + ChatPersistenceClient? _originalChatPersistenceClient; /// Chat persistence client - ChatPersistenceClient get chatPersistenceClient => _chatPersistenceClient; + ChatPersistenceClient? get chatPersistenceClient => _chatPersistenceClient; - ChatPersistenceClient _chatPersistenceClient; + ChatPersistenceClient? _chatPersistenceClient; /// Attachment uploader - AttachmentFileUploader attachmentFileUploader; + AttachmentFileUploader? attachmentFileUploader; /// Whether the chat persistence is available or not bool get persistenceEnabled => _chatPersistenceClient != null; - RetryPolicy _retryPolicy; + RetryPolicy? _retryPolicy; bool _synced = false; /// The retry policy options getter - RetryPolicy get retryPolicy => _retryPolicy; + RetryPolicy? get retryPolicy => _retryPolicy; /// This client state - ClientState state; + late ClientState state; /// By default the Chat client will write all messages with level Warn or /// Error to stdout. @@ -169,7 +171,7 @@ class StreamChatClient { /// final client = StreamChatClient("stream-chat-api-key", /// logHandlerFunction: myLogHandlerFunction); ///``` - LogHandlerFunction logHandlerFunction; + late LogHandlerFunction logHandlerFunction; /// Your project Stream Chat api key. /// Find your API keys here https://getstream.io/dashboard/ @@ -184,7 +186,7 @@ class StreamChatClient { /// The token will be the return value of the function. /// It's used by the client to refresh the token once expired or to connect /// the user without a predefined token using [connectUserWithProvider]. - final TokenProvider tokenProvider; + final TokenProvider? tokenProvider; /// [Dio] httpClient /// It's be chosen because it's easy to use and supports interesting features @@ -195,8 +197,8 @@ class StreamChatClient { static const _defaultBaseURL = 'chat-us-east-1.stream-io-api.com'; static const _tokenExpiredErrorCode = 40; - StreamSubscription _connectionStatusSubscription; - Future Function(ConnectionStatus) _connectionStatusHandler; + StreamSubscription? _connectionStatusSubscription; + Future Function(ConnectionStatus)? _connectionStatusHandler; final BehaviorSubject _controller = BehaviorSubject(); @@ -211,7 +213,7 @@ class StreamChatClient { _wsConnectionStatusController.add(status); /// The current status value of the websocket connection - ConnectionStatus get wsConnectionStatus => + ConnectionStatus? get wsConnectionStatus => _wsConnectionStatusController.value; /// This notifies the connection status of the websocket connection. @@ -220,19 +222,19 @@ class StreamChatClient { _wsConnectionStatusController.stream; /// The current user token - String token; + String? token; /// The id of the current websocket connection - String get connectionId => _connectionId; + String? get connectionId => _connectionId; bool _anonymous = false; - String _connectionId; - WebSocket _ws; + String? _connectionId; + late WebSocket _ws; bool get _hasConnectionId => _connectionId != null; void _setupDio( - Dio httpClient, + Dio? httpClient, Duration receiveTimeout, Duration connectTimeout, ) { @@ -252,7 +254,7 @@ class StreamChatClient { this.httpClient.options.connectTimeout = connectTimeout.inMilliseconds; this.httpClient.interceptors.add( InterceptorsWrapper( - onRequest: (options) async { + onRequest: (options, handler) async { options.queryParameters.addAll(_commonQueryParams); options.headers.addAll(_httpHeaders); @@ -267,9 +269,8 @@ class StreamChatClient { var stringData = options.data.toString(); if (options.data is FormData) { - final multiPart = (options.data as FormData).files[0]?.value; - stringData = - '${multiPart?.filename} - ${multiPart?.contentType}'; + final multiPart = (options.data as FormData).files[0].value; + stringData = '${multiPart.filename} - ${multiPart.contentType}'; } logger.info(''' @@ -280,15 +281,17 @@ class StreamChatClient { data: $stringData '''); - - return options; + handler.next(options); }, onError: _tokenExpiredInterceptor, ), ); } - Future _tokenExpiredInterceptor(DioError err) async { + Future _tokenExpiredInterceptor( + DioError err, + ErrorInterceptorHandler handler, + ) async { final apiError = ApiError( err.response?.data, err.response?.statusCode, @@ -299,11 +302,11 @@ class StreamChatClient { if (tokenProvider != null) { httpClient.lock(); - final userId = state.user.id; + final userId = state.user!.id; await _disconnect(); - final newToken = await tokenProvider(userId); + final newToken = await tokenProvider!(userId); await Future.delayed(const Duration(seconds: 4)); token = newToken; @@ -312,22 +315,38 @@ class StreamChatClient { await connectUser(User(id: userId), newToken); try { - return await httpClient.request( - err.request.path, - cancelToken: err.request.cancelToken, - data: err.request.data, - onReceiveProgress: err.request.onReceiveProgress, - onSendProgress: err.request.onSendProgress, - queryParameters: err.request.queryParameters, - options: err.request, + handler.resolve( + await httpClient.request( + err.requestOptions.path, + cancelToken: err.requestOptions.cancelToken, + data: err.requestOptions.data, + onReceiveProgress: err.requestOptions.onReceiveProgress, + onSendProgress: err.requestOptions.onSendProgress, + queryParameters: err.requestOptions.queryParameters, + options: Options( + method: err.requestOptions.method, + sendTimeout: err.requestOptions.sendTimeout, + receiveTimeout: err.requestOptions.receiveTimeout, + extra: err.requestOptions.extra, + headers: err.requestOptions.headers, + responseType: err.requestOptions.responseType, + contentType: err.requestOptions.contentType, + validateStatus: err.requestOptions.validateStatus, + receiveDataWhenStatusError: + err.requestOptions.receiveDataWhenStatusError, + followRedirects: err.requestOptions.followRedirects, + maxRedirects: err.requestOptions.maxRedirects, + requestEncoder: err.requestOptions.requestEncoder, + responseDecoder: err.requestOptions.responseDecoder, + listFormat: err.requestOptions.listFormat, + ), + ), ); - } catch (err) { - return err; + } on DioError { + handler.reject(err); } } } - - return err; } LogHandlerFunction _getDefaultLogHandler() { @@ -353,14 +372,14 @@ class StreamChatClient { ) => Logger.detached(name) ..level = logLevel - ..onRecord.listen(logHandlerFunction ?? _getDefaultLogHandler()); + ..onRecord.listen(logHandlerFunction); - void _setupLogger() { + void _setupLogger(LogHandlerFunction? logHandlerFunction) { logger.level = logLevel; - logHandlerFunction ??= _getDefaultLogHandler(); + this.logHandlerFunction = logHandlerFunction ?? _getDefaultLogHandler(); - logger.onRecord.listen(logHandlerFunction); + logger.onRecord.listen(this.logHandlerFunction); logger.info('logger setup'); } @@ -375,7 +394,7 @@ class StreamChatClient { await _wsConnectionStatusController.close(); } - Map get _httpHeaders => { + Map get _httpHeaders => { 'Authorization': token, 'stream-auth-type': _authType, 'X-Stream-Client': _userAgent, @@ -385,12 +404,12 @@ class StreamChatClient { /// Set the current user, this triggers a connection to the API. /// It returns a [Future] that resolves when the connection is setup. @Deprecated('Use `connectUser` instead. Will be removed in Future releases') - Future setUser(User user, String token) => connectUser(user, token); + Future setUser(User user, String token) => connectUser(user, token); /// Connects the current user, this triggers a connection to the API. /// It returns a [Future] that resolves when the connection is setup. - Future connectUser(User user, String token) async { - if (_connectCompleter != null && !_connectCompleter.isCompleted) { + Future connectUser(User? user, String? token) async { + if (_connectCompleter != null && !_connectCompleter!.isCompleted) { logger.warning('Already connecting'); throw Exception('Already connecting'); } @@ -398,15 +417,23 @@ class StreamChatClient { _connectCompleter = Completer(); logger.info('connect user'); + + if (user == null) { + final e = Error(); + _connectCompleter! + .completeError(e, StackTrace.fromString('No user provided.')); + throw e; + } + state.user = OwnUser.fromJson(user.toJson()); this.token = token; _anonymous = false; return connect().then((event) { - _connectCompleter.complete(event); + _connectCompleter!.complete(event); return event; }).catchError((e, s) { - _connectCompleter.completeError(e, s); + _connectCompleter!.completeError(e, s); throw e; }); } @@ -416,28 +443,29 @@ class StreamChatClient { @Deprecated( 'Use `connectUserWithProvider` instead. Will be removed in Future releases', ) - Future setUserWithProvider(User user) => connectUserWithProvider(user); + Future setUserWithProvider(User user) => + connectUserWithProvider(user); /// Connects the current user using the [tokenProvider] to fetch the token. /// It returns a [Future] that resolves when the connection is setup. - Future connectUserWithProvider(User user) async { + Future connectUserWithProvider(User user) async { if (tokenProvider == null) { throw Exception(''' TokenProvider must be provided in the constructor in order to use `connectUserWithProvider` method. Use `connectUser` providing a token. '''); } - final token = await tokenProvider(user.id); + final token = await tokenProvider!(user.id); return connectUser(user, token); } /// Stream of [Event] coming from websocket connection /// Pass an eventType as parameter in order to filter just a type of event Stream on([ - String eventType, - String eventType2, - String eventType3, - String eventType4, + String? eventType, + String? eventType2, + String? eventType3, + String? eventType4, ]) => stream.where((event) => eventType == null || @@ -455,9 +483,10 @@ class StreamChatClient { } if (!event.isLocal) { - if (_synced && event.createdAt != null) { + final createdAt = event.createdAt; + if (_synced && createdAt != null) { await _chatPersistenceClient?.updateConnectionInfo(event); - await _chatPersistenceClient?.updateLastSyncAt(event.createdAt); + await _chatPersistenceClient?.updateLastSyncAt(createdAt); } } @@ -471,10 +500,10 @@ class StreamChatClient { _controller.add(event); } - Completer _connectCompleter; + Completer? _connectCompleter; /// Connect the client websocket - Future connect() async { + Future connect() async { logger.info('connecting'); if (wsConnectionStatus == ConnectionStatus.connecting) { logger.warning('Already connecting'); @@ -490,20 +519,20 @@ class StreamChatClient { if (_originalChatPersistenceClient != null) { _chatPersistenceClient = _originalChatPersistenceClient; - await _chatPersistenceClient.connect(state.user.id); + await _chatPersistenceClient!.connect(state.user!.id); } _ws = WebSocket( baseUrl: baseURL, - user: state.user, + user: state.user!, connectParams: { 'api_key': apiKey, - 'authorization': token, + 'authorization': token!, 'stream-auth-type': _authType, 'X-Stream-Client': _userAgent, }, connectPayload: { - 'user_id': state.user.id, + 'user_id': state.user!.id, 'server_determines_connection_id': true, }, handler: handleEvent, @@ -520,17 +549,15 @@ class StreamChatClient { ); if (status == ConnectionStatus.connected) { - handleEvent(Event( + handleEvent(const Event( type: EventType.connectionRecovered, online: true, )); - if (state.channels?.isNotEmpty == true) { + if (state.channels.isNotEmpty == true) { // ignore: unawaited_futures - queryChannelsOnline(filter: { - 'cid': { - '\$in': state.channels.keys.toList(), - }, - }).then( + queryChannelsOnline( + filter: Filter.in_('cid', state.channels.keys.toList()), + ).then( (_) async { await resync(); }, @@ -546,9 +573,11 @@ class StreamChatClient { var event = await _chatPersistenceClient?.getConnectionInfo(); - await _ws.connect().then((e) { - _chatPersistenceClient?.updateConnectionInfo(e); - event = e; + await _ws.connect().then((e) async { + if (e != null) { + _chatPersistenceClient?.updateConnectionInfo(e); + event = e; + } resync(); }).catchError((err, stacktrace) { logger.severe('error connecting ws', err, stacktrace); @@ -562,7 +591,7 @@ class StreamChatClient { } /// Get the events missed while offline to sync the offline storage - Future resync([List cids]) async { + Future resync([List? cids]) async { final lastSyncAt = await _chatPersistenceClient?.getLastSyncAt(); if (lastSyncAt == null) { @@ -588,7 +617,7 @@ class StreamChatClient { SyncResponse.fromJson, ); - res.events.sort((a, b) => a.createdAt.compareTo(b.createdAt)); + res.events.sort((a, b) => a.createdAt!.compareTo(b.createdAt!)); res.events.forEach((element) { logger @@ -605,26 +634,26 @@ class StreamChatClient { } } - String _asMap(sort) => sort?.map((s) => s.toJson().toString())?.join(''); + String? _asMap(sort) => sort?.map((s) => s.toJson().toString())?.join(''); final _queryChannelsStreams = >>{}; /// Requests channels with a given query. Stream> queryChannels({ - Map filter, - List> sort, - Map options, + Filter? filter, + List>? sort, + Map? options, PaginationParams paginationParams = const PaginationParams(), - int messageLimit, + int? messageLimit, bool waitForConnect = true, }) async* { final hash = base64.encode(utf8.encode( - '$filter${_asMap(sort)}$options${paginationParams?.toJson()}' + '$filter${_asMap(sort)}$options${paginationParams.toJson()}' '$messageLimit', )); if (_queryChannelsStreams.containsKey(hash)) { - yield await _queryChannelsStreams[hash]; + yield await _queryChannelsStreams[hash]!; } else { final channels = await queryChannelsOffline( filter: filter, @@ -656,17 +685,17 @@ class StreamChatClient { /// Requests channels with a given query from the API. Future> queryChannelsOnline({ - @required Map filter, - List> sort, - Map options, - int messageLimit, + Filter? filter, + List>? sort, + Map? options, + int? messageLimit, PaginationParams paginationParams = const PaginationParams(), bool waitForConnect = true, }) async { if (waitForConnect) { - if (_connectCompleter != null && !_connectCompleter.isCompleted) { + if (_connectCompleter != null && !_connectCompleter!.isCompleted) { logger.info('awaiting connection completer'); - await _connectCompleter.future; + await _connectCompleter!.future; } if (wsConnectionStatus != ConnectionStatus.connected) { throw Exception( @@ -698,9 +727,7 @@ class StreamChatClient { payload.addAll(options); } - if (paginationParams != null) { - payload.addAll(paginationParams.toJson()); - } + payload.addAll(paginationParams.toJson()); final response = await get( '/channels', @@ -714,7 +741,7 @@ class StreamChatClient { QueryChannelsResponse.fromJson, ); - if ((res.channels ?? []).isEmpty && (paginationParams?.offset ?? 0) == 0) { + if (res.channels.isEmpty && paginationParams.offset == 0) { logger.warning( ''' We could not find any channel for this query. @@ -733,15 +760,14 @@ class StreamChatClient { state._updateUsers(users); - logger.info('Got ${res.channels?.length} channels from api'); + logger.info('Got ${res.channels.length} channels from api'); final updateData = _mapChannelStateToChannel(channels); await _chatPersistenceClient?.updateChannelQueries( filter, - channels.map((c) => c.channel.cid).toList(), - clearQueryCache: - paginationParams?.offset == null || paginationParams.offset == 0, + channels.map((c) => c.channel!.cid).toList(), + clearQueryCache: paginationParams.offset == 0, ); state.channels = updateData.key; @@ -750,15 +776,16 @@ class StreamChatClient { /// Requests channels with a given query from the Persistence client. Future> queryChannelsOffline({ - @required Map filter, - @required List> sort, + Filter? filter, + List>? sort, PaginationParams paginationParams = const PaginationParams(), }) async { - final offlineChannels = await _chatPersistenceClient?.getChannelStates( - filter: filter, - sort: sort, - paginationParams: paginationParams, - ); + final offlineChannels = (await _chatPersistenceClient?.getChannelStates( + filter: filter, + sort: sort, + paginationParams: paginationParams, + )) ?? + []; final updatedData = _mapChannelStateToChannel(offlineChannels); state.channels = updatedData.key; return updatedData.value; @@ -767,26 +794,26 @@ class StreamChatClient { MapEntry, List> _mapChannelStateToChannel( List channelStates, ) { - final channels = {...state.channels ?? {}}; + final channels = {...state.channels}; final newChannels = []; - if (channelStates != null) { - for (final channelState in channelStates) { - final channel = channels[channelState.channel.cid]; - if (channel != null) { - channel.state?.updateChannelState(channelState); - newChannels.add(channel); - } else { - final newChannel = Channel.fromState(this, channelState); - channels[newChannel.cid] = newChannel; - newChannels.add(newChannel); + for (final channelState in channelStates) { + final channel = channels[channelState.channel!.cid]; + if (channel != null) { + channel.state?.updateChannelState(channelState); + newChannels.add(channel); + } else { + final newChannel = Channel.fromState(this, channelState); + if (newChannel.cid != null) { + channels[newChannel.cid!] = newChannel; } + newChannels.add(newChannel); } } return MapEntry(channels, newChannels); } Object _parseError(DioError error) { - if (error.type == DioErrorType.RESPONSE) { + if (error.type == DioErrorType.response) { final apiError = ApiError(error.response?.data, error.response?.statusCode); logger.severe('apiError: ${apiError.toString()}'); @@ -799,7 +826,7 @@ class StreamChatClient { /// Handy method to make http GET request with error parsing. Future> get( String path, { - Map queryParameters, + Map? queryParameters, }) async { try { final response = await httpClient.get( @@ -817,8 +844,8 @@ class StreamChatClient { Future> post( String path, { dynamic data, - ProgressCallback onSendProgress, - CancelToken cancelToken, + ProgressCallback? onSendProgress, + CancelToken? cancelToken, }) async { try { final response = await httpClient.post( @@ -837,8 +864,8 @@ class StreamChatClient { /// Handy method to make http DELETE request with error parsing. Future> delete( String path, { - Map queryParameters, - CancelToken cancelToken, + Map? queryParameters, + CancelToken? cancelToken, }) async { try { final response = await httpClient.delete( @@ -856,7 +883,7 @@ class StreamChatClient { /// Handy method to make http PATCH request with error parsing. Future> patch( String path, { - Map queryParameters, + Map? queryParameters, dynamic data, }) async { try { @@ -875,7 +902,7 @@ class StreamChatClient { /// Handy method to make http PUT request with error parsing. Future> put( String path, { - Map queryParameters, + Map? queryParameters, dynamic data, }) async { try { @@ -892,12 +919,10 @@ class StreamChatClient { } /// Used to log errors and stacktrace in case of bad json deserialization - T decode(String j, DecoderFunction decoderFunction) { + T decode(String? j, DecoderFunction decoderFunction) { try { - if (j == null) { - return null; - } - return decoderFunction(json.decode(j)); + final data = j ?? '{}'; + return decoderFunction(json.decode(data)); } catch (error, stacktrace) { logger.severe('Error decoding response', error, stacktrace); rethrow; @@ -909,7 +934,7 @@ class StreamChatClient { String get _userAgent => 'stream-chat-dart-client-${CurrentPlatform.name}-' '${PACKAGE_VERSION.split('+')[0]}'; - Map get _commonQueryParams => { + Map get _commonQueryParams => { 'user_id': state.user?.id, 'api_key': apiKey, 'connection_id': _connectionId, @@ -919,13 +944,13 @@ class StreamChatClient { /// the API. It returns a [Future] that resolves when the connection is setup. @Deprecated( 'Use `connectAnonymousUser` instead. Will be removed in Future releases') - Future setAnonymousUser() => connectAnonymousUser(); + Future setAnonymousUser() => connectAnonymousUser(); /// Connects the current user with an anonymous id, this triggers a connection /// to the API. It returns a [Future] that resolves when the connection is /// setup. - Future connectAnonymousUser() async { - if (_connectCompleter != null && !_connectCompleter.isCompleted) { + Future connectAnonymousUser() async { + if (_connectCompleter != null && !_connectCompleter!.isCompleted) { logger.warning('Already connecting'); throw Exception('Already connecting'); } @@ -933,14 +958,14 @@ class StreamChatClient { _connectCompleter = Completer(); _anonymous = true; - final uuid = Uuid(); + const uuid = Uuid(); state.user = OwnUser(id: uuid.v4()); return connect().then((event) { - _connectCompleter.complete(event); + _connectCompleter!.complete(event); return event; }).catchError((e, s) { - _connectCompleter.completeError(e, s); + _connectCompleter!.completeError(e, s); throw e; }); } @@ -949,16 +974,17 @@ class StreamChatClient { /// It returns a [Future] that resolves when the connection is setup. @Deprecated( 'Use `connectGuestUser` instead. Will be removed in Future releases') - Future setGuestUser(User user) => connectGuestUser(user); + Future setGuestUser(User user) => connectGuestUser(user); /// Connects the current user as guest, this triggers a connection to the API. /// It returns a [Future] that resolves when the connection is setup. - Future connectGuestUser(User user) async { + Future connectGuestUser(User user) async { _anonymous = true; final response = await post('/guest', data: {'user': user.toJson()}) .then((res) => decode( res.data, ConnectGuestUserResponse.fromJson)) .whenComplete(() => _anonymous = false); + return connectUser( response.user, response.accessToken, @@ -991,23 +1017,23 @@ class StreamChatClient { Future _disconnect() async { logger.info('Client disconnecting'); - await _ws?.disconnect(); + await _ws.disconnect(); await _connectionStatusSubscription?.cancel(); } /// Requests users with a given query. Future queryUsers({ - Map filter, - List sort, - Map options, - PaginationParams pagination, + Filter? filter, + List? sort, + Map? options, + PaginationParams? pagination, }) async { final defaultOptions = { 'presence': _hasConnectionId, }; final payload = { - 'filter_conditions': filter ?? {}, + 'filter_conditions': filter, 'sort': sort, }..addAll(defaultOptions); @@ -1031,23 +1057,20 @@ class StreamChatClient { QueryUsersResponse.fromJson, ); - state?._updateUsers(response.users); + state._updateUsers(response.users); return response; } /// A message search. Future search( - Map filters, { - String query, - List sort, - PaginationParams paginationParams, - Map messageFilters, + Filter filter, { + String? query, + List? sort, + PaginationParams? paginationParams, + Filter? messageFilters, }) async { assert(() { - if (filters == null || filters.isEmpty) { - throw ArgumentError('`filters` cannot be set as null or empty'); - } if (query == null && messageFilters == null) { throw ArgumentError('Provide at least `query` or `messageFilters`'); } @@ -1060,7 +1083,7 @@ class StreamChatClient { }(), 'Check incoming params.'); final payload = { - 'filter_conditions': filters, + 'filter_conditions': filter, 'message_filter_conditions': messageFilters, 'query': query, 'sort': sort, @@ -1080,10 +1103,10 @@ class StreamChatClient { AttachmentFile file, String channelId, String channelType, { - ProgressCallback onSendProgress, - CancelToken cancelToken, + ProgressCallback? onSendProgress, + CancelToken? cancelToken, }) => - attachmentFileUploader.sendFile( + attachmentFileUploader!.sendFile( file, channelId, channelType, @@ -1096,10 +1119,10 @@ class StreamChatClient { AttachmentFile image, String channelId, String channelType, { - ProgressCallback onSendProgress, - CancelToken cancelToken, + ProgressCallback? onSendProgress, + CancelToken? cancelToken, }) => - attachmentFileUploader.sendImage( + attachmentFileUploader!.sendImage( image, channelId, channelType, @@ -1112,9 +1135,9 @@ class StreamChatClient { String url, String channelId, String channelType, { - CancelToken cancelToken, + CancelToken? cancelToken, }) => - attachmentFileUploader.deleteFile( + attachmentFileUploader!.deleteFile( url, channelId, channelType, @@ -1126,9 +1149,9 @@ class StreamChatClient { String url, String channelId, String channelType, { - CancelToken cancelToken, + CancelToken? cancelToken, }) => - attachmentFileUploader.deleteImage( + attachmentFileUploader!.deleteImage( url, channelId, channelType, @@ -1170,16 +1193,13 @@ class StreamChatClient { /// Returns a channel client with the given type, id and custom data. Channel channel( String type, { - String id, - Map extraData, + String? id, + Map extraData = const {}, }) { - if (type != null && - id != null && - state.channels?.containsKey('$type:$id') == true) { - return state.channels['$type:$id']; + if (id != null && state.channels.containsKey('$type:$id')) { + return state.channels['$type:$id']!; } - - return Channel(this, type, id, extraData); + return Channel(this, type, id, extraData: extraData); } /// Update or Create the given user object. @@ -1305,7 +1325,10 @@ class StreamChatClient { /// Sends the message to the given channel Future sendMessage( - Message message, String channelId, String channelType) async { + Message message, + String channelId, + String channelType, + ) async { final response = await post( '/channels/$channelType/$channelId/message', data: {'message': message.toJson()}, @@ -1343,14 +1366,13 @@ class StreamChatClient { ) { assert(() { if (timeoutOrExpirationDate is! DateTime && - timeoutOrExpirationDate is! num && - timeoutOrExpirationDate != null) { + timeoutOrExpirationDate is! num) { throw ArgumentError('Invalid timeout or Expiration date'); } return true; }(), 'Check whether time out is valid'); - DateTime pinExpires; + DateTime? pinExpires; if (timeoutOrExpirationDate is DateTime) { pinExpires = timeoutOrExpirationDate.toUtc(); } else if (timeoutOrExpirationDate is num) { @@ -1381,12 +1403,12 @@ class ClientState { .map((e) => e.me) .listen((user) { _userController.add(user); - if (user.totalUnreadCount != null) { - _totalUnreadCountController.add(user.totalUnreadCount); + if (user?.totalUnreadCount != null) { + _totalUnreadCountController.add(user?.totalUnreadCount); } - if (user.unreadChannels != null) { - _unreadChannelsController.add(user.unreadChannels); + if (user?.unreadChannels != null) { + _unreadChannelsController.add(user?.unreadChannels); } }), _client @@ -1411,23 +1433,25 @@ class ClientState { final _subscriptions = []; /// Used internally for optimistic update of unread count - set totalUnreadCount(int unreadCount) { - _totalUnreadCountController?.add(unreadCount ?? 0); + set totalUnreadCount(int? unreadCount) { + _totalUnreadCountController.add(unreadCount ?? 0); } void _listenChannelHidden() { _subscriptions.add(_client.on(EventType.channelHidden).listen((event) { - _client.chatPersistenceClient?.deleteChannels([event.cid]); - if (channels != null) { - channels = channels..removeWhere((cid, ch) => cid == event.cid); + final cid = event.cid; + + if (cid != null) { + _client.chatPersistenceClient?.deleteChannels([cid]); } + channels = channels..removeWhere((cid, ch) => cid == event.cid); })); } void _listenUserUpdated() { _subscriptions.add(_client.on(EventType.userUpdated).listen((event) { - if (event.user.id == user.id) { - user = OwnUser.fromJson(event.user.toJson()); + if (event.user!.id == user!.id) { + user = OwnUser.fromJson(event.user!.toJson()); } _updateUser(event.user); })); @@ -1441,60 +1465,60 @@ class ClientState { EventType.notificationChannelDeleted, ) .listen((Event event) async { - final eventChannel = event.channel; + final eventChannel = event.channel!; await _client.chatPersistenceClient?.deleteChannels([eventChannel.cid]); - if (channels != null) { - channels = channels..remove(eventChannel.cid); - } + channels = channels..remove(eventChannel.cid); })); } final StreamChatClient _client; /// Update user information - set user(OwnUser user) { + set user(OwnUser? user) { _userController.add(user); } - void _updateUsers(List userList) { + void _updateUsers(List userList) { final newUsers = { - ...users ?? {}, - for (var user in userList) user.id: user, + ...users, + for (var user in userList) user!.id: user, }; _usersController.add(newUsers); } - void _updateUser(User user) => _updateUsers([user]); + void _updateUser(User? user) => _updateUsers([user]); /// The current user - OwnUser get user => _userController.value; + OwnUser? get user => _userController.value; /// The current user as a stream - Stream get userStream => _userController.stream; + Stream get userStream => _userController.stream; /// The current user - Map get users => _usersController.value; + Map get users => + _usersController.value as Map; /// The current user as a stream - Stream> get usersStream => _usersController.stream; + Stream> get usersStream => _usersController.stream; /// The current unread channels count - int get unreadChannels => _unreadChannelsController.value; + int? get unreadChannels => _unreadChannelsController.value; /// The current unread channels count as a stream - Stream get unreadChannelsStream => _unreadChannelsController.stream; + Stream get unreadChannelsStream => _unreadChannelsController.stream; /// The current total unread messages count - int get totalUnreadCount => _totalUnreadCountController.value; + int? get totalUnreadCount => _totalUnreadCountController.value; /// The current total unread messages count as a stream - Stream get totalUnreadCountStream => _totalUnreadCountController.stream; + Stream get totalUnreadCountStream => _totalUnreadCountController.stream; /// The current list of channels in memory as a stream - Stream> get channelsStream => _channelsController.stream; + Stream?> get channelsStream => + _channelsController.stream; /// The current list of channels in memory - Map get channels => _channelsController.value; + Map get channels => _channelsController.value!; set channels(Map v) { _channelsController.add(v); @@ -1502,11 +1526,11 @@ class ClientState { final BehaviorSubject> _channelsController = BehaviorSubject.seeded({}); - final BehaviorSubject _userController = BehaviorSubject(); - final BehaviorSubject> _usersController = + final BehaviorSubject _userController = BehaviorSubject(); + final BehaviorSubject> _usersController = BehaviorSubject.seeded({}); - final BehaviorSubject _unreadChannelsController = BehaviorSubject(); - final BehaviorSubject _totalUnreadCountController = BehaviorSubject(); + final BehaviorSubject _unreadChannelsController = BehaviorSubject(); + final BehaviorSubject _totalUnreadCountController = BehaviorSubject(); /// Call this method to dispose this object void dispose() { diff --git a/packages/stream_chat/lib/src/db/chat_persistence_client.dart b/packages/stream_chat/lib/src/db/chat_persistence_client.dart index d15772df..e9c21956 100644 --- a/packages/stream_chat/lib/src/db/chat_persistence_client.dart +++ b/packages/stream_chat/lib/src/db/chat_persistence_client.dart @@ -2,11 +2,13 @@ import 'package:stream_chat/src/api/requests.dart'; import 'package:stream_chat/src/models/channel_model.dart'; import 'package:stream_chat/src/models/channel_state.dart'; import 'package:stream_chat/src/models/event.dart'; +import 'package:stream_chat/src/models/filter.dart'; import 'package:stream_chat/src/models/member.dart'; import 'package:stream_chat/src/models/message.dart'; import 'package:stream_chat/src/models/reaction.dart'; import 'package:stream_chat/src/models/read.dart'; import 'package:stream_chat/src/models/user.dart'; +import 'package:stream_chat/src/extensions/iterable_extension.dart'; /// A simple client used for persisting chat data locally. abstract class ChatPersistenceClient { @@ -20,14 +22,14 @@ abstract class ChatPersistenceClient { /// Get stored replies by messageId Future> getReplies( String parentId, { - PaginationParams options, + PaginationParams? options, }); /// Get stored connection event - Future getConnectionInfo(); + Future getConnectionInfo(); /// Get stored lastSyncAt - Future getLastSyncAt(); + Future getLastSyncAt(); /// Update stored connection event Future updateConnectionInfo(Event event); @@ -39,7 +41,7 @@ abstract class ChatPersistenceClient { Future> getChannelCids(); /// Get stored [ChannelModel]s by providing channel [cid] - Future getChannelByCid(String cid); + Future getChannelByCid(String cid); /// Get stored channel [Member]s by providing channel [cid] Future> getMembersByCid(String cid); @@ -53,20 +55,20 @@ abstract class ChatPersistenceClient { /// for filtering out messages Future> getMessagesByCid( String cid, { - PaginationParams messagePagination, + PaginationParams? messagePagination, }); /// Get stored pinned [Message]s by providing channel [cid] Future> getPinnedMessagesByCid( String cid, { - PaginationParams messagePagination, + PaginationParams? messagePagination, }); /// Get [ChannelState] data by providing channel [cid] Future getChannelStateByCid( String cid, { - PaginationParams messagePagination, - PaginationParams pinnedMessagePagination, + PaginationParams? messagePagination, + PaginationParams? pinnedMessagePagination, }) async { final data = await Future.wait([ getMembersByCid(cid), @@ -76,11 +78,11 @@ abstract class ChatPersistenceClient { getPinnedMessagesByCid(cid, messagePagination: pinnedMessagePagination), ]); return ChannelState( - members: data[0], - read: data[1], - channel: data[2], - messages: data[3], - pinnedMessages: data[4], + members: data[0] as List, + read: data[1] as List, + channel: data[2] as ChannelModel?, + messages: data[3] as List, + pinnedMessages: data[4] as List, ); } @@ -89,9 +91,9 @@ abstract class ChatPersistenceClient { /// Optionally, pass [filter], [sort], [paginationParams] /// for filtering out states. Future> getChannelStates({ - Map filter, - List> sort = const [], - PaginationParams paginationParams, + Filter? filter, + List>? sort, + PaginationParams? paginationParams, }); /// Update list of channel queries. @@ -99,7 +101,7 @@ abstract class ChatPersistenceClient { /// If [clearQueryCache] is true before the insert /// the list of matching rows will be deleted Future updateChannelQueries( - Map filter, + Filter? filter, List cids, { bool clearQueryCache = false, }); @@ -180,8 +182,11 @@ abstract class ChatPersistenceClient { .map((m) => m.id) .toList(growable: false)); + final cleanedChannelStates = + channelStates.where((it) => it.channel != null); + final deleteMembers = deleteMembersByCids( - channelStates.map((it) => it.channel.cid).toList(growable: false), + cleanedChannelStates.map((it) => it.channel!.cid).toList(growable: false), ); await Future.wait([ @@ -189,58 +194,57 @@ abstract class ChatPersistenceClient { deleteMembers, ]); - final channels = - channelStates.map((it) => it.channel).where((it) => it != null); + final channels = cleanedChannelStates.map((it) => it.channel).withNullifyer; - final reactions = channelStates + final reactions = cleanedChannelStates .expand((it) => it.messages) .expand((it) => [ if (it.ownReactions != null) - ...it.ownReactions.where((r) => r.userId != null), + ...it.ownReactions!.where((r) => r.userId != null), if (it.latestReactions != null) - ...it.latestReactions.where((r) => r.userId != null) + ...it.latestReactions!.where((r) => r.userId != null), ]) - .where((it) => it != null); + .withNullifyer; - final users = channelStates + final users = cleanedChannelStates .map((cs) => [ cs.channel?.createdBy, ...cs.messages - ?.map((m) => [ + .map((m) => [ m.user, if (m.latestReactions != null) - ...m.latestReactions.map((r) => r.user), + ...m.latestReactions!.map((r) => r.user), if (m.ownReactions != null) - ...m.ownReactions.map((r) => r.user), + ...m.ownReactions!.map((r) => r.user), ]) - ?.expand((v) => v), - if (cs.read != null) ...cs.read.map((r) => r.user), - if (cs.members != null) ...cs.members.map((m) => m.user), + .expand((v) => v), + ...cs.read.map((r) => r.user), + ...cs.members.map((m) => m.user), ]) .expand((it) => it) - .where((it) => it != null); + .withNullifyer; - final updateMessagesFuture = channelStates.map((it) { - final cid = it.channel.cid; - final messages = it.messages.where((it) => it != null); + final updateMessagesFuture = cleanedChannelStates.map((it) { + final cid = it.channel!.cid; + final messages = it.messages; return updateMessages(cid, messages.toList(growable: false)); }).toList(growable: false); - final updatePinnedMessagesFuture = channelStates.map((it) { - final cid = it.channel.cid; - final messages = it.pinnedMessages.where((it) => it != null); + final updatePinnedMessagesFuture = cleanedChannelStates.map((it) { + final cid = it.channel!.cid; + final messages = it.pinnedMessages; return updatePinnedMessages(cid, messages.toList(growable: false)); }).toList(growable: false); - final updateReadsFuture = channelStates.map((it) { - final cid = it.channel.cid; - final reads = it.read?.where((it) => it != null) ?? []; + final updateReadsFuture = cleanedChannelStates.map((it) { + final cid = it.channel!.cid; + final reads = it.read; return updateReads(cid, reads.toList(growable: false)); }).toList(growable: false); - final updateMembersFuture = channelStates.map((it) { - final cid = it.channel.cid; - final members = it.members.where((it) => it != null); + final updateMembersFuture = cleanedChannelStates.map((it) { + final cid = it.channel!.cid; + final members = it.members; return updateMembers(cid, members.toList(growable: false)); }).toList(growable: false); diff --git a/packages/stream_chat/lib/src/exceptions.dart b/packages/stream_chat/lib/src/exceptions.dart index 60dd6bdf..714bb386 100644 --- a/packages/stream_chat/lib/src/exceptions.dart +++ b/packages/stream_chat/lib/src/exceptions.dart @@ -4,25 +4,25 @@ import 'dart:convert'; class ApiError extends Error { /// Creates a new ApiError instance using the response body and status code ApiError(this.body, this.status) : jsonData = _decode(body) { - if (jsonData != null && jsonData.containsKey('code')) { - _code = jsonData['code']; + if (jsonData != null && jsonData!.containsKey('code')) { + _code = jsonData!['code']; } } /// Raw body of the response - final String body; + final String? body; /// Json parsed body - final Map jsonData; + final Map? jsonData; /// Http status code of the response - final int status; + final int? status; /// Stream specific error code - int get code => _code; - int _code; + int? get code => _code; + int? _code; - static Map _decode(String body) { + static Map? _decode(String? body) { try { if (body == null) { return null; diff --git a/packages/stream_chat/lib/src/extensions/iterable_extension.dart b/packages/stream_chat/lib/src/extensions/iterable_extension.dart new file mode 100644 index 00000000..a7d30f76 --- /dev/null +++ b/packages/stream_chat/lib/src/extensions/iterable_extension.dart @@ -0,0 +1,9 @@ +/// Useful extension functions for [Iterable] +extension IterableX on Iterable { + /// Removes all the null values + /// and converts `Iterable` into `Iterable` + Iterable get withNullifyer => [ + for (final item in this) + if (item != null) item + ]; +} diff --git a/packages/stream_chat/lib/src/extensions/map_extension.dart b/packages/stream_chat/lib/src/extensions/map_extension.dart index 1a3376ad..d99ab96a 100644 --- a/packages/stream_chat/lib/src/extensions/map_extension.dart +++ b/packages/stream_chat/lib/src/extensions/map_extension.dart @@ -1,6 +1,6 @@ /// Useful extension functions for [Map] -extension MapX on Map { +extension MapX on Map { /// Returns a new map with null keys or values removed - Map get nullProtected => - {...this}..removeWhere((key, value) => key == null || value == null); + Map get nullProtected => + Map.from(this)..removeWhere((key, value) => key == null || value == null); } diff --git a/packages/stream_chat/lib/src/extensions/rate_limit.dart b/packages/stream_chat/lib/src/extensions/rate_limit.dart index c9f5934f..bf54fe5c 100644 --- a/packages/stream_chat/lib/src/extensions/rate_limit.dart +++ b/packages/stream_chat/lib/src/extensions/rate_limit.dart @@ -10,7 +10,7 @@ extension RateLimit on Function { Duration wait, { bool leading = false, bool trailing = true, - Duration maxWait, + Duration? maxWait, }) => Debounce( this, @@ -40,7 +40,7 @@ Debounce debounce( Duration wait, { bool leading = false, bool trailing = true, - Duration maxWait, + Duration? maxWait, }) => Debounce( func, @@ -121,13 +121,13 @@ class Debounce { Duration wait, { bool leading = false, bool trailing = true, - Duration maxWait, + Duration? maxWait, }) : _leading = leading, _trailing = trailing, - _wait = wait?.inMilliseconds ?? 0, + _wait = wait.inMilliseconds, _maxing = maxWait != null { if (_maxing) { - _maxWait = math.max(maxWait.inMilliseconds, _wait); + _maxWait = math.max(maxWait!.inMilliseconds, _wait); } } @@ -137,15 +137,15 @@ class Debounce { final int _wait; final bool _maxing; - int _maxWait; - List _lastArgs; - Map _lastNamedArgs; - Timer _timer; - int _lastCallTime; - Object _result; - int _lastInvokeTime = 0; + late int _maxWait; + List? _lastArgs; + Map? _lastNamedArgs; + Timer? _timer; + int? _lastCallTime; + Object? _result; + int? _lastInvokeTime = 0; - Object _invokeFunc(int time) { + Object? _invokeFunc(int? time) { final args = _lastArgs; final namedArgs = _lastNamedArgs; _lastArgs = _lastNamedArgs = null; @@ -154,11 +154,11 @@ class Debounce { } Timer _startTimer(Function pendingFunc, int wait) => - Timer(Duration(milliseconds: wait), pendingFunc); + Timer(Duration(milliseconds: wait), pendingFunc as void Function()); bool _shouldInvoke(int time) { final timeSinceLastCall = time - (_lastCallTime ?? double.nan); - final timeSinceLastInvoke = time - _lastInvokeTime; + final timeSinceLastInvoke = time - _lastInvokeTime!; // Either this is the first call, activity has stopped and we're at the // trailing edge, the system time has gone backwards and we're treating @@ -169,7 +169,7 @@ class Debounce { (_maxing && timeSinceLastInvoke >= _maxWait); } - Object _trailingEdge(int time) { + Object? _trailingEdge(int time) { _timer = null; // Only invoke if we have `lastArgs` which means `func` has been @@ -182,8 +182,8 @@ class Debounce { } int _remainingWait(int time) { - final timeSinceLastCall = time - _lastCallTime; - final timeSinceLastInvoke = time - _lastInvokeTime; + final timeSinceLastCall = time - _lastCallTime!; + final timeSinceLastInvoke = time - _lastInvokeTime!; final timeWaiting = _wait - timeSinceLastCall; return _maxing @@ -201,7 +201,7 @@ class Debounce { } } - Object _leadingEdge(int time) { + Object? _leadingEdge(int? time) { // Reset any `maxWait` timer. _lastInvokeTime = time; // Start the timer for the trailing edge. @@ -218,7 +218,7 @@ class Debounce { } /// Immediately invokes all the remaining delayed functions. - Object flush() { + Object? flush() { final now = DateTime.now().millisecondsSinceEpoch; return _timer == null ? _result : _trailingEdge(now); } @@ -228,15 +228,15 @@ class Debounce { /// Calls/invokes this class like a function. /// Pass [args] and [namedArgs] to be used while invoking [_func]. - Object call( + Object? call( List args, { - Map namedArgs, + Map? namedArgs, }) { final time = DateTime.now().millisecondsSinceEpoch; final isInvoking = _shouldInvoke(time); _lastArgs = args; - _lastNamedArgs = namedArgs; + _lastNamedArgs = namedArgs as Map?; _lastCallTime = time; if (isInvoking) { @@ -323,13 +323,13 @@ class Throttle { void cancel() => _debounce.cancel(); /// Immediately invokes all the remaining delayed functions. - Object flush() => _debounce.flush(); + Object? flush() => _debounce.flush(); /// True if there are functions remaining to get invoked. bool get isPending => _debounce.isPending; /// Calls/invokes this class like a function. /// Pass [args] and [namedArgs] to be used while invoking `func`. - Object call(List args, {Map namedArgs}) => + Object? call(List args, {Map? namedArgs}) => _debounce.call(args, namedArgs: namedArgs); } diff --git a/packages/stream_chat/lib/src/extensions/string_extension.dart b/packages/stream_chat/lib/src/extensions/string_extension.dart index 49949412..440e2cb6 100644 --- a/packages/stream_chat/lib/src/extensions/string_extension.dart +++ b/packages/stream_chat/lib/src/extensions/string_extension.dart @@ -4,12 +4,15 @@ import 'package:mime/mime.dart'; /// Useful extension functions for [String] extension StringX on String { /// Returns the mime type from the passed file name. - http_parser.MediaType get mimeType { - if (this == null) return null; + http_parser.MediaType? get mimeType { if (toLowerCase().endsWith('heic')) { return http_parser.MediaType.parse('image/heic'); } else { - return http_parser.MediaType.parse(lookupMimeType(this)); + final mimeType = lookupMimeType(this); + if (mimeType == null) { + return null; + } + return http_parser.MediaType.parse(mimeType); } } } diff --git a/packages/stream_chat/lib/src/models/action.dart b/packages/stream_chat/lib/src/models/action.dart index 62d0f104..16a307e7 100644 --- a/packages/stream_chat/lib/src/models/action.dart +++ b/packages/stream_chat/lib/src/models/action.dart @@ -6,7 +6,13 @@ part 'action.g.dart'; @JsonSerializable() class Action { /// Constructor used for json serialization - Action({this.name, this.style, this.text, this.type, this.value}); + Action({ + required this.name, + this.style = 'default', + required this.text, + required this.type, + this.value, + }); /// Create a new instance from a json factory Action.fromJson(Map json) => _$ActionFromJson(json); @@ -15,6 +21,7 @@ class Action { final String name; /// The style of the action + @JsonKey(defaultValue: 'default') final String style; /// The test of the action @@ -24,7 +31,7 @@ class Action { final String type; /// The value of the action - final String value; + final String? value; /// Serialize to json Map toJson() => _$ActionToJson(this); diff --git a/packages/stream_chat/lib/src/models/action.g.dart b/packages/stream_chat/lib/src/models/action.g.dart index 3c567843..9ae999ed 100644 --- a/packages/stream_chat/lib/src/models/action.g.dart +++ b/packages/stream_chat/lib/src/models/action.g.dart @@ -6,13 +6,13 @@ part of 'action.dart'; // JsonSerializableGenerator // ************************************************************************** -Action _$ActionFromJson(Map json) { +Action _$ActionFromJson(Map json) { return Action( name: json['name'] as String, - style: json['style'] as String, + style: json['style'] as String? ?? 'default', text: json['text'] as String, type: json['type'] as String, - value: json['value'] as String, + value: json['value'] as String?, ); } diff --git a/packages/stream_chat/lib/src/models/attachment.dart b/packages/stream_chat/lib/src/models/attachment.dart index 548a3e6d..09ea4f58 100644 --- a/packages/stream_chat/lib/src/models/attachment.dart +++ b/packages/stream_chat/lib/src/models/attachment.dart @@ -14,10 +14,10 @@ part 'attachment.g.dart'; class Attachment extends Equatable { /// Constructor used for json serialization Attachment({ - String id, + String? id, this.type, this.titleLink, - String title, + String? title, this.thumbUrl, this.text, this.pretext, @@ -32,17 +32,19 @@ class Attachment extends Equatable { this.authorLink, this.authorIcon, this.assetUrl, - this.actions, - this.extraData, + List? actions, + this.extraData = const {}, this.file, - UploadState uploadState, - }) : id = id ?? Uuid().v4(), + UploadState? uploadState, + }) : id = id ?? const Uuid().v4(), title = title ?? file?.name, - localUri = file?.path != null ? Uri.parse(file.path) : null, - uploadState = uploadState ?? - ((assetUrl != null || imageUrl != null) - ? const UploadState.success() - : const UploadState.preparing()); + localUri = file?.path != null ? Uri.parse(file!.path!) : null, + actions = actions ?? [] { + this.uploadState = uploadState ?? + ((assetUrl != null || imageUrl != null) + ? const UploadState.success() + : const UploadState.preparing()); + } /// Create a new instance from a json factory Attachment.fromJson(Map json) => @@ -56,59 +58,63 @@ class Attachment extends Equatable { ///The attachment type based on the URL resource. This can be: audio, ///image or video - final String type; + final String? type; ///The link to which the attachment message points to. - final String titleLink; + final String? titleLink; /// The attachment title - final String title; + final String? title; /// The URL to the attached file thumbnail. You can use this to represent the /// attached link. - final String thumbUrl; + final String? thumbUrl; /// The attachment text. It will be displayed in the channel next to the /// original message. - final String text; + final String? text; /// Optional text that appears above the attachment block - final String pretext; + final String? pretext; /// The original URL that was used to scrape this attachment. - final String ogScrapeUrl; + final String? ogScrapeUrl; /// The URL to the attached image. This is present for URL pointing to an /// image article (eg. Unsplash) - final String imageUrl; - final String footerIcon; - final String footer; + final String? imageUrl; + final String? footerIcon; + final String? footer; final dynamic fields; - final String fallback; - final String color; + final String? fallback; + final String? color; /// The name of the author. - final String authorName; - final String authorLink; - final String authorIcon; + final String? authorName; + final String? authorLink; + final String? authorIcon; /// The URL to the audio, video or image related to the URL. - final String assetUrl; + final String? assetUrl; /// Actions from a command + @JsonKey(defaultValue: []) final List actions; - final Uri localUri; + final Uri? localUri; /// The file present inside this attachment. - final AttachmentFile file; + final AttachmentFile? file; /// The current upload state of the attachment - final UploadState uploadState; + late final UploadState uploadState; /// Map of custom channel extraData - @JsonKey(includeIfNull: false) - final Map extraData; + @JsonKey( + includeIfNull: false, + defaultValue: {}, + ) + final Map extraData; /// The attachment ID. /// @@ -147,37 +153,37 @@ class Attachment extends Equatable { ]; /// Serialize to json - Map toJson() => Serialization.moveFromExtraDataToRoot( - _$AttachmentToJson(this), topLevelFields) - ..removeWhere((key, value) => dbSpecificTopLevelFields.contains(key)); + Map toJson() => + Serialization.moveFromExtraDataToRoot(_$AttachmentToJson(this)) + ..removeWhere((key, value) => dbSpecificTopLevelFields.contains(key)); /// Serialize to db data - Map toData() => Serialization.moveFromExtraDataToRoot( - _$AttachmentToJson(this), topLevelFields + dbSpecificTopLevelFields); + Map toData() => + Serialization.moveFromExtraDataToRoot(_$AttachmentToJson(this)); Attachment copyWith({ - String id, - String type, - String titleLink, - String title, - String thumbUrl, - String text, - String pretext, - String ogScrapeUrl, - String imageUrl, - String footerIcon, - String footer, + String? id, + String? type, + String? titleLink, + String? title, + String? thumbUrl, + String? text, + String? pretext, + String? ogScrapeUrl, + String? imageUrl, + String? footerIcon, + String? footer, dynamic fields, - String fallback, - String color, - String authorName, - String authorLink, - String authorIcon, - String assetUrl, - List actions, - AttachmentFile file, - UploadState uploadState, - Map extraData, + String? fallback, + String? color, + String? authorName, + String? authorLink, + String? authorIcon, + String? assetUrl, + List? actions, + AttachmentFile? file, + UploadState? uploadState, + Map? extraData, }) => Attachment( id: id ?? this.id, @@ -205,7 +211,7 @@ class Attachment extends Equatable { ); @override - List get props => [ + List get props => [ id, type, titleLink, diff --git a/packages/stream_chat/lib/src/models/attachment.g.dart b/packages/stream_chat/lib/src/models/attachment.g.dart index c45771c2..c42aaf5f 100644 --- a/packages/stream_chat/lib/src/models/attachment.g.dart +++ b/packages/stream_chat/lib/src/models/attachment.g.dart @@ -6,46 +6,40 @@ part of 'attachment.dart'; // JsonSerializableGenerator // ************************************************************************** -Attachment _$AttachmentFromJson(Map json) { +Attachment _$AttachmentFromJson(Map json) { return Attachment( - id: json['id'] as String, - type: json['type'] as String, - titleLink: json['title_link'] as String, - title: json['title'] as String, - thumbUrl: json['thumb_url'] as String, - text: json['text'] as String, - pretext: json['pretext'] as String, - ogScrapeUrl: json['og_scrape_url'] as String, - imageUrl: json['image_url'] as String, - footerIcon: json['footer_icon'] as String, - footer: json['footer'] as String, + id: json['id'] as String?, + type: json['type'] as String?, + titleLink: json['title_link'] as String?, + title: json['title'] as String?, + thumbUrl: json['thumb_url'] as String?, + text: json['text'] as String?, + pretext: json['pretext'] as String?, + ogScrapeUrl: json['og_scrape_url'] as String?, + imageUrl: json['image_url'] as String?, + footerIcon: json['footer_icon'] as String?, + footer: json['footer'] as String?, fields: json['fields'], - fallback: json['fallback'] as String, - color: json['color'] as String, - authorName: json['author_name'] as String, - authorLink: json['author_link'] as String, - authorIcon: json['author_icon'] as String, - assetUrl: json['asset_url'] as String, - actions: (json['actions'] as List) - ?.map((e) => e == null - ? null - : Action.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList(), - extraData: (json['extra_data'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - ), + fallback: json['fallback'] as String?, + color: json['color'] as String?, + authorName: json['author_name'] as String?, + authorLink: json['author_link'] as String?, + authorIcon: json['author_icon'] as String?, + assetUrl: json['asset_url'] as String?, + actions: (json['actions'] as List?) + ?.map((e) => Action.fromJson(e as Map)) + .toList() ?? + [], + extraData: (json['extra_data'] as Map?)?.map( + (k, e) => MapEntry(k, e as Object), + ) ?? + {}, file: json['file'] == null ? null - : AttachmentFile.fromJson((json['file'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )), + : AttachmentFile.fromJson(json['file'] as Map), uploadState: json['upload_state'] == null ? null - : UploadState.fromJson((json['upload_state'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )), + : UploadState.fromJson(json['upload_state'] as Map), ); } @@ -75,10 +69,10 @@ Map _$AttachmentToJson(Attachment instance) { writeNotNull('author_link', instance.authorLink); writeNotNull('author_icon', instance.authorIcon); writeNotNull('asset_url', instance.assetUrl); - writeNotNull('actions', instance.actions?.map((e) => e?.toJson())?.toList()); + val['actions'] = instance.actions.map((e) => e.toJson()).toList(); writeNotNull('file', instance.file?.toJson()); - writeNotNull('upload_state', instance.uploadState?.toJson()); - writeNotNull('extra_data', instance.extraData); - writeNotNull('id', instance.id); + val['upload_state'] = instance.uploadState.toJson(); + val['extra_data'] = instance.extraData; + val['id'] = instance.id; return val; } diff --git a/packages/stream_chat/lib/src/models/attachment_file.dart b/packages/stream_chat/lib/src/models/attachment_file.dart index bae7810f..8dbb70ad 100644 --- a/packages/stream_chat/lib/src/models/attachment_file.dart +++ b/packages/stream_chat/lib/src/models/attachment_file.dart @@ -8,18 +8,21 @@ part 'attachment_file.g.dart'; /// Union class to hold various [UploadState] of a attachment. @freezed -abstract class UploadState with _$UploadState { +class UploadState with _$UploadState { /// Preparing state of the union const factory UploadState.preparing() = Preparing; /// InProgress state of the union - const factory UploadState.inProgress({int uploaded, int total}) = InProgress; + const factory UploadState.inProgress({ + required int uploaded, + required int total, + }) = InProgress; /// Success state of the union const factory UploadState.success() = Success; /// Failed state of the union - const factory UploadState.failed({@required String error}) = Failed; + const factory UploadState.failed({required String error}) = Failed; /// Creates a new instance from a json factory UploadState.fromJson(Map json) => @@ -27,7 +30,7 @@ abstract class UploadState with _$UploadState { } /// Helper extension for UploadState -extension UploadStateX on UploadState { +extension UploadStateX on UploadState? { /// Returns true if state is [Preparing] bool get isPreparing => this is Preparing; @@ -41,20 +44,29 @@ extension UploadStateX on UploadState { bool get isFailed => this is Failed; } -Uint8List _fromString(String bytes) => Uint8List.fromList(bytes.codeUnits); +Uint8List? _fromString(String? bytes) { + if (bytes == null) return null; + return Uint8List.fromList(bytes.codeUnits); +} -String _toString(Uint8List bytes) => String.fromCharCodes(bytes); +String? _toString(Uint8List? bytes) { + if (bytes == null) return null; + return String.fromCharCodes(bytes); +} /// The class that contains the information about an attachment file @JsonSerializable() class AttachmentFile { /// Creates a new [AttachmentFile] instance. const AttachmentFile({ + required this.size, this.path, this.name, this.bytes, - this.size, - }); + }) : assert( + path != null || bytes != null, + 'Either path or bytes should be != null', + ); /// Create a new instance from a json factory AttachmentFile.fromJson(Map json) => @@ -65,21 +77,21 @@ class AttachmentFile { /// ``` /// final File myFile = File(platformFile.path); /// ``` - final String path; + final String? path; /// File name including its extension. - final String name; + final String? name; /// Byte data for this file. Particularly useful if you want to manipulate /// its data or easily upload to somewhere else. @JsonKey(toJson: _toString, fromJson: _fromString) - final Uint8List bytes; + final Uint8List? bytes; /// The file size in bytes. - final int size; + final int? size; /// File extension for this file. - String get extension => name?.split('.')?.last; + String? get extension => name?.split('.').last; /// Serialize to json Map toJson() => _$AttachmentFileToJson(this); diff --git a/packages/stream_chat/lib/src/models/attachment_file.freezed.dart b/packages/stream_chat/lib/src/models/attachment_file.freezed.dart index 4eea1f13..a6c5e0c6 100644 --- a/packages/stream_chat/lib/src/models/attachment_file.freezed.dart +++ b/packages/stream_chat/lib/src/models/attachment_file.freezed.dart @@ -1,5 +1,5 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -// ignore_for_file: deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides part of 'attachment_file.dart'; @@ -8,6 +8,10 @@ part of 'attachment_file.dart'; // ************************************************************************** T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more informations: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); + UploadState _$UploadStateFromJson(Map json) { switch (json['runtimeType'] as String) { case 'preparing': @@ -28,74 +32,72 @@ UploadState _$UploadStateFromJson(Map json) { class _$UploadStateTearOff { const _$UploadStateTearOff(); -// ignore: unused_element Preparing preparing() { return const Preparing(); } -// ignore: unused_element - InProgress inProgress({int uploaded, int total}) { + InProgress inProgress({required int uploaded, required int total}) { return InProgress( uploaded: uploaded, total: total, ); } -// ignore: unused_element Success success() { return const Success(); } -// ignore: unused_element - Failed failed({@required String error}) { + Failed failed({required String error}) { return Failed( error: error, ); } -// ignore: unused_element UploadState fromJson(Map json) { return UploadState.fromJson(json); } } /// @nodoc -// ignore: unused_element const $UploadState = _$UploadStateTearOff(); /// @nodoc mixin _$UploadState { @optionalTypeArgs - TResult when({ - @required TResult preparing(), - @required TResult inProgress(int uploaded, int total), - @required TResult success(), - @required TResult failed(String error), - }); + TResult when({ + required TResult Function() preparing, + required TResult Function(int uploaded, int total) inProgress, + required TResult Function() success, + required TResult Function(String error) failed, + }) => + throw _privateConstructorUsedError; @optionalTypeArgs - TResult maybeWhen({ - TResult preparing(), - TResult inProgress(int uploaded, int total), - TResult success(), - TResult failed(String error), - @required TResult orElse(), - }); + TResult maybeWhen({ + TResult Function()? preparing, + TResult Function(int uploaded, int total)? inProgress, + TResult Function()? success, + TResult Function(String error)? failed, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; @optionalTypeArgs - TResult map({ - @required TResult preparing(Preparing value), - @required TResult inProgress(InProgress value), - @required TResult success(Success value), - @required TResult failed(Failed value), - }); + TResult map({ + required TResult Function(Preparing value) preparing, + required TResult Function(InProgress value) inProgress, + required TResult Function(Success value) success, + required TResult Function(Failed value) failed, + }) => + throw _privateConstructorUsedError; @optionalTypeArgs - TResult maybeMap({ - TResult preparing(Preparing value), - TResult inProgress(InProgress value), - TResult success(Success value), - TResult failed(Failed value), - @required TResult orElse(), - }); - Map toJson(); + TResult maybeMap({ + TResult Function(Preparing value)? preparing, + TResult Function(InProgress value)? inProgress, + TResult Function(Success value)? success, + TResult Function(Failed value)? failed, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; + Map toJson() => throw _privateConstructorUsedError; } /// @nodoc @@ -154,29 +156,24 @@ class _$Preparing implements Preparing { @override @optionalTypeArgs - TResult when({ - @required TResult preparing(), - @required TResult inProgress(int uploaded, int total), - @required TResult success(), - @required TResult failed(String error), + TResult when({ + required TResult Function() preparing, + required TResult Function(int uploaded, int total) inProgress, + required TResult Function() success, + required TResult Function(String error) failed, }) { - assert(preparing != null); - assert(inProgress != null); - assert(success != null); - assert(failed != null); return preparing(); } @override @optionalTypeArgs - TResult maybeWhen({ - TResult preparing(), - TResult inProgress(int uploaded, int total), - TResult success(), - TResult failed(String error), - @required TResult orElse(), + TResult maybeWhen({ + TResult Function()? preparing, + TResult Function(int uploaded, int total)? inProgress, + TResult Function()? success, + TResult Function(String error)? failed, + required TResult orElse(), }) { - assert(orElse != null); if (preparing != null) { return preparing(); } @@ -185,29 +182,24 @@ class _$Preparing implements Preparing { @override @optionalTypeArgs - TResult map({ - @required TResult preparing(Preparing value), - @required TResult inProgress(InProgress value), - @required TResult success(Success value), - @required TResult failed(Failed value), + TResult map({ + required TResult Function(Preparing value) preparing, + required TResult Function(InProgress value) inProgress, + required TResult Function(Success value) success, + required TResult Function(Failed value) failed, }) { - assert(preparing != null); - assert(inProgress != null); - assert(success != null); - assert(failed != null); return preparing(this); } @override @optionalTypeArgs - TResult maybeMap({ - TResult preparing(Preparing value), - TResult inProgress(InProgress value), - TResult success(Success value), - TResult failed(Failed value), - @required TResult orElse(), + TResult maybeMap({ + TResult Function(Preparing value)? preparing, + TResult Function(InProgress value)? inProgress, + TResult Function(Success value)? success, + TResult Function(Failed value)? failed, + required TResult orElse(), }) { - assert(orElse != null); if (preparing != null) { return preparing(this); } @@ -245,12 +237,18 @@ class _$InProgressCopyWithImpl<$Res> extends _$UploadStateCopyWithImpl<$Res> @override $Res call({ - Object uploaded = freezed, - Object total = freezed, + Object? uploaded = freezed, + Object? total = freezed, }) { return _then(InProgress( - uploaded: uploaded == freezed ? _value.uploaded : uploaded as int, - total: total == freezed ? _value.total : total as int, + uploaded: uploaded == freezed + ? _value.uploaded + : uploaded // ignore: cast_nullable_to_non_nullable + as int, + total: total == freezed + ? _value.total + : total // ignore: cast_nullable_to_non_nullable + as int, )); } } @@ -259,7 +257,7 @@ class _$InProgressCopyWithImpl<$Res> extends _$UploadStateCopyWithImpl<$Res> /// @nodoc class _$InProgress implements InProgress { - const _$InProgress({this.uploaded, this.total}); + const _$InProgress({required this.uploaded, required this.total}); factory _$InProgress.fromJson(Map json) => _$_$InProgressFromJson(json); @@ -298,29 +296,24 @@ class _$InProgress implements InProgress { @override @optionalTypeArgs - TResult when({ - @required TResult preparing(), - @required TResult inProgress(int uploaded, int total), - @required TResult success(), - @required TResult failed(String error), + TResult when({ + required TResult Function() preparing, + required TResult Function(int uploaded, int total) inProgress, + required TResult Function() success, + required TResult Function(String error) failed, }) { - assert(preparing != null); - assert(inProgress != null); - assert(success != null); - assert(failed != null); return inProgress(uploaded, total); } @override @optionalTypeArgs - TResult maybeWhen({ - TResult preparing(), - TResult inProgress(int uploaded, int total), - TResult success(), - TResult failed(String error), - @required TResult orElse(), + TResult maybeWhen({ + TResult Function()? preparing, + TResult Function(int uploaded, int total)? inProgress, + TResult Function()? success, + TResult Function(String error)? failed, + required TResult orElse(), }) { - assert(orElse != null); if (inProgress != null) { return inProgress(uploaded, total); } @@ -329,29 +322,24 @@ class _$InProgress implements InProgress { @override @optionalTypeArgs - TResult map({ - @required TResult preparing(Preparing value), - @required TResult inProgress(InProgress value), - @required TResult success(Success value), - @required TResult failed(Failed value), + TResult map({ + required TResult Function(Preparing value) preparing, + required TResult Function(InProgress value) inProgress, + required TResult Function(Success value) success, + required TResult Function(Failed value) failed, }) { - assert(preparing != null); - assert(inProgress != null); - assert(success != null); - assert(failed != null); return inProgress(this); } @override @optionalTypeArgs - TResult maybeMap({ - TResult preparing(Preparing value), - TResult inProgress(InProgress value), - TResult success(Success value), - TResult failed(Failed value), - @required TResult orElse(), + TResult maybeMap({ + TResult Function(Preparing value)? preparing, + TResult Function(InProgress value)? inProgress, + TResult Function(Success value)? success, + TResult Function(Failed value)? failed, + required TResult orElse(), }) { - assert(orElse != null); if (inProgress != null) { return inProgress(this); } @@ -365,15 +353,17 @@ class _$InProgress implements InProgress { } abstract class InProgress implements UploadState { - const factory InProgress({int uploaded, int total}) = _$InProgress; + const factory InProgress({required int uploaded, required int total}) = + _$InProgress; factory InProgress.fromJson(Map json) = _$InProgress.fromJson; - int get uploaded; - int get total; + int get uploaded => throw _privateConstructorUsedError; + int get total => throw _privateConstructorUsedError; @JsonKey(ignore: true) - $InProgressCopyWith get copyWith; + $InProgressCopyWith get copyWith => + throw _privateConstructorUsedError; } /// @nodoc @@ -416,29 +406,24 @@ class _$Success implements Success { @override @optionalTypeArgs - TResult when({ - @required TResult preparing(), - @required TResult inProgress(int uploaded, int total), - @required TResult success(), - @required TResult failed(String error), + TResult when({ + required TResult Function() preparing, + required TResult Function(int uploaded, int total) inProgress, + required TResult Function() success, + required TResult Function(String error) failed, }) { - assert(preparing != null); - assert(inProgress != null); - assert(success != null); - assert(failed != null); return success(); } @override @optionalTypeArgs - TResult maybeWhen({ - TResult preparing(), - TResult inProgress(int uploaded, int total), - TResult success(), - TResult failed(String error), - @required TResult orElse(), + TResult maybeWhen({ + TResult Function()? preparing, + TResult Function(int uploaded, int total)? inProgress, + TResult Function()? success, + TResult Function(String error)? failed, + required TResult orElse(), }) { - assert(orElse != null); if (success != null) { return success(); } @@ -447,29 +432,24 @@ class _$Success implements Success { @override @optionalTypeArgs - TResult map({ - @required TResult preparing(Preparing value), - @required TResult inProgress(InProgress value), - @required TResult success(Success value), - @required TResult failed(Failed value), + TResult map({ + required TResult Function(Preparing value) preparing, + required TResult Function(InProgress value) inProgress, + required TResult Function(Success value) success, + required TResult Function(Failed value) failed, }) { - assert(preparing != null); - assert(inProgress != null); - assert(success != null); - assert(failed != null); return success(this); } @override @optionalTypeArgs - TResult maybeMap({ - TResult preparing(Preparing value), - TResult inProgress(InProgress value), - TResult success(Success value), - TResult failed(Failed value), - @required TResult orElse(), + TResult maybeMap({ + TResult Function(Preparing value)? preparing, + TResult Function(InProgress value)? inProgress, + TResult Function(Success value)? success, + TResult Function(Failed value)? failed, + required TResult orElse(), }) { - assert(orElse != null); if (success != null) { return success(this); } @@ -506,10 +486,13 @@ class _$FailedCopyWithImpl<$Res> extends _$UploadStateCopyWithImpl<$Res> @override $Res call({ - Object error = freezed, + Object? error = freezed, }) { return _then(Failed( - error: error == freezed ? _value.error : error as String, + error: error == freezed + ? _value.error + : error // ignore: cast_nullable_to_non_nullable + as String, )); } } @@ -518,7 +501,7 @@ class _$FailedCopyWithImpl<$Res> extends _$UploadStateCopyWithImpl<$Res> /// @nodoc class _$Failed implements Failed { - const _$Failed({@required this.error}) : assert(error != null); + const _$Failed({required this.error}); factory _$Failed.fromJson(Map json) => _$_$FailedFromJson(json); @@ -550,29 +533,24 @@ class _$Failed implements Failed { @override @optionalTypeArgs - TResult when({ - @required TResult preparing(), - @required TResult inProgress(int uploaded, int total), - @required TResult success(), - @required TResult failed(String error), + TResult when({ + required TResult Function() preparing, + required TResult Function(int uploaded, int total) inProgress, + required TResult Function() success, + required TResult Function(String error) failed, }) { - assert(preparing != null); - assert(inProgress != null); - assert(success != null); - assert(failed != null); return failed(error); } @override @optionalTypeArgs - TResult maybeWhen({ - TResult preparing(), - TResult inProgress(int uploaded, int total), - TResult success(), - TResult failed(String error), - @required TResult orElse(), + TResult maybeWhen({ + TResult Function()? preparing, + TResult Function(int uploaded, int total)? inProgress, + TResult Function()? success, + TResult Function(String error)? failed, + required TResult orElse(), }) { - assert(orElse != null); if (failed != null) { return failed(error); } @@ -581,29 +559,24 @@ class _$Failed implements Failed { @override @optionalTypeArgs - TResult map({ - @required TResult preparing(Preparing value), - @required TResult inProgress(InProgress value), - @required TResult success(Success value), - @required TResult failed(Failed value), + TResult map({ + required TResult Function(Preparing value) preparing, + required TResult Function(InProgress value) inProgress, + required TResult Function(Success value) success, + required TResult Function(Failed value) failed, }) { - assert(preparing != null); - assert(inProgress != null); - assert(success != null); - assert(failed != null); return failed(this); } @override @optionalTypeArgs - TResult maybeMap({ - TResult preparing(Preparing value), - TResult inProgress(InProgress value), - TResult success(Success value), - TResult failed(Failed value), - @required TResult orElse(), + TResult maybeMap({ + TResult Function(Preparing value)? preparing, + TResult Function(InProgress value)? inProgress, + TResult Function(Success value)? success, + TResult Function(Failed value)? failed, + required TResult orElse(), }) { - assert(orElse != null); if (failed != null) { return failed(this); } @@ -617,11 +590,11 @@ class _$Failed implements Failed { } abstract class Failed implements UploadState { - const factory Failed({@required String error}) = _$Failed; + const factory Failed({required String error}) = _$Failed; factory Failed.fromJson(Map json) = _$Failed.fromJson; - String get error; + String get error => throw _privateConstructorUsedError; @JsonKey(ignore: true) - $FailedCopyWith get copyWith; + $FailedCopyWith get copyWith => throw _privateConstructorUsedError; } diff --git a/packages/stream_chat/lib/src/models/attachment_file.g.dart b/packages/stream_chat/lib/src/models/attachment_file.g.dart index c6716fba..55fcea6c 100644 --- a/packages/stream_chat/lib/src/models/attachment_file.g.dart +++ b/packages/stream_chat/lib/src/models/attachment_file.g.dart @@ -6,12 +6,12 @@ part of 'attachment_file.dart'; // JsonSerializableGenerator // ************************************************************************** -AttachmentFile _$AttachmentFileFromJson(Map json) { +AttachmentFile _$AttachmentFileFromJson(Map json) { return AttachmentFile( - path: json['path'] as String, - name: json['name'] as String, - bytes: _fromString(json['bytes'] as String), - size: json['size'] as int, + path: json['path'] as String?, + name: json['name'] as String?, + bytes: _fromString(json['bytes'] as String?), + size: json['size'] as int?, ); } @@ -23,14 +23,14 @@ Map _$AttachmentFileToJson(AttachmentFile instance) => 'size': instance.size, }; -_$Preparing _$_$PreparingFromJson(Map json) { +_$Preparing _$_$PreparingFromJson(Map json) { return _$Preparing(); } Map _$_$PreparingToJson(_$Preparing instance) => {}; -_$InProgress _$_$InProgressFromJson(Map json) { +_$InProgress _$_$InProgressFromJson(Map json) { return _$InProgress( uploaded: json['uploaded'] as int, total: json['total'] as int, @@ -43,14 +43,14 @@ Map _$_$InProgressToJson(_$InProgress instance) => 'total': instance.total, }; -_$Success _$_$SuccessFromJson(Map json) { +_$Success _$_$SuccessFromJson(Map json) { return _$Success(); } Map _$_$SuccessToJson(_$Success instance) => {}; -_$Failed _$_$FailedFromJson(Map json) { +_$Failed _$_$FailedFromJson(Map json) { return _$Failed( error: json['error'] as String, ); diff --git a/packages/stream_chat/lib/src/models/channel_config.dart b/packages/stream_chat/lib/src/models/channel_config.dart index 3541573e..9ba36e4f 100644 --- a/packages/stream_chat/lib/src/models/channel_config.dart +++ b/packages/stream_chat/lib/src/models/channel_config.dart @@ -1,5 +1,6 @@ import 'package:json_annotation/json_annotation.dart'; import 'package:stream_chat/src/models/command.dart'; + part 'channel_config.g.dart'; /// The class that contains the information about the configuration of a channel @@ -7,35 +8,38 @@ part 'channel_config.g.dart'; class ChannelConfig { /// Constructor used for json serialization ChannelConfig({ - this.automod, - this.commands, - this.connectEvents, - this.createdAt, - this.updatedAt, - this.maxMessageLength, - this.messageRetention, - this.mutes, - this.name, - this.reactions, - this.readEvents, - this.replies, - this.search, - this.typingEvents, - this.uploads, - this.urlEnrichment, - }); + this.automod = 'flag', + this.commands = const [], + this.connectEvents = false, + DateTime? createdAt, + DateTime? updatedAt, + this.maxMessageLength = 0, + this.messageRetention = '', + this.mutes = false, + this.reactions = false, + this.readEvents = false, + this.replies = false, + this.search = false, + this.typingEvents = false, + this.uploads = false, + this.urlEnrichment = false, + }) : createdAt = createdAt ?? DateTime.now(), + updatedAt = updatedAt ?? DateTime.now(); /// Create a new instance from a json factory ChannelConfig.fromJson(Map json) => _$ChannelConfigFromJson(json); /// Moderation configuration + @JsonKey(defaultValue: 'flag') final String automod; /// List of available commands + @JsonKey(defaultValue: []) final List commands; /// True if the channel should send connect events + @JsonKey(defaultValue: false) final bool connectEvents; /// Date of channel creation @@ -45,36 +49,43 @@ class ChannelConfig { final DateTime updatedAt; /// Max channel message length + @JsonKey(defaultValue: 0) final int maxMessageLength; /// Duration of message retention + @JsonKey(defaultValue: '') final String messageRetention; /// True if users can be muted + @JsonKey(defaultValue: false) final bool mutes; - /// Name of the channel - final String name; - /// True if reaction are active for this channel + @JsonKey(defaultValue: false) final bool reactions; /// True if readEvents are active for this channel + @JsonKey(defaultValue: false) final bool readEvents; /// True if reply message are active for this channel + @JsonKey(defaultValue: false) final bool replies; /// True if it's possible to perform a search in this channel + @JsonKey(defaultValue: false) final bool search; /// True if typing events should be sent for this channel + @JsonKey(defaultValue: false) final bool typingEvents; /// True if it's possible to upload files to this channel + @JsonKey(defaultValue: false) final bool uploads; /// True if urls appears as attachments + @JsonKey(defaultValue: false) final bool urlEnrichment; /// Serialize to json diff --git a/packages/stream_chat/lib/src/models/channel_config.g.dart b/packages/stream_chat/lib/src/models/channel_config.g.dart index e8152c80..723281c0 100644 --- a/packages/stream_chat/lib/src/models/channel_config.g.dart +++ b/packages/stream_chat/lib/src/models/channel_config.g.dart @@ -6,48 +6,43 @@ part of 'channel_config.dart'; // JsonSerializableGenerator // ************************************************************************** -ChannelConfig _$ChannelConfigFromJson(Map json) { +ChannelConfig _$ChannelConfigFromJson(Map json) { return ChannelConfig( - automod: json['automod'] as String, - commands: (json['commands'] as List) - ?.map((e) => e == null - ? null - : Command.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList(), - connectEvents: json['connect_events'] as bool, + automod: json['automod'] as String? ?? 'flag', + commands: (json['commands'] as List?) + ?.map((e) => Command.fromJson(e as Map)) + .toList() ?? + [], + connectEvents: json['connect_events'] as bool? ?? false, createdAt: json['created_at'] == null ? null : DateTime.parse(json['created_at'] as String), updatedAt: json['updated_at'] == null ? null : DateTime.parse(json['updated_at'] as String), - maxMessageLength: json['max_message_length'] as int, - messageRetention: json['message_retention'] as String, - mutes: json['mutes'] as bool, - name: json['name'] as String, - reactions: json['reactions'] as bool, - readEvents: json['read_events'] as bool, - replies: json['replies'] as bool, - search: json['search'] as bool, - typingEvents: json['typing_events'] as bool, - uploads: json['uploads'] as bool, - urlEnrichment: json['url_enrichment'] as bool, + maxMessageLength: json['max_message_length'] as int? ?? 0, + messageRetention: json['message_retention'] as String? ?? '', + mutes: json['mutes'] as bool? ?? false, + reactions: json['reactions'] as bool? ?? false, + readEvents: json['read_events'] as bool? ?? false, + replies: json['replies'] as bool? ?? false, + search: json['search'] as bool? ?? false, + typingEvents: json['typing_events'] as bool? ?? false, + uploads: json['uploads'] as bool? ?? false, + urlEnrichment: json['url_enrichment'] as bool? ?? false, ); } Map _$ChannelConfigToJson(ChannelConfig instance) => { 'automod': instance.automod, - 'commands': instance.commands?.map((e) => e?.toJson())?.toList(), + 'commands': instance.commands.map((e) => e.toJson()).toList(), 'connect_events': instance.connectEvents, - 'created_at': instance.createdAt?.toIso8601String(), - 'updated_at': instance.updatedAt?.toIso8601String(), + 'created_at': instance.createdAt.toIso8601String(), + 'updated_at': instance.updatedAt.toIso8601String(), 'max_message_length': instance.maxMessageLength, 'message_retention': instance.messageRetention, 'mutes': instance.mutes, - 'name': instance.name, 'reactions': instance.reactions, 'read_events': instance.readEvents, 'replies': instance.replies, diff --git a/packages/stream_chat/lib/src/models/channel_model.dart b/packages/stream_chat/lib/src/models/channel_model.dart index 62ea1853..98d86539 100644 --- a/packages/stream_chat/lib/src/models/channel_model.dart +++ b/packages/stream_chat/lib/src/models/channel_model.dart @@ -10,20 +10,29 @@ part 'channel_model.g.dart'; class ChannelModel { /// Constructor used for json serialization ChannelModel({ - this.id, - this.type, - this.cid, - this.config, + String? id, + String? type, + String? cid, + ChannelConfig? config, this.createdBy, - this.frozen, + this.frozen = false, this.lastMessageAt, - this.createdAt, - this.updatedAt, + DateTime? createdAt, + DateTime? updatedAt, this.deletedAt, - this.memberCount, - this.extraData, + this.memberCount = 0, + this.extraData = const {}, this.team, - }); + }) : assert( + (cid != null && cid.contains(':')) || (id != null && type != null), + 'provide either a cid or an id and type', + ), + id = id ?? cid!.split(':')[1], + type = type ?? cid!.split(':')[0], + cid = cid ?? '$type:$id', + config = config ?? ChannelConfig(), + createdAt = createdAt ?? DateTime.now(), + updatedAt = updatedAt ?? DateTime.now(); /// Create a new instance from a json factory ChannelModel.fromJson(Map json) => @@ -46,15 +55,15 @@ class ChannelModel { /// The user that created this channel @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) - final User createdBy; + final User? createdBy; /// True if this channel is frozen - @JsonKey(includeIfNull: false) + @JsonKey(includeIfNull: false, defaultValue: false) final bool frozen; /// The date of the last message @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) - final DateTime lastMessageAt; + final DateTime? lastMessageAt; /// The date of channel creation @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) @@ -66,19 +75,23 @@ class ChannelModel { /// The date of channel deletion @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) - final DateTime deletedAt; + final DateTime? deletedAt; /// The count of this channel members - @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) + @JsonKey( + includeIfNull: false, toJson: Serialization.readOnly, defaultValue: 0) final int memberCount; /// Map of custom channel extraData - @JsonKey(includeIfNull: false) - final Map extraData; + @JsonKey( + includeIfNull: false, + defaultValue: {}, + ) + final Map extraData; /// The team the channel belongs to @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) - final String team; + final String? team; /// Known top level fields. /// Useful for [Serialization] methods. @@ -99,29 +112,28 @@ class ChannelModel { /// Shortcut for channel name String get name => - extraData?.containsKey('name') == true ? extraData['name'] : cid; + extraData.containsKey('name') ? extraData['name'] as String : cid; /// Serialize to json Map toJson() => Serialization.moveFromExtraDataToRoot( _$ChannelModelToJson(this), - topLevelFields, ); /// Creates a copy of [ChannelModel] with specified attributes overridden. ChannelModel copyWith({ - String id, - String type, - String cid, - ChannelConfig config, - User createdBy, - bool frozen, - DateTime lastMessageAt, - DateTime createdAt, - DateTime updatedAt, - DateTime deletedAt, - int memberCount, - Map extraData, - String team, + String? id, + String? type, + String? cid, + ChannelConfig? config, + User? createdBy, + bool? frozen, + DateTime? lastMessageAt, + DateTime? createdAt, + DateTime? updatedAt, + DateTime? deletedAt, + int? memberCount, + Map? extraData, + String? team, }) => ChannelModel( id: id ?? this.id, @@ -141,7 +153,7 @@ class ChannelModel { /// Returns a new [ChannelModel] that is a combination of this channelModel /// and the given [other] channelModel. - ChannelModel merge(ChannelModel other) { + ChannelModel merge(ChannelModel? other) { if (other == null) return this; return copyWith( id: other.id, diff --git a/packages/stream_chat/lib/src/models/channel_model.g.dart b/packages/stream_chat/lib/src/models/channel_model.g.dart index 4f535f46..78e93910 100644 --- a/packages/stream_chat/lib/src/models/channel_model.g.dart +++ b/packages/stream_chat/lib/src/models/channel_model.g.dart @@ -6,22 +6,18 @@ part of 'channel_model.dart'; // JsonSerializableGenerator // ************************************************************************** -ChannelModel _$ChannelModelFromJson(Map json) { +ChannelModel _$ChannelModelFromJson(Map json) { return ChannelModel( - id: json['id'] as String, - type: json['type'] as String, - cid: json['cid'] as String, + id: json['id'] as String?, + type: json['type'] as String?, + cid: json['cid'] as String?, config: json['config'] == null ? null - : ChannelConfig.fromJson((json['config'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )), + : ChannelConfig.fromJson(json['config'] as Map), createdBy: json['created_by'] == null ? null - : User.fromJson((json['created_by'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )), - frozen: json['frozen'] as bool, + : User.fromJson(json['created_by'] as Map), + frozen: json['frozen'] as bool? ?? false, lastMessageAt: json['last_message_at'] == null ? null : DateTime.parse(json['last_message_at'] as String), @@ -34,11 +30,12 @@ ChannelModel _$ChannelModelFromJson(Map json) { deletedAt: json['deleted_at'] == null ? null : DateTime.parse(json['deleted_at'] as String), - memberCount: json['member_count'] as int, - extraData: (json['extra_data'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - ), - team: json['team'] as String, + memberCount: json['member_count'] as int? ?? 0, + extraData: (json['extra_data'] as Map?)?.map( + (k, e) => MapEntry(k, e as Object), + ) ?? + {}, + team: json['team'] as String?, ); } @@ -57,13 +54,13 @@ Map _$ChannelModelToJson(ChannelModel instance) { writeNotNull('cid', readonly(instance.cid)); writeNotNull('config', readonly(instance.config)); writeNotNull('created_by', readonly(instance.createdBy)); - writeNotNull('frozen', instance.frozen); + val['frozen'] = instance.frozen; writeNotNull('last_message_at', readonly(instance.lastMessageAt)); writeNotNull('created_at', readonly(instance.createdAt)); writeNotNull('updated_at', readonly(instance.updatedAt)); writeNotNull('deleted_at', readonly(instance.deletedAt)); writeNotNull('member_count', readonly(instance.memberCount)); - writeNotNull('extra_data', instance.extraData); + val['extra_data'] = instance.extraData; writeNotNull('team', readonly(instance.team)); return val; } diff --git a/packages/stream_chat/lib/src/models/channel_state.dart b/packages/stream_chat/lib/src/models/channel_state.dart index 3a1f3178..9d18f434 100644 --- a/packages/stream_chat/lib/src/models/channel_state.dart +++ b/packages/stream_chat/lib/src/models/channel_state.dart @@ -22,24 +22,29 @@ class ChannelState { }); /// The channel to which this state belongs - final ChannelModel channel; + final ChannelModel? channel; /// A paginated list of channel messages + @JsonKey(defaultValue: []) final List messages; /// A paginated list of channel members + @JsonKey(defaultValue: []) final List members; /// A paginated list of pinned messages + @JsonKey(defaultValue: []) final List pinnedMessages; /// The count of users watching the channel - final int watcherCount; + final int? watcherCount; /// A paginated list of users watching the channel + @JsonKey(defaultValue: []) final List watchers; /// The list of channel reads + @JsonKey(defaultValue: []) final List read; /// Create a new instance from a json @@ -51,13 +56,13 @@ class ChannelState { /// Creates a copy of [ChannelState] with specified attributes overridden. ChannelState copyWith({ - ChannelModel channel, - List messages, - List members, - List pinnedMessages, - int watcherCount, - List watchers, - List read, + ChannelModel? channel, + List? messages, + List? members, + List? pinnedMessages, + int? watcherCount, + List? watchers, + List? read, }) => ChannelState( channel: channel ?? this.channel, diff --git a/packages/stream_chat/lib/src/models/channel_state.g.dart b/packages/stream_chat/lib/src/models/channel_state.g.dart index a66899a4..39c6373f 100644 --- a/packages/stream_chat/lib/src/models/channel_state.g.dart +++ b/packages/stream_chat/lib/src/models/channel_state.g.dart @@ -6,60 +6,43 @@ part of 'channel_state.dart'; // JsonSerializableGenerator // ************************************************************************** -ChannelState _$ChannelStateFromJson(Map json) { +ChannelState _$ChannelStateFromJson(Map json) { return ChannelState( channel: json['channel'] == null ? null - : ChannelModel.fromJson((json['channel'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )), - messages: (json['messages'] as List) - ?.map((e) => e == null - ? null - : Message.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList(), - members: (json['members'] as List) - ?.map((e) => e == null - ? null - : Member.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList(), - pinnedMessages: (json['pinned_messages'] as List) - ?.map((e) => e == null - ? null - : Message.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList(), - watcherCount: json['watcher_count'] as int, - watchers: (json['watchers'] as List) - ?.map((e) => e == null - ? null - : User.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList(), - read: (json['read'] as List) - ?.map((e) => e == null - ? null - : Read.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList(), + : ChannelModel.fromJson(json['channel'] as Map), + messages: (json['messages'] as List?) + ?.map((e) => Message.fromJson(e as Map)) + .toList() ?? + [], + members: (json['members'] as List?) + ?.map((e) => Member.fromJson(e as Map)) + .toList() ?? + [], + pinnedMessages: (json['pinned_messages'] as List?) + ?.map((e) => Message.fromJson(e as Map)) + .toList() ?? + [], + watcherCount: json['watcher_count'] as int?, + watchers: (json['watchers'] as List?) + ?.map((e) => User.fromJson(e as Map)) + .toList() ?? + [], + read: (json['read'] as List?) + ?.map((e) => Read.fromJson(e as Map)) + .toList() ?? + [], ); } Map _$ChannelStateToJson(ChannelState instance) => { 'channel': instance.channel?.toJson(), - 'messages': instance.messages?.map((e) => e?.toJson())?.toList(), - 'members': instance.members?.map((e) => e?.toJson())?.toList(), + 'messages': instance.messages.map((e) => e.toJson()).toList(), + 'members': instance.members.map((e) => e.toJson()).toList(), 'pinned_messages': - instance.pinnedMessages?.map((e) => e?.toJson())?.toList(), + instance.pinnedMessages.map((e) => e.toJson()).toList(), 'watcher_count': instance.watcherCount, - 'watchers': instance.watchers?.map((e) => e?.toJson())?.toList(), - 'read': instance.read?.map((e) => e?.toJson())?.toList(), + 'watchers': instance.watchers.map((e) => e.toJson()).toList(), + 'read': instance.read.map((e) => e.toJson()).toList(), }; diff --git a/packages/stream_chat/lib/src/models/command.dart b/packages/stream_chat/lib/src/models/command.dart index a5ababd2..5ba0043c 100644 --- a/packages/stream_chat/lib/src/models/command.dart +++ b/packages/stream_chat/lib/src/models/command.dart @@ -7,9 +7,9 @@ part 'command.g.dart'; class Command { /// Constructor used for json serialization Command({ - this.name, - this.description, - this.args, + required this.name, + required this.description, + required this.args, }); /// Create a new instance from a json diff --git a/packages/stream_chat/lib/src/models/command.g.dart b/packages/stream_chat/lib/src/models/command.g.dart index f32e8e8a..cf8be971 100644 --- a/packages/stream_chat/lib/src/models/command.g.dart +++ b/packages/stream_chat/lib/src/models/command.g.dart @@ -6,7 +6,7 @@ part of 'command.dart'; // JsonSerializableGenerator // ************************************************************************** -Command _$CommandFromJson(Map json) { +Command _$CommandFromJson(Map json) { return Command( name: json['name'] as String, description: json['description'] as String, diff --git a/packages/stream_chat/lib/src/models/device.dart b/packages/stream_chat/lib/src/models/device.dart index 150e6759..5dc98d25 100644 --- a/packages/stream_chat/lib/src/models/device.dart +++ b/packages/stream_chat/lib/src/models/device.dart @@ -7,8 +7,8 @@ part 'device.g.dart'; class Device { /// Constructor used for json serialization Device({ - this.id, - this.pushProvider, + required this.id, + required this.pushProvider, }); /// Create a new instance from a json diff --git a/packages/stream_chat/lib/src/models/device.g.dart b/packages/stream_chat/lib/src/models/device.g.dart index bac60856..5fcd9435 100644 --- a/packages/stream_chat/lib/src/models/device.g.dart +++ b/packages/stream_chat/lib/src/models/device.g.dart @@ -6,7 +6,7 @@ part of 'device.dart'; // JsonSerializableGenerator // ************************************************************************** -Device _$DeviceFromJson(Map json) { +Device _$DeviceFromJson(Map json) { return Device( id: json['id'] as String, pushProvider: json['push_provider'] as String, diff --git a/packages/stream_chat/lib/src/models/event.dart b/packages/stream_chat/lib/src/models/event.dart index 28cf1b38..1675197d 100644 --- a/packages/stream_chat/lib/src/models/event.dart +++ b/packages/stream_chat/lib/src/models/event.dart @@ -10,7 +10,7 @@ part 'event.g.dart'; @JsonSerializable() class Event { /// Constructor used for json serialization - Event({ + const Event({ this.type, this.cid, this.connectionId, @@ -27,72 +27,73 @@ class Event { this.channelId, this.channelType, this.parentId, - this.extraData, - }) : isLocal = true; + this.extraData = const {}, + this.isLocal = true, + }); /// Create a new instance from a json factory Event.fromJson(Map json) => _$EventFromJson(Serialization.moveToExtraDataFromRoot( json, topLevelFields, - )) - ..isLocal = false; + )); /// The type of the event /// [EventType] contains some predefined constant types - final String type; + final String? type; /// The channel cid to which the event belongs - final String cid; + final String? cid; /// The channel id to which the event belongs - final String channelId; + final String? channelId; /// The channel type to which the event belongs - final String channelType; + final String? channelType; /// The connection id in which the event has been sent - final String connectionId; + final String? connectionId; /// The date of creation of the event - final DateTime createdAt; + final DateTime? createdAt; /// User object of the health check user - final OwnUser me; + final OwnUser? me; /// User object of the current user - final User user; + final User? user; /// The message sent with the event - final Message message; + final Message? message; /// The channel sent with the event - final EventChannel channel; + final EventChannel? channel; /// The member sent with the event - final Member member; + final Member? member; /// The reaction sent with the event - final Reaction reaction; + final Reaction? reaction; /// The number of unread messages for current user - final int totalUnreadCount; + final int? totalUnreadCount; /// User total unread channels - final int unreadChannels; + final int? unreadChannels; /// Online status - final bool online; + final bool? online; /// The id of the parent message of a thread - final String parentId; + final String? parentId; /// True if the event is generated by this client - bool isLocal; + @JsonKey(defaultValue: false) + final bool isLocal; /// Map of custom channel extraData - @JsonKey(includeIfNull: false) - final Map extraData; + @JsonKey(defaultValue: {}) + final Map extraData; /// Known top level fields. /// Useful for [Serialization] methods. @@ -119,28 +120,27 @@ class Event { /// Serialize to json Map toJson() => Serialization.moveFromExtraDataToRoot( _$EventToJson(this), - topLevelFields, ); /// Creates a copy of [Event] with specified attributes overridden. Event copyWith({ - String type, - String cid, - String channelId, - String channelType, - String connectionId, - DateTime createdAt, - OwnUser me, - User user, - Message message, - EventChannel channel, - Member member, - Reaction reaction, - int totalUnreadCount, - int unreadChannels, - bool online, - String parentId, - Map extraData, + String? type, + String? cid, + String? channelId, + String? channelType, + String? connectionId, + DateTime? createdAt, + OwnUser? me, + User? user, + Message? message, + EventChannel? channel, + Member? member, + Reaction? reaction, + int? totalUnreadCount, + int? unreadChannels, + bool? online, + String? parentId, + Map? extraData, }) => Event( type: type ?? this.type, @@ -169,18 +169,18 @@ class EventChannel extends ChannelModel { /// Constructor used for json serialization EventChannel({ this.members, - String id, - String type, - String cid, - ChannelConfig config, - User createdBy, - bool frozen, - DateTime lastMessageAt, - DateTime createdAt, - DateTime updatedAt, - DateTime deletedAt, - int memberCount, - Map extraData, + String? id, + String? type, + required String cid, + required ChannelConfig config, + User? createdBy, + bool frozen = false, + DateTime? lastMessageAt, + required DateTime createdAt, + required DateTime updatedAt, + DateTime? deletedAt, + required int memberCount, + Map? extraData, }) : super( id: id, type: type, @@ -193,7 +193,7 @@ class EventChannel extends ChannelModel { updatedAt: updatedAt, deletedAt: deletedAt, memberCount: memberCount, - extraData: extraData, + extraData: extraData ?? {}, ); /// Create a new instance from a json @@ -204,7 +204,7 @@ class EventChannel extends ChannelModel { )); /// A paginated list of channel members - final List members; + final List? members; /// Known top level fields. /// Useful for [Serialization] methods. @@ -217,6 +217,5 @@ class EventChannel extends ChannelModel { @override Map toJson() => Serialization.moveFromExtraDataToRoot( _$EventChannelToJson(this), - topLevelFields, ); } diff --git a/packages/stream_chat/lib/src/models/event.g.dart b/packages/stream_chat/lib/src/models/event.g.dart index aaef8181..b76c675c 100644 --- a/packages/stream_chat/lib/src/models/event.g.dart +++ b/packages/stream_chat/lib/src/models/event.g.dart @@ -6,126 +6,93 @@ part of 'event.dart'; // JsonSerializableGenerator // ************************************************************************** -Event _$EventFromJson(Map json) { +Event _$EventFromJson(Map json) { return Event( - type: json['type'] as String, - cid: json['cid'] as String, - connectionId: json['connection_id'] as String, + type: json['type'] as String?, + cid: json['cid'] as String?, + connectionId: json['connection_id'] as String?, createdAt: json['created_at'] == null ? null : DateTime.parse(json['created_at'] as String), me: json['me'] == null ? null - : OwnUser.fromJson((json['me'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )), + : OwnUser.fromJson(json['me'] as Map), user: json['user'] == null ? null - : User.fromJson((json['user'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )), + : User.fromJson(json['user'] as Map), message: json['message'] == null ? null - : Message.fromJson((json['message'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )), - totalUnreadCount: json['total_unread_count'] as int, - unreadChannels: json['unread_channels'] as int, + : Message.fromJson(json['message'] as Map), + totalUnreadCount: json['total_unread_count'] as int?, + unreadChannels: json['unread_channels'] as int?, reaction: json['reaction'] == null ? null - : Reaction.fromJson((json['reaction'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )), - online: json['online'] as bool, + : Reaction.fromJson(json['reaction'] as Map), + online: json['online'] as bool?, channel: json['channel'] == null ? null - : EventChannel.fromJson((json['channel'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )), + : EventChannel.fromJson(json['channel'] as Map), member: json['member'] == null ? null - : Member.fromJson((json['member'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )), - channelId: json['channel_id'] as String, - channelType: json['channel_type'] as String, - parentId: json['parent_id'] as String, - extraData: (json['extra_data'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - ), - )..isLocal = json['is_local'] as bool; + : Member.fromJson(json['member'] as Map), + channelId: json['channel_id'] as String?, + channelType: json['channel_type'] as String?, + parentId: json['parent_id'] as String?, + extraData: (json['extra_data'] as Map?)?.map( + (k, e) => MapEntry(k, e as Object), + ) ?? + {}, + isLocal: json['is_local'] as bool? ?? false, + ); } -Map _$EventToJson(Event instance) { - final val = { - 'type': instance.type, - 'cid': instance.cid, - 'channel_id': instance.channelId, - 'channel_type': instance.channelType, - 'connection_id': instance.connectionId, - 'created_at': instance.createdAt?.toIso8601String(), - 'me': instance.me?.toJson(), - 'user': instance.user?.toJson(), - 'message': instance.message?.toJson(), - 'channel': instance.channel?.toJson(), - 'member': instance.member?.toJson(), - 'reaction': instance.reaction?.toJson(), - 'total_unread_count': instance.totalUnreadCount, - 'unread_channels': instance.unreadChannels, - 'online': instance.online, - 'parent_id': instance.parentId, - 'is_local': instance.isLocal, - }; +Map _$EventToJson(Event instance) => { + 'type': instance.type, + 'cid': instance.cid, + 'channel_id': instance.channelId, + 'channel_type': instance.channelType, + 'connection_id': instance.connectionId, + 'created_at': instance.createdAt?.toIso8601String(), + 'me': instance.me?.toJson(), + 'user': instance.user?.toJson(), + 'message': instance.message?.toJson(), + 'channel': instance.channel?.toJson(), + 'member': instance.member?.toJson(), + 'reaction': instance.reaction?.toJson(), + 'total_unread_count': instance.totalUnreadCount, + 'unread_channels': instance.unreadChannels, + 'online': instance.online, + 'parent_id': instance.parentId, + 'is_local': instance.isLocal, + 'extra_data': instance.extraData, + }; - void writeNotNull(String key, dynamic value) { - if (value != null) { - val[key] = value; - } - } - - writeNotNull('extra_data', instance.extraData); - return val; -} - -EventChannel _$EventChannelFromJson(Map json) { +EventChannel _$EventChannelFromJson(Map json) { return EventChannel( - members: (json['members'] as List) - ?.map((e) => e == null - ? null - : Member.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList(), - id: json['id'] as String, - type: json['type'] as String, + members: (json['members'] as List?) + ?.map((e) => Member.fromJson(e as Map)) + .toList(), + id: json['id'] as String?, + type: json['type'] as String?, cid: json['cid'] as String, - config: json['config'] == null - ? null - : ChannelConfig.fromJson((json['config'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )), + config: ChannelConfig.fromJson(json['config'] as Map), createdBy: json['created_by'] == null ? null - : User.fromJson((json['created_by'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )), - frozen: json['frozen'] as bool, + : User.fromJson(json['created_by'] as Map), + frozen: json['frozen'] as bool? ?? false, lastMessageAt: json['last_message_at'] == null ? null : DateTime.parse(json['last_message_at'] as String), - createdAt: json['created_at'] == null - ? null - : DateTime.parse(json['created_at'] as String), - updatedAt: json['updated_at'] == null - ? null - : DateTime.parse(json['updated_at'] as String), + createdAt: DateTime.parse(json['created_at'] as String), + updatedAt: DateTime.parse(json['updated_at'] as String), deletedAt: json['deleted_at'] == null ? null : DateTime.parse(json['deleted_at'] as String), - memberCount: json['member_count'] as int, - extraData: (json['extra_data'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - ), + memberCount: json['member_count'] as int? ?? 0, + extraData: (json['extra_data'] as Map?)?.map( + (k, e) => MapEntry(k, e as Object), + ) ?? + {}, ); } @@ -144,13 +111,13 @@ Map _$EventChannelToJson(EventChannel instance) { writeNotNull('cid', readonly(instance.cid)); writeNotNull('config', readonly(instance.config)); writeNotNull('created_by', readonly(instance.createdBy)); - writeNotNull('frozen', instance.frozen); + val['frozen'] = instance.frozen; writeNotNull('last_message_at', readonly(instance.lastMessageAt)); writeNotNull('created_at', readonly(instance.createdAt)); writeNotNull('updated_at', readonly(instance.updatedAt)); writeNotNull('deleted_at', readonly(instance.deletedAt)); writeNotNull('member_count', readonly(instance.memberCount)); - writeNotNull('extra_data', instance.extraData); - val['members'] = instance.members?.map((e) => e?.toJson())?.toList(); + val['extra_data'] = instance.extraData; + val['members'] = instance.members?.map((e) => e.toJson()).toList(); return val; } diff --git a/packages/stream_chat/lib/src/models/filter.dart b/packages/stream_chat/lib/src/models/filter.dart new file mode 100644 index 00000000..2356c57b --- /dev/null +++ b/packages/stream_chat/lib/src/models/filter.dart @@ -0,0 +1,208 @@ +// ignore_for_file: non_constant_identifier_names, constant_identifier_names + +import 'package:equatable/equatable.dart'; + +const _groupOperators = [ + FilterOperator.and, + FilterOperator.or, + FilterOperator.nor, +]; + +/// Possible operators to use in filters. +enum FilterOperator { + /// Matches values that are equal to a specified value. + equal, + + /// Matches all values that are not equal to a specified value. + notEqual, + + /// Matches values that are greater than a specified value. + greater, + + /// Matches values that are greater than a specified value. + greaterOrEqual, + + /// Matches values that are less than a specified value. + less, + + /// Matches values that are less than or equal to a specified value. + lessOrEqual, + + /// Matches any of the values specified in an array. + in_, + + /// Matches none of the values specified in an array. + notIn, + + /// Matches values by performing text search with the specified value. + query, + + /// Matches values with the specified prefix. + autoComplete, + + /// Matches values that exist/don't exist based on the specified boolean value. + exists, + + /// Matches all the values specified in an array. + and, + + /// Matches at least one of the values specified in an array. + or, + + /// Matches none of the values specified in an array. + nor, +} + +/// Helper extension for [FilterOperator] +extension FilterOperatorX on FilterOperator { + /// Converts [FilterOperator] into rew values + String get rawValue => { + FilterOperator.equal: '\$eq', + FilterOperator.notEqual: '\$ne', + FilterOperator.greater: '\$gt', + FilterOperator.greaterOrEqual: '\$gte', + FilterOperator.less: '\$lt', + FilterOperator.lessOrEqual: '\$lte', + FilterOperator.in_: '\$in', + FilterOperator.notIn: '\$nin', + FilterOperator.query: '\$q', + FilterOperator.autoComplete: '\$autocomplete', + FilterOperator.exists: '\$exists', + FilterOperator.and: '\$and', + FilterOperator.or: '\$or', + FilterOperator.nor: '\$nor', + }[this]!; +} + +/// Stream supports a limited set of filters for querying channels, +/// users and members. The example below shows how to filter for channels +/// of type messaging where the current user is a member +/// +/// ```dart +/// final filter = Filter.and( +/// Filter.equal('type', 'messaging'), +/// Filter.in_('members', [user.id]) +/// ) +/// ``` +/// See Query Channels Documentation +class Filter extends Equatable { + const Filter.__({ + required this.operator, + required this.value, + this.key, + }); + + Filter._({ + required FilterOperator operator, + required this.value, + this.key, + }) : operator = operator.rawValue; + + /// Combines the provided filters and matches the values + /// matched by all filters. + factory Filter.and(List filters) => + Filter._(operator: FilterOperator.and, value: filters); + + /// Combines the provided filters and matches the values + /// matched by at least one of the filters. + factory Filter.or(List filters) => + Filter._(operator: FilterOperator.or, value: filters); + + /// Combines the provided filters and matches the values + /// not matched by all the filters. + factory Filter.nor(List filters) => + Filter._(operator: FilterOperator.nor, value: filters); + + /// Matches values that are equal to a specified value. + factory Filter.equal(String key, Object value) => + Filter._(operator: FilterOperator.equal, key: key, value: value); + + /// Matches all values that are not equal to a specified value. + factory Filter.notEqual(String key, Object value) => + Filter._(operator: FilterOperator.notEqual, key: key, value: value); + + /// Matches values that are greater than a specified value. + factory Filter.greater(String key, Object value) => + Filter._(operator: FilterOperator.greater, key: key, value: value); + + /// Matches values that are greater than a specified value. + factory Filter.greaterOrEqual(String key, Object value) => + Filter._(operator: FilterOperator.greaterOrEqual, key: key, value: value); + + /// Matches values that are less than a specified value. + factory Filter.less(String key, Object value) => + Filter._(operator: FilterOperator.less, key: key, value: value); + + /// Matches values that are less than or equal to a specified value. + factory Filter.lessOrEqual(String key, Object value) => + Filter._(operator: FilterOperator.lessOrEqual, key: key, value: value); + + /// Matches any of the values specified in an array. + factory Filter.in_(String key, List values) => + Filter._(operator: FilterOperator.in_, key: key, value: values); + + /// Matches none of the values specified in an array. + factory Filter.notIn(String key, List values) => + Filter._(operator: FilterOperator.notIn, key: key, value: values); + + /// Matches values by performing text search with the specified value. + factory Filter.query(String key, String text) => + Filter._(operator: FilterOperator.query, key: key, value: text); + + /// Matches values with the specified prefix. + factory Filter.autoComplete(String key, String text) => + Filter._(operator: FilterOperator.autoComplete, key: key, value: text); + + /// Matches values that exist/don't exist based on the specified boolean value. + factory Filter.exists(String key, {bool exists = true}) => + Filter._(operator: FilterOperator.exists, key: key, value: exists); + + /// Creates a custom [Filter] if there isn't one already available. + const factory Filter.custom({ + required String operator, + required Object value, + String? key, + }) = Filter.__; + + /// An operator used for the filter. The operator string must start with `$` + final String operator; + + /// The "left-hand" side of the filter. + /// Specifies the name of the field the filter should match. + /// + /// Some operators like `and` or `or`, + /// don't require the key value to be present. + /// see-more : [_groupOperators] + final String? key; + + /// The "right-hand" side of the filter. + /// Specifies the [value] the filter should match. + final Object /*List|List|String*/ value; + + @override + List get props => [operator, key, value]; + + /// Serializes to json object + Map toJson() { + final json = {}; + final groupOperators = _groupOperators.map((it) => it.rawValue); + + assert( + groupOperators.contains(operator) || key != null, + 'Filter must contain the `key` when the operator is not a ' + 'group operator.', + ); + + if (groupOperators.contains(operator)) { + // Filters with group operators are encoded in the following form: + // { $: [ , ] } + json[operator] = value; + } else { + // Normal filters are encoded in the following form: + // { key: { $: } } + json[key!] = {operator: value}; + } + + return json; + } +} diff --git a/packages/stream_chat/lib/src/models/member.dart b/packages/stream_chat/lib/src/models/member.dart index e99d8d34..49df170c 100644 --- a/packages/stream_chat/lib/src/models/member.dart +++ b/packages/stream_chat/lib/src/models/member.dart @@ -12,15 +12,16 @@ class Member { this.user, this.inviteAcceptedAt, this.inviteRejectedAt, - this.invited, + this.invited = false, this.role, this.userId, - this.isModerator, - this.createdAt, - this.updatedAt, - this.banned, - this.shadowBanned, - }); + this.isModerator = false, + DateTime? createdAt, + DateTime? updatedAt, + this.banned = false, + this.shadowBanned = false, + }) : createdAt = createdAt ?? DateTime.now(), + updatedAt = updatedAt ?? DateTime.now(); /// Create a new instance from a json factory Member.fromJson(Map json) { @@ -31,30 +32,34 @@ class Member { } /// The interested user - final User user; + final User? user; /// The date in which the user accepted the invite to the channel - final DateTime inviteAcceptedAt; + final DateTime? inviteAcceptedAt; /// The date in which the user rejected the invite to the channel - final DateTime inviteRejectedAt; + final DateTime? inviteRejectedAt; /// True if the user has been invited to the channel + @JsonKey(defaultValue: false) final bool invited; /// The role of the user in the channel - final String role; + final String? role; /// The id of the interested user - final String userId; + final String? userId; /// True if the user is a moderator of the channel + @JsonKey(defaultValue: false) final bool isModerator; /// True if the member is banned from the channel + @JsonKey(defaultValue: false) final bool banned; /// True if the member is shadow banned from the channel + @JsonKey(defaultValue: false) final bool shadowBanned; /// The date of creation @@ -65,17 +70,17 @@ class Member { /// Creates a copy of [Member] with specified attributes overridden. Member copyWith({ - User user, - DateTime inviteAcceptedAt, - DateTime inviteRejectedAt, - bool invited, - String role, - String userId, - bool isModerator, - DateTime createdAt, - DateTime updatedAt, - bool banned, - bool shadowBanned, + User? user, + DateTime? inviteAcceptedAt, + DateTime? inviteRejectedAt, + bool? invited, + String? role, + String? userId, + bool? isModerator, + DateTime? createdAt, + DateTime? updatedAt, + bool? banned, + bool? shadowBanned, }) => Member( user: user ?? this.user, diff --git a/packages/stream_chat/lib/src/models/member.g.dart b/packages/stream_chat/lib/src/models/member.g.dart index 3ac8778e..a75b458d 100644 --- a/packages/stream_chat/lib/src/models/member.g.dart +++ b/packages/stream_chat/lib/src/models/member.g.dart @@ -6,31 +6,29 @@ part of 'member.dart'; // JsonSerializableGenerator // ************************************************************************** -Member _$MemberFromJson(Map json) { +Member _$MemberFromJson(Map json) { return Member( user: json['user'] == null ? null - : User.fromJson((json['user'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )), + : User.fromJson(json['user'] as Map), inviteAcceptedAt: json['invite_accepted_at'] == null ? null : DateTime.parse(json['invite_accepted_at'] as String), inviteRejectedAt: json['invite_rejected_at'] == null ? null : DateTime.parse(json['invite_rejected_at'] as String), - invited: json['invited'] as bool, - role: json['role'] as String, - userId: json['user_id'] as String, - isModerator: json['is_moderator'] as bool, + invited: json['invited'] as bool? ?? false, + role: json['role'] as String?, + userId: json['user_id'] as String?, + isModerator: json['is_moderator'] as bool? ?? false, createdAt: json['created_at'] == null ? null : DateTime.parse(json['created_at'] as String), updatedAt: json['updated_at'] == null ? null : DateTime.parse(json['updated_at'] as String), - banned: json['banned'] as bool, - shadowBanned: json['shadow_banned'] as bool, + banned: json['banned'] as bool? ?? false, + shadowBanned: json['shadow_banned'] as bool? ?? false, ); } @@ -44,6 +42,6 @@ Map _$MemberToJson(Member instance) => { 'is_moderator': instance.isModerator, 'banned': instance.banned, 'shadow_banned': instance.shadowBanned, - 'created_at': instance.createdAt?.toIso8601String(), - 'updated_at': instance.updatedAt?.toIso8601String(), + 'created_at': instance.createdAt.toIso8601String(), + 'updated_at': instance.updatedAt.toIso8601String(), }; diff --git a/packages/stream_chat/lib/src/models/message.dart b/packages/stream_chat/lib/src/models/message.dart index 28790b15..cec591c0 100644 --- a/packages/stream_chat/lib/src/models/message.dart +++ b/packages/stream_chat/lib/src/models/message.dart @@ -45,13 +45,13 @@ enum MessageSendingStatus { class Message extends Equatable { /// Constructor used for json serialization Message({ - String id, + String? id, this.text, - this.type, - this.attachments, - this.mentionedUsers, - this.silent, - this.shadowed, + this.type = 'regular', + this.attachments = const [], + this.mentionedUsers = const [], + this.silent = false, + this.shadowed = false, this.reactionCounts, this.reactionScores, this.latestReactions, @@ -63,19 +63,21 @@ class Message extends Equatable { this.threadParticipants, this.showInChannel, this.command, - this.createdAt, - this.updatedAt, + DateTime? createdAt, + DateTime? updatedAt, this.user, this.pinned = false, this.pinnedAt, - DateTime pinExpires, + DateTime? pinExpires, this.pinnedBy, - this.extraData, + this.extraData = const {}, this.deletedAt, this.status = MessageSendingStatus.sent, - this.skipPush, - }) : id = id ?? Uuid().v4(), - pinExpires = pinExpires?.toUtc(); + this.skipPush = false, + }) : id = id ?? const Uuid().v4(), + pinExpires = pinExpires?.toUtc(), + createdAt = createdAt ?? DateTime.now(), + updatedAt = updatedAt ?? DateTime.now(); /// Create a new instance from a json factory Message.fromJson(Map json) => _$MessageFromJson( @@ -86,75 +88,91 @@ class Message extends Equatable { final String id; /// The text of this message - final String text; + final String? text; /// The status of a sending message @JsonKey(ignore: true) final MessageSendingStatus status; /// The message type - @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) + @JsonKey( + includeIfNull: false, + toJson: Serialization.readOnly, + defaultValue: 'regular', + ) final String type; /// The list of attachments, either provided by the user or generated from a /// command or as a result of URL scraping. - @JsonKey(includeIfNull: false) + @JsonKey( + includeIfNull: false, + defaultValue: [], + ) final List attachments; /// The list of user mentioned in the message - @JsonKey(toJson: Serialization.userIds) + @JsonKey( + toJson: Serialization.userIds, + defaultValue: [], + ) final List mentionedUsers; /// A map describing the count of number of every reaction @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) - final Map reactionCounts; + final Map? reactionCounts; /// A map describing the count of score of every reaction @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) - final Map reactionScores; + final Map? reactionScores; /// The latest reactions to the message created by any user. @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) - final List latestReactions; + final List? latestReactions; /// The reactions added to the message by the current user. @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) - final List ownReactions; + final List? ownReactions; /// The ID of the parent message, if the message is a thread reply. - final String parentId; + final String? parentId; /// A quoted reply message @JsonKey(toJson: Serialization.readOnly) - final Message quotedMessage; + final Message? quotedMessage; /// The ID of the quoted message, if the message is a quoted reply. - final String quotedMessageId; + final String? quotedMessageId; /// Reserved field indicating the number of replies for this message. @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) - final int replyCount; + final int? replyCount; /// Reserved field indicating the thread participants for this message. @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) - final List threadParticipants; + final List? threadParticipants; /// Check if this message needs to show in the channel. - final bool showInChannel; + final bool? showInChannel; /// If true the message is silent + @JsonKey(defaultValue: false) final bool silent; /// If true the message will not send a push notification + @JsonKey(defaultValue: false) final bool skipPush; /// If true the message is shadowed - @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) + @JsonKey( + includeIfNull: false, + toJson: Serialization.readOnly, + defaultValue: false, + ) final bool shadowed; /// A used command name. @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) - final String command; + final String? command; /// Reserved field indicating when the message was created. @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) @@ -166,27 +184,31 @@ class Message extends Equatable { /// User who sent the message @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) - final User user; + final User? user; /// If true the message is pinned + @JsonKey(defaultValue: false) final bool pinned; /// Reserved field indicating when the message was pinned @JsonKey(toJson: Serialization.readOnly) - final DateTime pinnedAt; + final DateTime? pinnedAt; /// Reserved field indicating when the message will expire /// /// if `null` message has no expiry - final DateTime pinExpires; + final DateTime? pinExpires; /// Reserved field indicating who pinned the message @JsonKey(toJson: Serialization.readOnly) - final User pinnedBy; + final User? pinnedBy; /// Message custom extraData - @JsonKey(includeIfNull: false) - final Map extraData; + @JsonKey( + includeIfNull: false, + defaultValue: {}, + ) + final Map extraData; /// True if the message is a system info bool get isSystem => type == 'system'; @@ -199,7 +221,7 @@ class Message extends Equatable { /// Reserved field indicating when the message was deleted. @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) - final DateTime deletedAt; + final DateTime? deletedAt; /// Known top level fields. /// Useful for [Serialization] methods. @@ -236,39 +258,40 @@ class Message extends Equatable { /// Serialize to json Map toJson() => Serialization.moveFromExtraDataToRoot( - _$MessageToJson(this), topLevelFields); + _$MessageToJson(this), + ); /// Creates a copy of [Message] with specified attributes overridden. Message copyWith({ - String id, - String text, - String type, - List attachments, - List mentionedUsers, - Map reactionCounts, - Map reactionScores, - List latestReactions, - List ownReactions, - String parentId, - Message quotedMessage, - String quotedMessageId, - int replyCount, - List threadParticipants, - bool showInChannel, - bool shadowed, - bool silent, - String command, - DateTime createdAt, - DateTime updatedAt, - DateTime deletedAt, - User user, - bool pinned, - DateTime pinnedAt, - Object pinExpires = _pinExpires, - User pinnedBy, - Map extraData, - MessageSendingStatus status, - bool skipPush, + String? id, + String? text, + String? type, + List? attachments, + List? mentionedUsers, + Map? reactionCounts, + Map? reactionScores, + List? latestReactions, + List? ownReactions, + String? parentId, + Message? quotedMessage, + String? quotedMessageId, + int? replyCount, + List? threadParticipants, + bool? showInChannel, + bool? shadowed, + bool? silent, + String? command, + DateTime? createdAt, + DateTime? updatedAt, + DateTime? deletedAt, + User? user, + bool? pinned, + DateTime? pinnedAt, + Object? pinExpires = _pinExpires, + User? pinnedBy, + Map? extraData, + MessageSendingStatus? status, + bool? skipPush, }) { assert(() { if (pinExpires is! DateTime && @@ -306,49 +329,47 @@ class Message extends Equatable { pinned: pinned ?? this.pinned, pinnedAt: pinnedAt ?? this.pinnedAt, pinnedBy: pinnedBy ?? this.pinnedBy, - pinExpires: pinExpires == _pinExpires ? this.pinExpires : pinExpires, + pinExpires: + pinExpires == _pinExpires ? this.pinExpires : pinExpires as DateTime?, skipPush: skipPush ?? this.skipPush, ); } /// Returns a new [Message] that is a combination of this message and the /// given [other] message. - Message merge(Message other) { - if (other == null) return this; - return copyWith( - id: other.id, - text: other.text, - type: other.type, - attachments: other.attachments, - mentionedUsers: other.mentionedUsers, - reactionCounts: other.reactionCounts, - reactionScores: other.reactionScores, - latestReactions: other.latestReactions, - ownReactions: other.ownReactions, - parentId: other.parentId, - quotedMessage: other.quotedMessage, - quotedMessageId: other.quotedMessageId, - replyCount: other.replyCount, - threadParticipants: other.threadParticipants, - showInChannel: other.showInChannel, - command: other.command, - createdAt: other.createdAt, - silent: other.silent, - extraData: other.extraData, - user: other.user, - shadowed: other.shadowed, - updatedAt: other.updatedAt, - deletedAt: other.deletedAt, - status: other.status, - pinned: other.pinned, - pinnedAt: other.pinnedAt, - pinExpires: other.pinExpires, - pinnedBy: other.pinnedBy, - ); - } + Message merge(Message other) => copyWith( + id: other.id, + text: other.text, + type: other.type, + attachments: other.attachments, + mentionedUsers: other.mentionedUsers, + reactionCounts: other.reactionCounts, + reactionScores: other.reactionScores, + latestReactions: other.latestReactions, + ownReactions: other.ownReactions, + parentId: other.parentId, + quotedMessage: other.quotedMessage, + quotedMessageId: other.quotedMessageId, + replyCount: other.replyCount, + threadParticipants: other.threadParticipants, + showInChannel: other.showInChannel, + command: other.command, + createdAt: other.createdAt, + silent: other.silent, + extraData: other.extraData, + user: other.user, + shadowed: other.shadowed, + updatedAt: other.updatedAt, + deletedAt: other.deletedAt, + status: other.status, + pinned: other.pinned, + pinnedAt: other.pinnedAt, + pinExpires: other.pinExpires, + pinnedBy: other.pinnedBy, + ); @override - List get props => [ + List get props => [ id, text, type, @@ -386,7 +407,7 @@ class Message extends Equatable { @JsonSerializable() class TranslatedMessage extends Message { /// Constructor used for json serialization - TranslatedMessage(this.i18n); + TranslatedMessage(this.i18n) : super(); /// Create a new instance from a json factory TranslatedMessage.fromJson(Map json) => @@ -395,7 +416,7 @@ class TranslatedMessage extends Message { ); /// A Map of - final Map i18n; + final Map? i18n; /// Known top level fields. /// Useful for [Serialization] methods. @@ -408,6 +429,5 @@ class TranslatedMessage extends Message { @override Map toJson() => Serialization.moveFromExtraDataToRoot( _$TranslatedMessageToJson(this), - topLevelFields, ); } diff --git a/packages/stream_chat/lib/src/models/message.g.dart b/packages/stream_chat/lib/src/models/message.g.dart index 80df1589..4168777a 100644 --- a/packages/stream_chat/lib/src/models/message.g.dart +++ b/packages/stream_chat/lib/src/models/message.g.dart @@ -6,64 +6,44 @@ part of 'message.dart'; // JsonSerializableGenerator // ************************************************************************** -Message _$MessageFromJson(Map json) { +Message _$MessageFromJson(Map json) { return Message( - id: json['id'] as String, - text: json['text'] as String, - type: json['type'] as String, - attachments: (json['attachments'] as List) - ?.map((e) => e == null - ? null - : Attachment.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList(), - mentionedUsers: (json['mentioned_users'] as List) - ?.map((e) => e == null - ? null - : User.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList(), - silent: json['silent'] as bool, - shadowed: json['shadowed'] as bool, - reactionCounts: (json['reaction_counts'] as Map)?.map( - (k, e) => MapEntry(k as String, e as int), + id: json['id'] as String?, + text: json['text'] as String?, + type: json['type'] as String? ?? 'regular', + attachments: (json['attachments'] as List?) + ?.map((e) => Attachment.fromJson(e as Map)) + .toList() ?? + [], + mentionedUsers: (json['mentioned_users'] as List?) + ?.map((e) => User.fromJson(e as Map)) + .toList() ?? + [], + silent: json['silent'] as bool? ?? false, + shadowed: json['shadowed'] as bool? ?? false, + reactionCounts: (json['reaction_counts'] as Map?)?.map( + (k, e) => MapEntry(k, e as int), ), - reactionScores: (json['reaction_scores'] as Map)?.map( - (k, e) => MapEntry(k as String, e as int), + reactionScores: (json['reaction_scores'] as Map?)?.map( + (k, e) => MapEntry(k, e as int), ), - latestReactions: (json['latest_reactions'] as List) - ?.map((e) => e == null - ? null - : Reaction.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList(), - ownReactions: (json['own_reactions'] as List) - ?.map((e) => e == null - ? null - : Reaction.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList(), - parentId: json['parent_id'] as String, + latestReactions: (json['latest_reactions'] as List?) + ?.map((e) => Reaction.fromJson(e as Map)) + .toList(), + ownReactions: (json['own_reactions'] as List?) + ?.map((e) => Reaction.fromJson(e as Map)) + .toList(), + parentId: json['parent_id'] as String?, quotedMessage: json['quoted_message'] == null ? null - : Message.fromJson((json['quoted_message'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )), - quotedMessageId: json['quoted_message_id'] as String, - replyCount: json['reply_count'] as int, - threadParticipants: (json['thread_participants'] as List) - ?.map((e) => e == null - ? null - : User.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList(), - showInChannel: json['show_in_channel'] as bool, - command: json['command'] as String, + : Message.fromJson(json['quoted_message'] as Map), + quotedMessageId: json['quoted_message_id'] as String?, + replyCount: json['reply_count'] as int?, + threadParticipants: (json['thread_participants'] as List?) + ?.map((e) => User.fromJson(e as Map)) + .toList(), + showInChannel: json['show_in_channel'] as bool?, + command: json['command'] as String?, createdAt: json['created_at'] == null ? null : DateTime.parse(json['created_at'] as String), @@ -72,10 +52,8 @@ Message _$MessageFromJson(Map json) { : DateTime.parse(json['updated_at'] as String), user: json['user'] == null ? null - : User.fromJson((json['user'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )), - pinned: json['pinned'] as bool, + : User.fromJson(json['user'] as Map), + pinned: json['pinned'] as bool? ?? false, pinnedAt: json['pinned_at'] == null ? null : DateTime.parse(json['pinned_at'] as String), @@ -84,16 +62,15 @@ Message _$MessageFromJson(Map json) { : DateTime.parse(json['pin_expires'] as String), pinnedBy: json['pinned_by'] == null ? null - : User.fromJson((json['pinned_by'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )), - extraData: (json['extra_data'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - ), + : User.fromJson(json['pinned_by'] as Map), + extraData: (json['extra_data'] as Map?)?.map( + (k, e) => MapEntry(k, e as Object), + ) ?? + {}, deletedAt: json['deleted_at'] == null ? null : DateTime.parse(json['deleted_at'] as String), - skipPush: json['skip_push'] as bool, + skipPush: json['skip_push'] as bool? ?? false, ); } @@ -110,8 +87,7 @@ Map _$MessageToJson(Message instance) { } writeNotNull('type', readonly(instance.type)); - writeNotNull( - 'attachments', instance.attachments?.map((e) => e?.toJson())?.toList()); + val['attachments'] = instance.attachments.map((e) => e.toJson()).toList(); val['mentioned_users'] = Serialization.userIds(instance.mentionedUsers); writeNotNull('reaction_counts', readonly(instance.reactionCounts)); writeNotNull('reaction_scores', readonly(instance.reactionScores)); @@ -134,15 +110,15 @@ Map _$MessageToJson(Message instance) { val['pinned_at'] = readonly(instance.pinnedAt); val['pin_expires'] = instance.pinExpires?.toIso8601String(); val['pinned_by'] = readonly(instance.pinnedBy); - writeNotNull('extra_data', instance.extraData); + val['extra_data'] = instance.extraData; writeNotNull('deleted_at', readonly(instance.deletedAt)); return val; } -TranslatedMessage _$TranslatedMessageFromJson(Map json) { +TranslatedMessage _$TranslatedMessageFromJson(Map json) { return TranslatedMessage( - (json['i18n'] as Map)?.map( - (k, e) => MapEntry(k as String, e as String), + (json['i18n'] as Map?)?.map( + (k, e) => MapEntry(k, e as String), ), ); } diff --git a/packages/stream_chat/lib/src/models/mute.dart b/packages/stream_chat/lib/src/models/mute.dart index e3d5e1a0..3ba26230 100644 --- a/packages/stream_chat/lib/src/models/mute.dart +++ b/packages/stream_chat/lib/src/models/mute.dart @@ -9,7 +9,12 @@ part 'mute.g.dart'; @JsonSerializable() class Mute { /// Constructor used for json serialization - Mute({this.user, this.channel, this.createdAt, this.updatedAt}); + Mute({ + required this.user, + required this.channel, + required this.createdAt, + required this.updatedAt, + }); /// Create a new instance from a json factory Mute.fromJson(Map json) => _$MuteFromJson(json); diff --git a/packages/stream_chat/lib/src/models/mute.g.dart b/packages/stream_chat/lib/src/models/mute.g.dart index 9d0b9318..e77b8707 100644 --- a/packages/stream_chat/lib/src/models/mute.g.dart +++ b/packages/stream_chat/lib/src/models/mute.g.dart @@ -6,24 +6,12 @@ part of 'mute.dart'; // JsonSerializableGenerator // ************************************************************************** -Mute _$MuteFromJson(Map json) { +Mute _$MuteFromJson(Map json) { return Mute( - user: json['user'] == null - ? null - : User.fromJson((json['user'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )), - channel: json['channel'] == null - ? null - : ChannelModel.fromJson((json['channel'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )), - createdAt: json['created_at'] == null - ? null - : DateTime.parse(json['created_at'] as String), - updatedAt: json['updated_at'] == null - ? null - : DateTime.parse(json['updated_at'] as String), + user: User.fromJson(json['user'] as Map), + channel: ChannelModel.fromJson(json['channel'] as Map), + createdAt: DateTime.parse(json['created_at'] as String), + updatedAt: DateTime.parse(json['updated_at'] as String), ); } diff --git a/packages/stream_chat/lib/src/models/own_user.dart b/packages/stream_chat/lib/src/models/own_user.dart index 18ee2abf..292e0f4a 100644 --- a/packages/stream_chat/lib/src/models/own_user.dart +++ b/packages/stream_chat/lib/src/models/own_user.dart @@ -12,19 +12,19 @@ part 'own_user.g.dart'; class OwnUser extends User { /// Constructor used for json serialization OwnUser({ - this.devices, - this.mutes, - this.totalUnreadCount, + this.devices = const [], + this.mutes = const [], + this.totalUnreadCount = 0, this.unreadChannels, - this.channelMutes, - String id, - String role, - DateTime createdAt, - DateTime updatedAt, - DateTime lastActive, - bool online, - Map extraData, - bool banned, + this.channelMutes = const [], + required String id, + String? role, + DateTime? createdAt, + DateTime? updatedAt, + DateTime? lastActive, + bool online = false, + Map extraData = const {}, + bool banned = false, }) : super( id: id, role: role, @@ -41,24 +41,34 @@ class OwnUser extends User { Serialization.moveToExtraDataFromRoot(json, topLevelFields)); /// List of user devices - @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) + @JsonKey( + includeIfNull: false, + toJson: Serialization.readOnly, + defaultValue: []) final List devices; /// List of users muted by the user - @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) + @JsonKey( + includeIfNull: false, + toJson: Serialization.readOnly, + defaultValue: []) final List mutes; /// List of users muted by the user - @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) + @JsonKey( + includeIfNull: false, + toJson: Serialization.readOnly, + defaultValue: []) final List channelMutes; /// Total unread messages by the user - @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) + @JsonKey( + includeIfNull: false, toJson: Serialization.readOnly, defaultValue: 0) final int totalUnreadCount; /// Total unread channels by the user @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) - final int unreadChannels; + final int? unreadChannels; /// Known top level fields. /// Useful for [Serialization] methods. @@ -74,5 +84,6 @@ class OwnUser extends User { /// Serialize to json @override Map toJson() => Serialization.moveFromExtraDataToRoot( - _$OwnUserToJson(this), topLevelFields); + _$OwnUserToJson(this), + ); } diff --git a/packages/stream_chat/lib/src/models/own_user.g.dart b/packages/stream_chat/lib/src/models/own_user.g.dart index 887e6b28..a5c27402 100644 --- a/packages/stream_chat/lib/src/models/own_user.g.dart +++ b/packages/stream_chat/lib/src/models/own_user.g.dart @@ -6,33 +6,24 @@ part of 'own_user.dart'; // JsonSerializableGenerator // ************************************************************************** -OwnUser _$OwnUserFromJson(Map json) { +OwnUser _$OwnUserFromJson(Map json) { return OwnUser( - devices: (json['devices'] as List) - ?.map((e) => e == null - ? null - : Device.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList(), - mutes: (json['mutes'] as List) - ?.map((e) => e == null - ? null - : Mute.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList(), - totalUnreadCount: json['total_unread_count'] as int, - unreadChannels: json['unread_channels'] as int, - channelMutes: (json['channel_mutes'] as List) - ?.map((e) => e == null - ? null - : Mute.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList(), + devices: (json['devices'] as List?) + ?.map((e) => Device.fromJson(e as Map)) + .toList() ?? + [], + mutes: (json['mutes'] as List?) + ?.map((e) => Mute.fromJson(e as Map)) + .toList() ?? + [], + totalUnreadCount: json['total_unread_count'] as int? ?? 0, + unreadChannels: json['unread_channels'] as int?, + channelMutes: (json['channel_mutes'] as List?) + ?.map((e) => Mute.fromJson(e as Map)) + .toList() ?? + [], id: json['id'] as String, - role: json['role'] as String, + role: json['role'] as String?, createdAt: json['created_at'] == null ? null : DateTime.parse(json['created_at'] as String), @@ -42,11 +33,11 @@ OwnUser _$OwnUserFromJson(Map json) { lastActive: json['last_active'] == null ? null : DateTime.parse(json['last_active'] as String), - online: json['online'] as bool, - extraData: (json['extra_data'] as Map)?.map( - (k, e) => MapEntry(k as String, e), + online: json['online'] as bool? ?? false, + extraData: (json['extra_data'] as Map).map( + (k, e) => MapEntry(k, e as Object), ), - banned: json['banned'] as bool, + banned: json['banned'] as bool? ?? false, ); } @@ -67,7 +58,7 @@ Map _$OwnUserToJson(OwnUser instance) { writeNotNull('last_active', readonly(instance.lastActive)); writeNotNull('online', readonly(instance.online)); writeNotNull('banned', readonly(instance.banned)); - writeNotNull('extra_data', instance.extraData); + val['extra_data'] = instance.extraData; writeNotNull('devices', readonly(instance.devices)); writeNotNull('mutes', readonly(instance.mutes)); writeNotNull('channel_mutes', readonly(instance.channelMutes)); diff --git a/packages/stream_chat/lib/src/models/reaction.dart b/packages/stream_chat/lib/src/models/reaction.dart index 6792bf3f..0e60e281 100644 --- a/packages/stream_chat/lib/src/models/reaction.dart +++ b/packages/stream_chat/lib/src/models/reaction.dart @@ -10,20 +10,24 @@ class Reaction { /// Constructor used for json serialization Reaction({ this.messageId, - this.createdAt, - this.type, + DateTime? createdAt, + required this.type, this.user, - String userId, - this.score, + String? userId, + this.score = 0, this.extraData, - }) : userId = userId ?? user?.id; + }) : userId = userId ?? user?.id, + createdAt = createdAt ?? DateTime.now(); /// Create a new instance from a json - factory Reaction.fromJson(Map json) => _$ReactionFromJson( - Serialization.moveToExtraDataFromRoot(json, topLevelFields)); + factory Reaction.fromJson(Map json) => + _$ReactionFromJson(Serialization.moveToExtraDataFromRoot( + json, + topLevelFields, + )); /// The messageId to which the reaction belongs - final String messageId; + final String? messageId; /// The type of the reaction final String type; @@ -34,18 +38,19 @@ class Reaction { /// The user that sent the reaction @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) - final User user; + final User? user; /// The score of the reaction (ie. number of reactions sent) + @JsonKey(defaultValue: 0) final int score; /// The userId that sent the reaction @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) - final String userId; + final String? userId; /// Reaction custom extraData @JsonKey(includeIfNull: false) - final Map extraData; + final Map? extraData; /// Map of custom user extraData static const topLevelFields = [ @@ -59,17 +64,18 @@ class Reaction { /// Serialize to json Map toJson() => Serialization.moveFromExtraDataToRoot( - _$ReactionToJson(this), topLevelFields); + _$ReactionToJson(this), + ); /// Creates a copy of [Reaction] with specified attributes overridden. Reaction copyWith({ - String messageId, - DateTime createdAt, - String type, - User user, - String userId, - int score, - Map extraData, + String? messageId, + DateTime? createdAt, + String? type, + User? user, + String? userId, + int? score, + Map? extraData, }) => Reaction( messageId: messageId ?? this.messageId, @@ -83,16 +89,13 @@ class Reaction { /// Returns a new [Reaction] that is a combination of this reaction and the /// given [other] reaction. - Reaction merge(Reaction other) { - if (other == null) return this; - return copyWith( - messageId: other.messageId, - createdAt: other.createdAt, - type: other.type, - user: other.user, - userId: other.userId, - score: other.score, - extraData: other.extraData, - ); - } + Reaction merge(Reaction other) => copyWith( + messageId: other.messageId, + createdAt: other.createdAt, + type: other.type, + user: other.user, + userId: other.userId, + score: other.score, + extraData: other.extraData, + ); } diff --git a/packages/stream_chat/lib/src/models/reaction.g.dart b/packages/stream_chat/lib/src/models/reaction.g.dart index a270af01..24b60830 100644 --- a/packages/stream_chat/lib/src/models/reaction.g.dart +++ b/packages/stream_chat/lib/src/models/reaction.g.dart @@ -6,22 +6,20 @@ part of 'reaction.dart'; // JsonSerializableGenerator // ************************************************************************** -Reaction _$ReactionFromJson(Map json) { +Reaction _$ReactionFromJson(Map json) { return Reaction( - messageId: json['message_id'] as String, + messageId: json['message_id'] as String?, createdAt: json['created_at'] == null ? null : DateTime.parse(json['created_at'] as String), type: json['type'] as String, user: json['user'] == null ? null - : User.fromJson((json['user'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )), - userId: json['user_id'] as String, - score: json['score'] as int, - extraData: (json['extra_data'] as Map)?.map( - (k, e) => MapEntry(k as String, e), + : User.fromJson(json['user'] as Map), + userId: json['user_id'] as String?, + score: json['score'] as int? ?? 0, + extraData: (json['extra_data'] as Map?)?.map( + (k, e) => MapEntry(k, e as Object), ), ); } diff --git a/packages/stream_chat/lib/src/models/read.dart b/packages/stream_chat/lib/src/models/read.dart index fc8ef1bc..cbd47dd1 100644 --- a/packages/stream_chat/lib/src/models/read.dart +++ b/packages/stream_chat/lib/src/models/read.dart @@ -8,9 +8,9 @@ part 'read.g.dart'; class Read { /// Constructor used for json serialization Read({ - this.lastRead, - this.user, - this.unreadMessages, + required this.lastRead, + required this.user, + this.unreadMessages = 0, }); /// Create a new instance from a json @@ -23,6 +23,7 @@ class Read { final User user; /// Number of unread messages + @JsonKey(defaultValue: 0) final int unreadMessages; /// Serialize to json @@ -30,9 +31,9 @@ class Read { /// Creates a copy of [Read] with specified attributes overridden. Read copyWith({ - DateTime lastRead, - User user, - int unreadMessages, + DateTime? lastRead, + User? user, + int? unreadMessages, }) => Read( lastRead: lastRead ?? this.lastRead, diff --git a/packages/stream_chat/lib/src/models/read.g.dart b/packages/stream_chat/lib/src/models/read.g.dart index d04ae146..93c832d8 100644 --- a/packages/stream_chat/lib/src/models/read.g.dart +++ b/packages/stream_chat/lib/src/models/read.g.dart @@ -6,22 +6,16 @@ part of 'read.dart'; // JsonSerializableGenerator // ************************************************************************** -Read _$ReadFromJson(Map json) { +Read _$ReadFromJson(Map json) { return Read( - lastRead: json['last_read'] == null - ? null - : DateTime.parse(json['last_read'] as String), - user: json['user'] == null - ? null - : User.fromJson((json['user'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )), - unreadMessages: json['unread_messages'] as int, + lastRead: DateTime.parse(json['last_read'] as String), + user: User.fromJson(json['user'] as Map), + unreadMessages: json['unread_messages'] as int? ?? 0, ); } Map _$ReadToJson(Read instance) => { - 'last_read': instance.lastRead?.toIso8601String(), - 'user': instance.user?.toJson(), + 'last_read': instance.lastRead.toIso8601String(), + 'user': instance.user.toJson(), 'unread_messages': instance.unreadMessages, }; diff --git a/packages/stream_chat/lib/src/models/serialization.dart b/packages/stream_chat/lib/src/models/serialization.dart index 18cdd545..d584912e 100644 --- a/packages/stream_chat/lib/src/models/serialization.dart +++ b/packages/stream_chat/lib/src/models/serialization.dart @@ -10,16 +10,14 @@ class Serialization { static const Function readOnly = readonly; /// List of users to list of userIds - static List userIds(List users) => - users?.map((u) => u.id)?.toList(); + static List? userIds(List? users) => + users?.map((u) => u.id).toList(); /// Takes unknown json keys and puts them in the `extra_data` key static Map moveToExtraDataFromRoot( Map json, List topLevelFields, ) { - if (json == null) return null; - final jsonClone = Map.from(json); final extraDataMap = Map.from(json) @@ -38,7 +36,6 @@ class Serialization { /// the json map static Map moveFromExtraDataToRoot( Map json, - List topLevelFields, ) { final jsonClone = Map.from(json); return jsonClone diff --git a/packages/stream_chat/lib/src/models/user.dart b/packages/stream_chat/lib/src/models/user.dart index 3b9f9add..88a7440c 100644 --- a/packages/stream_chat/lib/src/models/user.dart +++ b/packages/stream_chat/lib/src/models/user.dart @@ -8,33 +8,22 @@ part 'user.g.dart'; class User { /// Constructor used for json serialization User({ - this.id, + required this.id, this.role, - this.createdAt, - this.updatedAt, + DateTime? createdAt, + DateTime? updatedAt, this.lastActive, - this.online, - this.extraData, - this.banned, - this.teams, - }); + this.online = false, + this.extraData = const {}, + this.banned = false, + this.teams = const [], + }) : createdAt = createdAt ?? DateTime.now(), + updatedAt = updatedAt ?? DateTime.now(); /// Create a new instance from a json factory User.fromJson(Map json) => _$UserFromJson( Serialization.moveToExtraDataFromRoot(json, topLevelFields)); - /// Use this named constructor to create a new user instance - User.init( - this.id, { - this.online, - this.extraData, - }) : createdAt = null, - updatedAt = null, - lastActive = null, - banned = null, - teams = null, - role = null; - /// Known top level fields. /// Useful for [Serialization] methods. static const topLevelFields = [ @@ -53,10 +42,13 @@ class User { /// User role @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) - final String role; + final String? role; /// User role - @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) + @JsonKey( + includeIfNull: false, + toJson: Serialization.readOnly, + defaultValue: []) final List teams; /// Date of user creation @@ -69,28 +61,33 @@ class User { /// Date of last user connection @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) - final DateTime lastActive; + final DateTime? lastActive; /// True if user is online - @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) + @JsonKey( + includeIfNull: false, toJson: Serialization.readOnly, defaultValue: false) final bool online; /// True if user is banned from the chat - @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) + @JsonKey( + includeIfNull: false, toJson: Serialization.readOnly, defaultValue: false) final bool banned; /// Map of custom user extraData @JsonKey(includeIfNull: false) - final Map extraData; + final Map extraData; @override int get hashCode => id.hashCode; /// Shortcut for user name - String get name => - (extraData?.containsKey('name') == true && extraData['name'] != '') - ? extraData['name'] - : id; + String get name { + if (extraData.containsKey('name')) { + final name = extraData['name'] as String; + if (name.isNotEmpty) return name; + } + return id; + } @override bool operator ==(Object other) => @@ -98,20 +95,21 @@ class User { other is User && runtimeType == other.runtimeType && id == other.id; /// Serialize to json - Map toJson() => - Serialization.moveFromExtraDataToRoot(_$UserToJson(this), topLevelFields); + Map toJson() => Serialization.moveFromExtraDataToRoot( + _$UserToJson(this), + ); /// Creates a copy of [User] with specified attributes overridden. User copyWith({ - String id, - String role, - DateTime createdAt, - DateTime updatedAt, - DateTime lastActive, - bool online, - Map extraData, - bool banned, - List teams, + String? id, + String? role, + DateTime? createdAt, + DateTime? updatedAt, + DateTime? lastActive, + bool? online, + Map? extraData, + bool? banned, + List? teams, }) => User( id: id ?? this.id, diff --git a/packages/stream_chat/lib/src/models/user.g.dart b/packages/stream_chat/lib/src/models/user.g.dart index b27935a7..11538395 100644 --- a/packages/stream_chat/lib/src/models/user.g.dart +++ b/packages/stream_chat/lib/src/models/user.g.dart @@ -6,10 +6,10 @@ part of 'user.dart'; // JsonSerializableGenerator // ************************************************************************** -User _$UserFromJson(Map json) { +User _$UserFromJson(Map json) { return User( id: json['id'] as String, - role: json['role'] as String, + role: json['role'] as String?, createdAt: json['created_at'] == null ? null : DateTime.parse(json['created_at'] as String), @@ -19,12 +19,14 @@ User _$UserFromJson(Map json) { lastActive: json['last_active'] == null ? null : DateTime.parse(json['last_active'] as String), - online: json['online'] as bool, - extraData: (json['extra_data'] as Map)?.map( - (k, e) => MapEntry(k as String, e), + online: json['online'] as bool? ?? false, + extraData: (json['extra_data'] as Map).map( + (k, e) => MapEntry(k, e as Object), ), - banned: json['banned'] as bool, - teams: (json['teams'] as List)?.map((e) => e as String)?.toList(), + banned: json['banned'] as bool? ?? false, + teams: + (json['teams'] as List?)?.map((e) => e as String).toList() ?? + [], ); } @@ -46,6 +48,6 @@ Map _$UserToJson(User instance) { writeNotNull('last_active', readonly(instance.lastActive)); writeNotNull('online', readonly(instance.online)); writeNotNull('banned', readonly(instance.banned)); - writeNotNull('extra_data', instance.extraData); + val['extra_data'] = instance.extraData; return val; } diff --git a/packages/stream_chat/lib/stream_chat.dart b/packages/stream_chat/lib/stream_chat.dart index f491c39a..2ac56b10 100644 --- a/packages/stream_chat/lib/stream_chat.dart +++ b/packages/stream_chat/lib/stream_chat.dart @@ -3,6 +3,7 @@ library stream_chat; export 'package:async/async.dart'; export 'package:dio/src/dio_error.dart'; export 'package:dio/src/multipart_file.dart'; +export 'package:dio/src/options.dart'; export 'package:dio/src/options.dart' show ProgressCallback; export 'package:logging/logging.dart' show Logger, Level; @@ -27,6 +28,7 @@ export './src/models/channel_state.dart'; export './src/models/command.dart'; export './src/models/device.dart'; export './src/models/event.dart'; +export './src/models/filter.dart' show Filter; export './src/models/member.dart'; export './src/models/message.dart'; export './src/models/mute.dart'; diff --git a/packages/stream_chat/lib/version.dart b/packages/stream_chat/lib/version.dart index a18852e1..c03f9c04 100644 --- a/packages/stream_chat/lib/version.dart +++ b/packages/stream_chat/lib/version.dart @@ -3,4 +3,4 @@ import 'package:stream_chat/src/client.dart'; /// Current package version /// Used in [StreamChatClient] to build the `x-stream-client` header // ignore: constant_identifier_names -const PACKAGE_VERSION = '1.5.3'; +const PACKAGE_VERSION = '2.0.0-nullsafety.0'; diff --git a/packages/stream_chat/pubspec.yaml b/packages/stream_chat/pubspec.yaml index 52c01b2d..6001ecf0 100644 --- a/packages/stream_chat/pubspec.yaml +++ b/packages/stream_chat/pubspec.yaml @@ -1,31 +1,31 @@ name: stream_chat homepage: https://getstream.io/ description: The official Dart client for Stream Chat, a service for building chat applications. -version: 1.5.3 +version: 2.0.0-nullsafety.0 repository: https://github.com/GetStream/stream-chat-flutter issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues environment: - sdk: ">=2.7.0 <3.0.0" + sdk: '>=2.12.0 <3.0.0' dependencies: - async: ^2.4.2 - collection: ^1.14.13 - dio: ^3.0.10 + async: ^2.5.0 + collection: ^1.15.0 + dio: ^4.0.0 equatable: ^2.0.0 - freezed_annotation: ^0.12.0 - http_parser: ^3.1.4 - json_annotation: ^3.0.1 - logging: ^0.11.4 - meta: ^1.2.4 - mime: ^0.9.7 - rxdart: ^0.25.0 - uuid: ^2.2.2 - web_socket_channel: ^1.2.0 + freezed_annotation: ^0.14.0 + http_parser: ^4.0.0 + json_annotation: ^4.0.1 + logging: ^1.0.1 + meta: ^1.3.0 + mime: ^1.0.0 + rxdart: ^0.26.0 + uuid: ^3.0.4 + web_socket_channel: ^2.0.0 dev_dependencies: - build_runner: ^1.10.0 - freezed: ^0.12.7 - json_serializable: ^3.3.0 - mockito: ^4.1.1 - test: ^1.15.7 + build_runner: ^2.0.1 + freezed: ^0.14.1+3 + json_serializable: ^4.1.0 + mocktail: ^0.1.1 + test: ^1.16.8 diff --git a/packages/stream_chat/test/src/api/channel_test.dart b/packages/stream_chat/test/src/api/channel_test.dart index 755a3f3b..c3409b7e 100644 --- a/packages/stream_chat/test/src/api/channel_test.dart +++ b/packages/stream_chat/test/src/api/channel_test.dart @@ -1,19 +1,22 @@ +import 'dart:convert'; + import 'package:dio/dio.dart'; import 'package:dio/native_imp.dart'; -import 'package:mockito/mockito.dart'; +import 'package:mocktail/mocktail.dart'; import 'package:stream_chat/src/api/requests.dart'; import 'package:stream_chat/src/client.dart'; import 'package:stream_chat/src/event_type.dart'; import 'package:stream_chat/src/models/event.dart'; import 'package:stream_chat/src/models/message.dart'; -import 'package:stream_chat/src/models/reaction.dart'; import 'package:stream_chat/src/models/own_user.dart'; +import 'package:stream_chat/src/models/reaction.dart'; +import 'package:stream_chat/stream_chat.dart'; import 'package:test/test.dart'; -import 'package:stream_chat/stream_chat.dart'; - class MockDio extends Mock implements DioForNative {} +class FakeRequestOptions extends Fake implements RequestOptions {} + class MockAttachmentUploader extends Mock implements AttachmentFileUploader {} class MockHttpClientAdapter extends Mock implements HttpClientAdapter {} @@ -24,8 +27,8 @@ void main() { test('sendMessage', () async { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); final client = StreamChatClient( 'api-key', @@ -33,28 +36,45 @@ void main() { tokenProvider: (_) async => '', ); final channelClient = client.channel('messaging', id: 'testid'); - final message = Message( - text: 'hey', - id: 'test', - ); + final message = Message(text: 'hey', id: 'test'); - when(mockDio.post('/channels/messaging/testid/message', data: { - 'message': message.toJson(), - })).thenAnswer((_) async => Response(data: '{}', statusCode: 200)); + when(() => mockDio.post( + any(), + data: any(named: 'data'), + )).thenAnswer((_) async => Response( + data: jsonEncode(ChannelState()), + statusCode: 200, + requestOptions: FakeRequestOptions(), + )); + + await channelClient.watch(); + + when( + () => mockDio.post( + '/channels/messaging/testid/message', + data: {'message': message.toJson()}, + ), + ).thenAnswer( + (_) async => Response( + data: jsonEncode({'message': message}), + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), + ); await channelClient.sendMessage(message); - verify( + verify(() => mockDio.post('/channels/messaging/testid/message', data: { - 'message': message.toJson(), - })).called(1); + 'message': message.toJson(), + })).called(1); }); test('markRead', () async { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); final client = StreamChatClient( 'api-key', @@ -64,29 +84,40 @@ void main() { final channelClient = client.channel('messaging', id: 'testid'); - when(mockDio.post( - any, - data: anyNamed('data'), - )).thenAnswer((_) async => Response( - data: '{}', + when(() => mockDio.post( + any(), + data: any(named: 'data'), + )).thenAnswer((_) async => Response( + data: jsonEncode(ChannelState()), statusCode: 200, + requestOptions: FakeRequestOptions(), )); await channelClient.watch(); - when(mockDio.post('/channels/messaging/testid/read', data: {})) - .thenAnswer((_) async => Response(data: '{}', statusCode: 200)); + when( + () => mockDio.post( + '/channels/messaging/testid/read', + data: {}, + ), + ).thenAnswer( + (_) async => Response( + data: '{}', + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), + ); await channelClient.markRead(); - verify(mockDio.post('/channels/messaging/testid/read', + verify(() => mockDio.post('/channels/messaging/testid/read', data: {})).called(1); }); test('getReplies', () async { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); final client = StreamChatClient( 'api-key', @@ -94,24 +125,28 @@ void main() { tokenProvider: (_) async => '', ); final channelClient = client.channel('messaging', id: 'testid'); - final pagination = PaginationParams(); + const pagination = PaginationParams(); - when(mockDio.get('/messages/messageid/replies', - queryParameters: pagination.toJson())) - .thenAnswer((_) async => Response(data: '{}', statusCode: 200)); + when(() => mockDio.get('/messages/messageid/replies', + queryParameters: pagination.toJson())).thenAnswer( + (_) async => Response( + data: '{}', + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), + ); await channelClient.getReplies('messageid', pagination); - verify(mockDio.get('/messages/messageid/replies', - queryParameters: pagination.toJson())) - .called(1); + verify(() => mockDio.get('/messages/messageid/replies', + queryParameters: pagination.toJson())).called(1); }); test('sendAction', () async { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); final client = StreamChatClient( 'api-key', @@ -120,39 +155,46 @@ void main() { ); final channelClient = client.channel('messaging', id: 'testid'); - when(mockDio.post( - any, - data: anyNamed('data'), - )).thenAnswer((_) async => Response( - data: '{}', - statusCode: 200, - )); + when(() => mockDio.post( + any(), + data: any(named: 'data'), + )).thenAnswer( + (_) async => Response( + data: '{}', + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), + ); await channelClient.watch(); - final Map data = {'test': true}; + final data = {'test': true}; - when(mockDio.post('/messages/messageid/action', data: { - 'id': 'testid', - 'type': 'messaging', - 'form_data': data, - 'message_id': 'messageid', - })).thenAnswer((_) async => Response(data: '{}', statusCode: 200)); + when(() => mockDio.post('/messages/messageid/action', data: { + 'id': 'testid', + 'type': 'messaging', + 'form_data': data, + 'message_id': 'messageid', + })).thenAnswer((_) async => Response( + data: '{}', + statusCode: 200, + requestOptions: FakeRequestOptions(), + )); await channelClient.sendAction(Message(id: 'messageid'), data); - verify(mockDio.post('/messages/messageid/action', data: { - 'id': 'testid', - 'type': 'messaging', - 'form_data': data, - 'message_id': 'messageid', - })).called(1); + verify(() => mockDio.post('/messages/messageid/action', data: { + 'id': 'testid', + 'type': 'messaging', + 'form_data': data, + 'message_id': 'messageid', + })).called(1); }); test('getMessagesById', () async { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); final client = StreamChatClient( 'api-key', @@ -162,13 +204,18 @@ void main() { final channelClient = client.channel('messaging', id: 'testid'); final messageIds = ['a', 'b']; - when(mockDio.get('/channels/messaging/testid/messages', - queryParameters: {'ids': messageIds.join(',')})) - .thenAnswer((_) async => Response(data: '{}', statusCode: 200)); + when(() => mockDio.get('/channels/messaging/testid/messages', + queryParameters: {'ids': messageIds.join(',')})).thenAnswer( + (_) async => Response( + data: '{}', + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), + ); await channelClient.getMessagesById(messageIds); - verify(mockDio.get('/channels/messaging/testid/messages', + verify(() => mockDio.get('/channels/messaging/testid/messages', queryParameters: {'ids': messageIds.join(',')})).called(1); }); @@ -176,12 +223,15 @@ void main() { final mockDio = MockDio(); final mockUploader = MockAttachmentUploader(); - final file = AttachmentFile(path: 'filePath/fileName.pdf'); - final channelId = 'testId'; - final channelType = 'messaging'; + const file = AttachmentFile( + path: 'filePath/fileName.pdf', + size: 100, + ); + const channelId = 'testId'; + const channelType = 'messaging'; - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); final client = StreamChatClient( 'api-key', @@ -191,24 +241,39 @@ void main() { ); final channelClient = client.channel(channelType, id: channelId); - when(mockUploader.sendFile(file, channelId, channelType)) + when(() => mockDio.post( + any(), + data: any(named: 'data'), + )).thenAnswer((_) async => Response( + data: jsonEncode(ChannelState()), + statusCode: 200, + requestOptions: FakeRequestOptions(), + )); + + await channelClient.watch(); + + when(() => mockUploader.sendFile(file, channelId, channelType)) .thenAnswer((_) async => SendFileResponse()); await channelClient.sendFile(file); - verify(mockUploader.sendFile(file, channelId, channelType)).called(1); + verify(() => mockUploader.sendFile(file, channelId, channelType)) + .called(1); }); test('sendImage', () async { final mockDio = MockDio(); final mockUploader = MockAttachmentUploader(); - final image = AttachmentFile(path: 'imagePath/imageName.jpeg'); - final channelId = 'testId'; - final channelType = 'messaging'; + const image = AttachmentFile( + path: 'imagePath/imageName.jpeg', + size: 100, + ); + const channelId = 'testId'; + const channelType = 'messaging'; - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); final client = StreamChatClient( 'api-key', @@ -218,19 +283,31 @@ void main() { ); final channelClient = client.channel(channelType, id: channelId); - when(mockUploader.sendImage(image, channelId, channelType)) + when(() => mockDio.post( + any(), + data: any(named: 'data'), + )).thenAnswer((_) async => Response( + data: jsonEncode(ChannelState()), + statusCode: 200, + requestOptions: FakeRequestOptions(), + )); + + await channelClient.watch(); + + when(() => mockUploader.sendImage(image, channelId, channelType)) .thenAnswer((_) async => SendImageResponse()); await channelClient.sendImage(image); - verify(mockUploader.sendImage(image, channelId, channelType)).called(1); + verify(() => mockUploader.sendImage(image, channelId, channelType)) + .called(1); }); test('deleteFile', () async { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); final client = StreamChatClient( 'api-key', @@ -238,23 +315,43 @@ void main() { tokenProvider: (_) async => '', ); final channelClient = client.channel('messaging', id: 'testid'); - final url = 'url'; + const url = 'url'; - when(mockDio.delete('/channels/messaging/testid/file', - queryParameters: {'url': url})) - .thenAnswer((_) async => Response(data: '{}', statusCode: 200)); + when(() => mockDio.post( + any(), + data: any(named: 'data'), + )).thenAnswer((_) async => Response( + data: jsonEncode(ChannelState()), + statusCode: 200, + requestOptions: FakeRequestOptions(), + )); + + await channelClient.watch(); + + when( + () => mockDio.delete( + '/channels/messaging/testid/file', + queryParameters: {'url': url}, + ), + ).thenAnswer( + (_) async => Response( + data: '{}', + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), + ); await channelClient.deleteFile(url); - verify(mockDio.delete('/channels/messaging/testid/file', + verify(() => mockDio.delete('/channels/messaging/testid/file', queryParameters: {'url': url})).called(1); }); test('deleteImage', () async { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); final client = StreamChatClient( 'api-key', @@ -262,15 +359,35 @@ void main() { tokenProvider: (_) async => '', ); final channelClient = client.channel('messaging', id: 'testid'); - final url = 'url'; + const url = 'url'; - when(mockDio.delete('/channels/messaging/testid/image', - queryParameters: {'url': url})) - .thenAnswer((_) async => Response(data: '{}', statusCode: 200)); + when(() => mockDio.post( + any(), + data: any(named: 'data'), + )).thenAnswer((_) async => Response( + data: jsonEncode(ChannelState()), + statusCode: 200, + requestOptions: FakeRequestOptions(), + )); + + await channelClient.watch(); + + when( + () => mockDio.delete( + '/channels/messaging/testid/image', + queryParameters: {'url': url}, + ), + ).thenAnswer( + (_) async => Response( + data: '{}', + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), + ); await channelClient.deleteImage(url); - verify(mockDio.delete('/channels/messaging/testid/image', + verify(() => mockDio.delete('/channels/messaging/testid/image', queryParameters: {'url': url})).called(1); }); @@ -290,60 +407,90 @@ void main() { test('should be pinned successfully', () async { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); final client = StreamChatClient( 'api-key', httpClient: mockDio, tokenProvider: (_) async => '', ); - final channelClient = client.channel('messaging', id: 'testid'); + final message = Message(text: 'Hello', id: 'test'); - final message = Message( - text: 'Hello', - id: 'test', + when(() => mockDio.post( + any(), + data: any(named: 'data'), + )).thenAnswer((_) async => Response( + data: jsonEncode(ChannelState()), + statusCode: 200, + requestOptions: FakeRequestOptions(), + )); + + await channelClient.watch(); + + when( + () => mockDio.post( + '/messages/${message.id}', + data: anything, + ), + ).thenAnswer( + (_) async => Response( + data: jsonEncode({'message': message}), + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), ); - when(mockDio.post( - '/messages/${message.id}', - data: anything, - )).thenAnswer((_) async => Response(data: '{}', statusCode: 200)); - await channelClient.pinMessage(message, 30); - verify(mockDio.post('/messages/${message.id}', data: anything)) + verify(() => + mockDio.post('/messages/${message.id}', data: anything)) .called(1); }); test('should be unpinned successfully', () async { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); final client = StreamChatClient( 'api-key', httpClient: mockDio, tokenProvider: (_) async => '', ); - final channelClient = client.channel('messaging', id: 'testid'); + final message = Message(text: 'Hello', id: 'test'); - final message = Message( - text: 'Hello', - id: 'test', + when(() => mockDio.post( + any(), + data: any(named: 'data'), + )).thenAnswer((_) async => Response( + data: jsonEncode(ChannelState()), + statusCode: 200, + requestOptions: FakeRequestOptions(), + )); + + await channelClient.watch(); + + when( + () => mockDio.post( + '/messages/${message.id}', + data: anything, + ), + ).thenAnswer( + (_) async => Response( + data: jsonEncode({'message': message}), + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), ); - when(mockDio.post( - '/messages/${message.id}', - data: anything, - )).thenAnswer((_) async => Response(data: '{}', statusCode: 200)); - await channelClient.unpinMessage(message); - verify(mockDio.post('/messages/${message.id}', data: anything)) + verify(() => + mockDio.post('/messages/${message.id}', data: anything)) .called(1); }); }); @@ -351,8 +498,8 @@ void main() { test('sendEvent', () async { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); final client = StreamChatClient( 'api-key', @@ -361,32 +508,46 @@ void main() { ); final channelClient = client.channel('messaging', id: 'testid'); - when(mockDio.post( - any, - data: anyNamed('data'), - )).thenAnswer((_) async => Response( - data: '{}', - statusCode: 200, - )); + when( + () => mockDio.post( + any(), + data: any(named: 'data'), + ), + ).thenAnswer( + (_) async => Response( + data: '{}', + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), + ); await channelClient.watch(); - final event = Event(type: EventType.any); + const event = Event(type: EventType.any); - when(mockDio.post('/channels/messaging/testid/event', - data: {'event': event.toJson()})) - .thenAnswer((_) async => Response(data: '{}', statusCode: 200)); + when( + () => mockDio.post( + '/channels/messaging/testid/event', + data: {'event': event.toJson()}, + ), + ).thenAnswer( + (_) async => Response( + data: '{}', + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), + ); await channelClient.sendEvent(event); - verify(mockDio.post('/channels/messaging/testid/event', + verify(() => mockDio.post('/channels/messaging/testid/event', data: {'event': event.toJson()})).called(1); }); test('keyStroke', () async { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); final client = StreamChatClient( 'api-key', @@ -395,32 +556,46 @@ void main() { ); final channelClient = client.channel('messaging', id: 'testid'); - when(mockDio.post( - any, - data: anyNamed('data'), - )).thenAnswer((_) async => Response( - data: '{}', - statusCode: 200, - )); + when( + () => mockDio.post( + any(), + data: any(named: 'data'), + ), + ).thenAnswer( + (_) async => Response( + data: '{}', + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), + ); await channelClient.watch(); - final event = Event(type: EventType.typingStart); + const event = Event(type: EventType.typingStart); - when(mockDio.post('/channels/messaging/testid/event', - data: {'event': event.toJson()})) - .thenAnswer((_) async => Response(data: '{}', statusCode: 200)); + when( + () => mockDio.post( + '/channels/messaging/testid/event', + data: {'event': event.toJson()}, + ), + ).thenAnswer( + (_) async => Response( + data: '{}', + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), + ); await channelClient.keyStroke(); - verify(mockDio.post('/channels/messaging/testid/event', + verify(() => mockDio.post('/channels/messaging/testid/event', data: {'event': event.toJson()})).called(1); }); test('stopTyping', () async { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); final client = StreamChatClient( 'api-key', @@ -429,24 +604,36 @@ void main() { ); final channelClient = client.channel('messaging', id: 'testid'); - when(mockDio.post( - any, - data: anyNamed('data'), - )).thenAnswer((_) async => Response( - data: '{}', - statusCode: 200, - )); + when(() => mockDio.post( + any(), + data: any(named: 'data'), + )).thenAnswer( + (_) async => Response( + data: '{}', + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), + ); await channelClient.watch(); - final event = Event(type: EventType.typingStop); + const event = Event(type: EventType.typingStop); - when(mockDio.post('/channels/messaging/testid/event', - data: {'event': event.toJson()})) - .thenAnswer((_) async => Response(data: '{}', statusCode: 200)); + when( + () => mockDio.post( + '/channels/messaging/testid/event', + data: {'event': event.toJson()}, + ), + ).thenAnswer( + (_) async => Response( + data: '{}', + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), + ); await channelClient.stopTyping(); - verify(mockDio.post('/channels/messaging/testid/event', + verify(() => mockDio.post('/channels/messaging/testid/event', data: {'event': event.toJson()})).called(1); }); @@ -454,84 +641,120 @@ void main() { test('sendReaction', () async { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); final client = StreamChatClient( 'api-key', httpClient: mockDio, tokenProvider: (_) async => '', - )..state.user = OwnUser(id: 'test-id'); + ); + final user = OwnUser(id: 'test-id'); + + client.state.user = user; + + final message = Message(id: 'messageid'); + const reactionType = 'test'; + final reaction = Reaction(type: reactionType); final channelClient = client.channel('messaging', id: 'testid'); - final reactionType = 'test'; - when(mockDio.post( - '/messages/messageid/reaction', - data: { - 'reaction': { - 'type': reactionType, + when(() => mockDio.post( + any(), + data: any(named: 'data'), + )).thenAnswer( + (_) async => Response( + data: '{}', + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), + ); + await channelClient.watch(); + + when( + () => mockDio.post( + '/messages/${message.id}/reaction', + data: { + 'reaction': { + 'type': reactionType, + }, + 'enforce_unique': false, }, - 'enforce_unique': false, - }, - )).thenAnswer((_) async => Response(data: '{}', statusCode: 200)); + ), + ).thenAnswer( + (_) async => Response( + data: jsonEncode({ + 'message': message, + 'reaction': reaction, + }), + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), + ); await channelClient.sendReaction( - Message( - id: 'messageid', - reactionCounts: const {}, - reactionScores: const {}, - latestReactions: const [], - ownReactions: const [], - ), + message, reactionType, ); - verify(mockDio.post('/messages/messageid/reaction', data: { - 'reaction': { - 'type': reactionType, - }, - 'enforce_unique': false, - })).called(1); + verify( + () => mockDio.post('/messages/messageid/reaction', data: { + 'reaction': { + 'type': reactionType, + }, + 'enforce_unique': false, + })).called(1); }); test('deleteReaction', () async { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); final client = StreamChatClient( 'api-key', httpClient: mockDio, tokenProvider: (_) async => '', - )..state.user = OwnUser(id: 'test-id'); + ); + + client.state.user = OwnUser(id: 'test-id'); final channelClient = client.channel('messaging', id: 'testid'); - when(mockDio.delete('/messages/messageid/reaction/test')) - .thenAnswer((_) async => Response(data: '{}', statusCode: 200)); + when( + () => mockDio.delete('/messages/messageid/reaction/test'), + ).thenAnswer( + (_) async => Response( + data: '{}', + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), + ); await channelClient.deleteReaction( Message( id: 'messageid', - reactionCounts: const {}, - reactionScores: const {}, - latestReactions: const [], - ownReactions: const [], ), - Reaction(type: 'test'), + Reaction( + type: 'test', + createdAt: DateTime.now(), + user: User( + id: client.state.user?.id ?? '', + ), + ), ); - verify(mockDio.delete('/messages/messageid/reaction/test')) + verify(() => + mockDio.delete('/messages/messageid/reaction/test')) .called(1); }); test('getReactions', () async { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); final client = StreamChatClient( 'api-key', @@ -539,17 +762,25 @@ void main() { tokenProvider: (_) async => '', ); final channelClient = client.channel('messaging', id: 'testid'); - final pagination = PaginationParams(); + const pagination = PaginationParams(); - when(mockDio.get('/messages/messageid/reactions', - queryParameters: pagination.toJson())) - .thenAnswer((_) async => Response(data: '{}', statusCode: 200)); + when( + () => mockDio.get( + '/messages/messageid/reactions', + queryParameters: pagination.toJson(), + ), + ).thenAnswer( + (_) async => Response( + data: '{}', + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), + ); await channelClient.getReactions('messageid', pagination); - verify(mockDio.get('/messages/messageid/reactions', - queryParameters: pagination.toJson())) - .called(1); + verify(() => mockDio.get('/messages/messageid/reactions', + queryParameters: pagination.toJson())).called(1); }); }); @@ -557,8 +788,8 @@ void main() { test('addMembers', () async { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); final client = StreamChatClient( 'api-key', @@ -566,25 +797,52 @@ void main() { tokenProvider: (_) async => '', ); final channelClient = client.channel('messaging', id: 'testid'); - final members = ['vishal']; + final channelModel = ChannelModel(cid: 'messaging:testid'); + + when(() => mockDio.post( + any(), + data: any(named: 'data'), + )).thenAnswer((_) async => Response( + data: jsonEncode(ChannelState(channel: channelModel)), + statusCode: 200, + requestOptions: FakeRequestOptions(), + )); + + await channelClient.watch(); + + final members = [Member(userId: 'vishal')]; + final memberIds = members.map((e) => e.userId!).toList(); final message = Message(text: 'test'); - when(mockDio.post('/channels/messaging/testid', - data: {'add_members': members, 'message': message.toJson()})) - .thenAnswer((_) async => Response(data: '{}', statusCode: 200)); + when( + () => mockDio.post( + '/channels/messaging/testid', + data: {'add_members': memberIds, 'message': message.toJson()}, + ), + ).thenAnswer( + (_) async => Response( + data: jsonEncode({ + 'members': members, + 'message': message, + 'channel': channelModel, + }), + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), + ); - await channelClient.addMembers(members, message); + await channelClient.addMembers(memberIds, message); - verify(mockDio.post('/channels/messaging/testid', - data: {'add_members': members, 'message': message.toJson()})) + verify(() => mockDio.post('/channels/messaging/testid', + data: {'add_members': memberIds, 'message': message.toJson()})) .called(1); }); test('acceptInvite', () async { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); final client = StreamChatClient( 'api-key', @@ -592,15 +850,40 @@ void main() { tokenProvider: (_) async => '', ); final channelClient = client.channel('messaging', id: 'testid'); + final channelModel = ChannelModel(cid: 'messaging:testid'); + + when(() => mockDio.post( + any(), + data: any(named: 'data'), + )).thenAnswer((_) async => Response( + data: jsonEncode(ChannelState(channel: channelModel)), + statusCode: 200, + requestOptions: FakeRequestOptions(), + )); + + await channelClient.watch(); + final message = Message(text: 'test'); - when(mockDio.post('/channels/messaging/testid', - data: {'accept_invite': true, 'message': message.toJson()})) - .thenAnswer((_) async => Response(data: '{}', statusCode: 200)); + when( + () => mockDio.post( + '/channels/messaging/testid', + data: {'accept_invite': true, 'message': message.toJson()}, + ), + ).thenAnswer( + (_) async => Response( + data: jsonEncode({ + 'message': message, + 'channel': channelModel, + }), + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), + ); await channelClient.acceptInvite(message); - verify(mockDio.post('/channels/messaging/testid', + verify(() => mockDio.post('/channels/messaging/testid', data: {'accept_invite': true, 'message': message.toJson()})) .called(1); }); @@ -609,8 +892,8 @@ void main() { test('without id', () async { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); final client = StreamChatClient( 'api-key', @@ -618,15 +901,18 @@ void main() { tokenProvider: (_) async => '', ); final channelClient = client.channel('messaging'); - final Map options = { + final options = { 'watch': true, 'state': false, 'presence': true, }; - when(mockDio.post('/channels/messaging/query', data: options)) - .thenAnswer((_) async { - return Response(data: r''' + when(() => mockDio.post( + '/channels/messaging/query', + data: options, + )).thenAnswer( + (_) async => Response( + data: r''' { "channel": { "id": "!members-0LOcD0mZtTan60zHobLmELjdndXsonnBVNzZnB5mTt0", @@ -912,23 +1198,27 @@ void main() { } ] } - ''', statusCode: 200); - }); + ''', + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), + ); final response = await channelClient.query(options: options); - verify(mockDio.post('/channels/messaging/query', - data: options)) - .called(1); - expect(channelClient.id, response.channel.id); - expect(channelClient.cid, response.channel.cid); + verify(() => mockDio.post( + '/channels/messaging/query', + data: options, + )).called(1); + expect(channelClient.id, response.channel?.id); + expect(channelClient.cid, response.channel?.cid); }); test('with id', () async { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); final client = StreamChatClient( 'api-key', @@ -936,13 +1226,12 @@ void main() { tokenProvider: (_) async => '', ); final channelClient = client.channel('messaging', id: 'testid'); - final Map options = { - 'state': false, - }; + final options = {'state': false}; - when(mockDio.post('/channels/messaging/testid/query', - data: options)) - .thenAnswer((_) async => Response(data: r''' + when(() => mockDio.post('/channels/messaging/testid/query', + data: options)).thenAnswer( + (_) async => Response( + data: r''' { "channel": { "id": "!members-0LOcD0mZtTan60zHobLmELjdndXsonnBVNzZnB5mTt0", @@ -1228,21 +1517,24 @@ void main() { } ] } - ''', statusCode: 200)); + ''', + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), + ); await channelClient.query(options: options); - verify(mockDio.post('/channels/messaging/testid/query', - data: options)) - .called(1); + verify(() => mockDio.post('/channels/messaging/testid/query', + data: options)).called(1); }); }); test('create', () async { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); final client = StreamChatClient( 'api-key', @@ -1250,14 +1542,16 @@ void main() { tokenProvider: (_) async => '', ); final channelClient = client.channel('messaging'); - final Map options = { + final options = { 'watch': false, 'state': false, 'presence': false, }; - when(mockDio.post('/channels/messaging/query', data: options)) - .thenAnswer((_) async => Response(data: r''' + when(() => mockDio.post('/channels/messaging/query', + data: options)).thenAnswer( + (_) async => Response( + data: r''' { "channel": { "id": "!members-0LOcD0mZtTan60zHobLmELjdndXsonnBVNzZnB5mTt0", @@ -1543,21 +1837,25 @@ void main() { } ] } - ''', statusCode: 200)); + ''', + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), + ); final response = await channelClient.create(); - verify(mockDio.post('/channels/messaging/query', data: options)) - .called(1); - expect(channelClient.id, response.channel.id); - expect(channelClient.cid, response.channel.cid); + verify(() => mockDio.post('/channels/messaging/query', + data: options)).called(1); + expect(channelClient.id, response.channel?.id); + expect(channelClient.cid, response.channel?.cid); }); test('watch', () async { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); final client = StreamChatClient( 'api-key', @@ -1571,8 +1869,10 @@ void main() { 'presence': true, }; - when(mockDio.post('/channels/messaging/query', data: options)) - .thenAnswer((_) async => Response(data: r''' + when(() => mockDio.post('/channels/messaging/query', + data: options)).thenAnswer( + (_) async => Response( + data: r''' { "channel": { "id": "!members-0LOcD0mZtTan60zHobLmELjdndXsonnBVNzZnB5mTt0", @@ -1858,21 +2158,25 @@ void main() { } ] } - ''', statusCode: 200)); + ''', + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), + ); final response = await channelClient.watch({'presence': true}); - verify(mockDio.post('/channels/messaging/query', data: options)) - .called(1); - expect(channelClient.id, response.channel.id); - expect(channelClient.cid, response.channel.cid); + verify(() => mockDio.post('/channels/messaging/query', + data: options)).called(1); + expect(channelClient.id, response.channel?.id); + expect(channelClient.cid, response.channel?.cid); }); test('stopWatching', () async { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); final client = StreamChatClient( 'api-key', @@ -1881,24 +2185,32 @@ void main() { ); final channelClient = client.channel('messaging', id: 'testid'); - when(mockDio.post( - '/channels/messaging/testid/stop-watching', - data: {}, - )).thenAnswer((_) async => Response(data: '{}', statusCode: 200)); + when( + () => mockDio.post( + '/channels/messaging/testid/stop-watching', + data: {}, + ), + ).thenAnswer( + (_) async => Response( + data: '{}', + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), + ); await channelClient.stopWatching(); - verify(mockDio.post( - '/channels/messaging/testid/stop-watching', - data: {}, - )).called(1); + verify(() => mockDio.post( + '/channels/messaging/testid/stop-watching', + data: {}, + )).called(1); }); test('update', () async { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); final client = StreamChatClient( 'api-key', @@ -1906,26 +2218,55 @@ void main() { tokenProvider: (_) async => '', ); final channelClient = client.channel('messaging', id: 'testid'); + final channelModel = ChannelModel(cid: 'messaging:testid'); + + when(() => mockDio.post( + any(), + data: any(named: 'data'), + )).thenAnswer((_) async => Response( + data: jsonEncode(ChannelState(channel: channelModel)), + statusCode: 200, + requestOptions: FakeRequestOptions(), + )); + + await channelClient.watch(); + final message = Message(text: 'test'); - when(mockDio.post('/channels/messaging/testid', data: { - 'message': message.toJson(), - 'data': {'test': true}, - })).thenAnswer((_) async => Response(data: '{}', statusCode: 200)); + when( + () => mockDio.post( + '/channels/messaging/testid', + data: { + 'message': message.toJson(), + 'data': {'test': true}, + }, + ), + ).thenAnswer( + (_) async => Response( + data: jsonEncode({ + 'channel': channelModel, + 'message': message, + }), + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), + ); await channelClient.update({'test': true}, message); - verify(mockDio.post('/channels/messaging/testid', data: { - 'message': message.toJson(), - 'data': {'test': true}, - })).called(1); + verify( + () => mockDio.post('/channels/messaging/testid', data: { + 'message': message.toJson(), + 'data': {'test': true}, + }), + ).called(1); }); test('delete', () async { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); final client = StreamChatClient( 'api-key', @@ -1934,19 +2275,27 @@ void main() { ); final channelClient = client.channel('messaging', id: 'testid'); - when(mockDio.delete('/channels/messaging/testid')) - .thenAnswer((_) async => Response(data: '{}', statusCode: 200)); + when( + () => mockDio.delete('/channels/messaging/testid'), + ).thenAnswer( + (_) async => Response( + data: '{}', + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), + ); await channelClient.delete(); - verify(mockDio.delete('/channels/messaging/testid')).called(1); + verify(() => mockDio.delete('/channels/messaging/testid')) + .called(1); }); test('truncate', () async { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); final client = StreamChatClient( 'api-key', @@ -1955,20 +2304,28 @@ void main() { ); final channelClient = client.channel('messaging', id: 'testid'); - when(mockDio.post('/channels/messaging/testid/truncate')) - .thenAnswer((_) async => Response(data: '{}', statusCode: 200)); + when( + () => mockDio.post('/channels/messaging/testid/truncate'), + ).thenAnswer( + (_) async => Response( + data: '{}', + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), + ); await channelClient.truncate(); - verify(mockDio.post('/channels/messaging/testid/truncate')) + verify(() => + mockDio.post('/channels/messaging/testid/truncate')) .called(1); }); test('rejectInvite', () async { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); final client = StreamChatClient( 'api-key', @@ -1976,15 +2333,40 @@ void main() { tokenProvider: (_) async => '', ); final channelClient = client.channel('messaging', id: 'testid'); + final channelModel = ChannelModel(cid: 'messaging:testid'); + + when(() => mockDio.post( + any(), + data: any(named: 'data'), + )).thenAnswer((_) async => Response( + data: jsonEncode(ChannelState(channel: channelModel)), + statusCode: 200, + requestOptions: FakeRequestOptions(), + )); + + await channelClient.watch(); + final message = Message(text: 'test'); - when(mockDio.post('/channels/messaging/testid', - data: {'reject_invite': true, 'message': message.toJson()})) - .thenAnswer((_) async => Response(data: '{}', statusCode: 200)); + when( + () => mockDio.post( + '/channels/messaging/testid', + data: {'reject_invite': true, 'message': message.toJson()}, + ), + ).thenAnswer( + (_) async => Response( + data: jsonEncode({ + 'message': message, + 'channel': channelModel, + }), + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), + ); await channelClient.rejectInvite(message); - verify(mockDio.post('/channels/messaging/testid', + verify(() => mockDio.post('/channels/messaging/testid', data: {'reject_invite': true, 'message': message.toJson()})) .called(1); }); @@ -1992,8 +2374,8 @@ void main() { test('inviteMembers', () async { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); final client = StreamChatClient( 'api-key', @@ -2001,24 +2383,52 @@ void main() { tokenProvider: (_) async => '', ); final channelClient = client.channel('messaging', id: 'testid'); - final members = ['vishal']; + final channelModel = ChannelModel(cid: 'messaging:testid'); + + when(() => mockDio.post( + any(), + data: any(named: 'data'), + )).thenAnswer((_) async => Response( + data: jsonEncode(ChannelState(channel: channelModel)), + statusCode: 200, + requestOptions: FakeRequestOptions(), + )); + + await channelClient.watch(); + + final members = [Member(userId: 'vishal')]; + final memberIds = members.map((e) => e.userId!).toList(); final message = Message(text: 'test'); - when(mockDio.post('/channels/messaging/testid', - data: {'invites': members, 'message': message.toJson()})) - .thenAnswer((_) async => Response(data: '{}', statusCode: 200)); + when( + () => mockDio.post( + '/channels/messaging/testid', + data: {'invites': memberIds, 'message': message.toJson()}, + ), + ).thenAnswer( + (_) async => Response( + data: jsonEncode({ + 'members': members, + 'message': message, + 'channel': channelModel, + }), + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), + ); - await channelClient.inviteMembers(members, message); + await channelClient.inviteMembers(memberIds, message); - verify(mockDio.post('/channels/messaging/testid', - data: {'invites': members, 'message': message.toJson()})).called(1); + verify(() => mockDio.post('/channels/messaging/testid', + data: {'invites': memberIds, 'message': message.toJson()})) + .called(1); }); test('removeMembers', () async { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); final client = StreamChatClient( 'api-key', @@ -2026,25 +2436,53 @@ void main() { tokenProvider: (_) async => '', ); final channelClient = client.channel('messaging', id: 'testid'); - final members = ['vishal']; + final channelModel = ChannelModel(cid: 'messaging:testid'); + + when(() => mockDio.post( + any(), + data: any(named: 'data'), + )).thenAnswer((_) async => Response( + data: jsonEncode(ChannelState(channel: channelModel)), + statusCode: 200, + requestOptions: FakeRequestOptions(), + )); + + await channelClient.watch(); + + final members = [Member(userId: 'vishal')]; + final memberIds = members.map((e) => e.userId!).toList(); final message = Message(text: 'test'); - when(mockDio.post('/channels/messaging/testid', - data: {'remove_members': members, 'message': message.toJson()})) - .thenAnswer((_) async => Response(data: '{}', statusCode: 200)); + when( + () => mockDio.post( + '/channels/messaging/testid', + data: {'remove_members': memberIds, 'message': message.toJson()}, + ), + ).thenAnswer( + (_) async => Response( + data: jsonEncode({ + 'members': members, + 'message': message, + 'channel': channelModel, + }), + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), + ); - await channelClient.removeMembers(members, message); + await channelClient.removeMembers(memberIds, message); - verify(mockDio.post('/channels/messaging/testid', - data: {'remove_members': members, 'message': message.toJson()})) - .called(1); + verify(() => mockDio.post('/channels/messaging/testid', data: { + 'remove_members': memberIds, + 'message': message.toJson() + })).called(1); }); test('hide', () async { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); final client = StreamChatClient( 'api-key', @@ -2053,30 +2491,44 @@ void main() { ); final channelClient = client.channel('messaging', id: 'testid'); - when(mockDio.post( - any, - data: anyNamed('data'), - )).thenAnswer((_) async => Response( - data: '{}', - statusCode: 200, - )); + when( + () => mockDio.post( + any(), + data: any(named: 'data'), + ), + ).thenAnswer( + (_) async => Response( + data: '{}', + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), + ); await channelClient.watch(); - when(mockDio.post('/channels/messaging/testid/hide', - data: {'clear_history': true})) - .thenAnswer((_) async => Response(data: '{}', statusCode: 200)); + when( + () => mockDio.post( + '/channels/messaging/testid/hide', + data: {'clear_history': true}, + ), + ).thenAnswer( + (_) async => Response( + data: '{}', + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), + ); await channelClient.hide(clearHistory: true); - verify(mockDio.post('/channels/messaging/testid/hide', + verify(() => mockDio.post('/channels/messaging/testid/hide', data: {'clear_history': true})).called(1); }); test('show', () async { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); final client = StreamChatClient( 'api-key', @@ -2085,29 +2537,41 @@ void main() { ); final channelClient = client.channel('messaging', id: 'testid'); - when(mockDio.post( - any, - data: anyNamed('data'), - )).thenAnswer((_) async => Response( - data: '{}', - statusCode: 200, - )); + when( + () => mockDio.post( + any(), + data: any(named: 'data'), + ), + ).thenAnswer( + (_) async => Response( + data: '{}', + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), + ); await channelClient.watch(); - when(mockDio.post('/channels/messaging/testid/show')) - .thenAnswer((_) async => Response(data: '{}', statusCode: 200)); + when( + () => mockDio.post('/channels/messaging/testid/show'), + ).thenAnswer( + (_) async => Response( + data: '{}', + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), + ); await channelClient.show(); - verify(mockDio.post('/channels/messaging/testid/show')) + verify(() => mockDio.post('/channels/messaging/testid/show')) .called(1); }); test('banUser', () async { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); final client = StreamChatClient( 'api-key', @@ -2116,38 +2580,51 @@ void main() { ); final channelClient = client.channel('messaging', id: 'testid'); - when(mockDio.post( - any, - data: anyNamed('data'), - )).thenAnswer((_) async => Response( - data: '{}', - statusCode: 200, - )); + when( + () => mockDio.post( + any(), + data: any(named: 'data'), + ), + ).thenAnswer( + (_) async => Response( + data: '{}', + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), + ); await channelClient.watch(); - when(mockDio.post('/moderation/ban', data: { - 'test': true, - 'target_user_id': 'test-id', - 'type': 'messaging', - 'id': 'testid', - })).thenAnswer((_) async => Response(data: '{}', statusCode: 200)); + when( + () => mockDio.post('/moderation/ban', data: { + 'test': true, + 'target_user_id': 'test-id', + 'type': 'messaging', + 'id': 'testid', + }), + ).thenAnswer( + (_) async => Response( + data: '{}', + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), + ); - final Map options = {'test': true}; + final options = {'test': true}; await channelClient.banUser('test-id', options); - verify(mockDio.post('/moderation/ban', data: { - 'test': true, - 'target_user_id': 'test-id', - 'type': 'messaging', - 'id': 'testid', - })).called(1); + verify(() => mockDio.post('/moderation/ban', data: { + 'test': true, + 'target_user_id': 'test-id', + 'type': 'messaging', + 'id': 'testid', + })).called(1); }); test('unbanUser', () async { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); final client = StreamChatClient( 'api-key', @@ -2156,28 +2633,46 @@ void main() { ); final channelClient = client.channel('messaging', id: 'testid'); - when(mockDio.post( - any, - data: anyNamed('data'), - )).thenAnswer((_) async => Response( - data: '{}', - statusCode: 200, - )); + when( + () => mockDio.post( + any(), + data: any(named: 'data'), + ), + ).thenAnswer( + (_) async => Response( + data: '{}', + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), + ); await channelClient.watch(); - when(mockDio.delete('/moderation/ban', queryParameters: { - 'target_user_id': 'test-id', - 'type': 'messaging', - 'id': 'testid', - })).thenAnswer((_) async => Response(data: '{}', statusCode: 200)); + when( + () => mockDio.delete( + '/moderation/ban', + queryParameters: { + 'target_user_id': 'test-id', + 'type': 'messaging', + 'id': 'testid', + }, + ), + ).thenAnswer( + (_) async => Response( + data: '{}', + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), + ); await channelClient.unbanUser('test-id'); - verify(mockDio.delete('/moderation/ban', queryParameters: { - 'target_user_id': 'test-id', - 'type': 'messaging', - 'id': 'testid', - })).called(1); + verify( + () => mockDio.delete('/moderation/ban', queryParameters: { + 'target_user_id': 'test-id', + 'type': 'messaging', + 'id': 'testid', + }), + ).called(1); }); }); }); diff --git a/packages/stream_chat/test/src/api/requests_test.dart b/packages/stream_chat/test/src/api/requests_test.dart index de8ddaf8..76253403 100644 --- a/packages/stream_chat/test/src/api/requests_test.dart +++ b/packages/stream_chat/test/src/api/requests_test.dart @@ -1,18 +1,19 @@ -import 'package:test/test.dart'; import 'package:stream_chat/stream_chat.dart'; +import 'package:test/test.dart'; void main() { group('src/api/requests', () { test('SortOption', () { - final option = SortOption('name'); + const option = SortOption('name'); final j = option.toJson(); expect(j, {'field': 'name', 'direction': -1}); }); test('PaginationParams', () { - final option = PaginationParams(); + const option = PaginationParams(); final j = option.toJson(); - expect(j, {'limit': 10, 'offset': 0}); + expect(j, containsPair('limit', 10)); + expect(j, containsPair('offset', 0)); }); }); } diff --git a/packages/stream_chat/test/src/api/responses_test.dart b/packages/stream_chat/test/src/api/responses_test.dart index 8c7b57e5..f91248af 100644 --- a/packages/stream_chat/test/src/api/responses_test.dart +++ b/packages/stream_chat/test/src/api/responses_test.dart @@ -12,7 +12,8 @@ import 'package:stream_chat/stream_chat.dart'; void main() { group('src/api/responses', () { test('QueryChannelsResponse', () { - const jsonExample = r'''{ + const jsonExample = r''' + { "channels": [ { "channel": { @@ -3284,7 +3285,7 @@ void main() { }); test('QueryReactionsResponse', () { - const jsonExample = r''' + const jsonExample = ''' {"reactions": [{"message_id": "4637f7e4-a06b-42db-ba5a-8d8270dd926f","user_id": "c1c9b454-2bcc-402d-8bb0-2f3706ce1680","user": {"id": "c1c9b454-2bcc-402d-8bb0-2f3706ce1680","role": "user","created_at": "2020-01-28T22:17:30.83015Z","updated_at": "2020-01-28T22:17:31.19435Z","banned": false,"online": false,"image": "https://randomuser.me/api/portraits/women/2.jpg","name": "Mia Denys"},"type": "love","score": 1,"created_at": "2020-01-28T22:17:31.128376Z","updated_at": "2020-01-28T22:17:31.128376Z"}]} '''; final response = @@ -3402,37 +3403,38 @@ void main() { test('ListDevicesResponse', () { const jsonExample = - r'''{"devices":[{"push_provider":"firebase","id":"test"}],"duration":"0.35ms"}'''; + '''{"devices":[{"push_provider":"firebase","id":"test"}],"duration":"0.35ms"}'''; final response = ListDevicesResponse.fromJson(json.decode(jsonExample)); expect(response.devices, isA>()); }); test('SendFileResponse', () { - const jsonExample = r'''{"file": "file-url","duration":"0.35ms"}'''; + const jsonExample = '''{"file": "file-url","duration":"0.35ms"}'''; final response = SendFileResponse.fromJson(json.decode(jsonExample)); expect(response.file, isA()); }); test('SendImageResponse', () { - const jsonExample = r'''{"file": "file-url","duration":"0.35ms"}'''; + const jsonExample = '''{"file": "file-url","duration":"0.35ms"}'''; final response = SendImageResponse.fromJson(json.decode(jsonExample)); expect(response.file, isA()); }); test('SendImageResponse', () { - const jsonExample = r'''{"file": "file-url","duration":"0.35ms"}'''; + const jsonExample = '''{"file": "file-url","duration":"0.35ms"}'''; final response = SendImageResponse.fromJson(json.decode(jsonExample)); expect(response.file, isA()); }); test('EmptyResponse', () { - const jsonExample = r'''{"file": "file-url","duration":"0.35ms"}'''; + const jsonExample = '''{"file": "file-url","duration":"0.35ms"}'''; final response = EmptyResponse.fromJson(json.decode(jsonExample)); expect(response.duration, isA()); }); test('SendReactionResponse', () { - const jsonExample = r'''{"message": { + const jsonExample = r''' + {"message": { "id": "c6076f11-7768-4a04-bdf2-c43dddd6d666", "text": "What we don't know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.", "html": "\u003cp\u003eWhat we don’t know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.\u003c/p\u003e\n", @@ -3481,8 +3483,8 @@ void main() { }); test('UpdateUsersResponse', () { - const jsonExample = - r'''{"users": {"bbb19d9a-ee50-45bc-84e5-0584e79d0c9e":{ + const jsonExample = ''' + {"users": {"bbb19d9a-ee50-45bc-84e5-0584e79d0c9e":{ "id": "bbb19d9a-ee50-45bc-84e5-0584e79d0c9e", "role": "user", "created_at": "2020-01-28T22:17:30.826259Z", @@ -3498,7 +3500,7 @@ void main() { test('ConnectGuestUserResponse', () { const jsonExample = - r'{"user":{"id":"guest-ac612aee-25fe-49fb-b1af-969e41f452a0-wild-breeze-7","role":"guest","created_at":"2020-02-03T10:19:01.538434Z","updated_at":"2020-02-03T10:19:01.539543Z","banned":false,"online":false},"access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiZ3Vlc3QtYWM2MTJhZWUtMjVmZS00OWZiLWIxYWYtOTY5ZTQxZjQ1MmEwLXdpbGQtYnJlZXplLTcifQ.mmoFGu7oJjpFsp7nFN78UbIpO7gowbuIbyoppsuvbXA","duration":"4.66ms"}'; + '''{"user":{"id":"guest-ac612aee-25fe-49fb-b1af-969e41f452a0-wild-breeze-7","role":"guest","created_at":"2020-02-03T10:19:01.538434Z","updated_at":"2020-02-03T10:19:01.539543Z","banned":false,"online":false},"access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiZ3Vlc3QtYWM2MTJhZWUtMjVmZS00OWZiLWIxYWYtOTY5ZTQxZjQ1MmEwLXdpbGQtYnJlZXplLTcifQ.mmoFGu7oJjpFsp7nFN78UbIpO7gowbuIbyoppsuvbXA","duration":"4.66ms"}'''; final response = ConnectGuestUserResponse.fromJson(json.decode(jsonExample)); expect(response.user, isA()); @@ -3506,7 +3508,8 @@ void main() { }); test('GetMessagesByIdResponse', () { - const jsonExample = r'''{"messages":[{ + const jsonExample = r''' + {"messages":[{ "id": "c6076f11-7768-4a04-bdf2-c43dddd6d666", "text": "What we don't know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.", "html": "\u003cp\u003eWhat we don’t know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.\u003c/p\u003e\n", @@ -3537,7 +3540,8 @@ void main() { }); test('SendActionResponse', () { - const jsonExample = r'''{"message":{ + const jsonExample = r''' + {"message":{ "id": "c6076f11-7768-4a04-bdf2-c43dddd6d666", "text": "What we don't know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.", "html": "\u003cp\u003eWhat we don’t know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.\u003c/p\u003e\n", @@ -3567,7 +3571,8 @@ void main() { }); test('UpdateMessageResponse', () { - const jsonExample = r'''{"message":{ + const jsonExample = r''' + {"message":{ "id": "c6076f11-7768-4a04-bdf2-c43dddd6d666", "text": "What we don't know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.", "html": "\u003cp\u003eWhat we don’t know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.\u003c/p\u003e\n", @@ -3597,7 +3602,8 @@ void main() { }); test('SendMessageResponse', () { - const jsonExample = r'''{"message":{ + const jsonExample = r''' + {"message":{ "id": "c6076f11-7768-4a04-bdf2-c43dddd6d666", "text": "What we don't know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.", "html": "\u003cp\u003eWhat we don’t know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.\u003c/p\u003e\n", @@ -3627,7 +3633,8 @@ void main() { }); test('GetMessageResponse', () { - const jsonExample = r'''{"message":{ + const jsonExample = r''' + {"message":{ "id": "c6076f11-7768-4a04-bdf2-c43dddd6d666", "text": "What we don't know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.", "html": "\u003cp\u003eWhat we don’t know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.\u003c/p\u003e\n", @@ -3657,7 +3664,8 @@ void main() { }); test('UpdateChannelResponse', () { - const jsonExample = r'''{"message":{ + const jsonExample = r''' + {"message":{ "id": "c6076f11-7768-4a04-bdf2-c43dddd6d666", "text": "What we don't know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.", "html": "\u003cp\u003eWhat we don’t know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.\u003c/p\u003e\n", @@ -3770,7 +3778,8 @@ void main() { }); test('InviteMembersResponse', () { - const jsonExample = r'''{"message":{ + const jsonExample = r''' + {"message":{ "id": "c6076f11-7768-4a04-bdf2-c43dddd6d666", "text": "What we don't know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.", "html": "\u003cp\u003eWhat we don’t know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.\u003c/p\u003e\n", @@ -3883,7 +3892,8 @@ void main() { }); test('RemoveMembersResponse', () { - const jsonExample = r'''{"message":{ + const jsonExample = r''' + {"message":{ "id": "c6076f11-7768-4a04-bdf2-c43dddd6d666", "text": "What we don't know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.", "html": "\u003cp\u003eWhat we don’t know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.\u003c/p\u003e\n", @@ -3996,7 +4006,8 @@ void main() { }); test('AddMembersResponse', () { - const jsonExample = r'''{"message":{ + const jsonExample = r''' + {"message":{ "id": "c6076f11-7768-4a04-bdf2-c43dddd6d666", "text": "What we don't know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.", "html": "\u003cp\u003eWhat we don’t know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.\u003c/p\u003e\n", @@ -4109,7 +4120,8 @@ void main() { }); test('AcceptInviteResponse', () { - const jsonExample = r'''{"message":{ + const jsonExample = r''' + {"message":{ "id": "c6076f11-7768-4a04-bdf2-c43dddd6d666", "text": "What we don't know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.", "html": "\u003cp\u003eWhat we don’t know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.\u003c/p\u003e\n", @@ -4222,7 +4234,8 @@ void main() { }); test('RejectInviteResponse', () { - const jsonExample = r'''{"message":{ + const jsonExample = r''' + {"message":{ "id": "c6076f11-7768-4a04-bdf2-c43dddd6d666", "text": "What we don't know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.", "html": "\u003cp\u003eWhat we don’t know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.\u003c/p\u003e\n", diff --git a/packages/stream_chat/test/src/api/web_socket_stub_test.dart b/packages/stream_chat/test/src/api/web_socket_stub_test.dart new file mode 100644 index 00000000..7f5bf4b1 --- /dev/null +++ b/packages/stream_chat/test/src/api/web_socket_stub_test.dart @@ -0,0 +1,11 @@ +import 'package:test/test.dart'; +import 'package:stream_chat/src/api/web_socket_channel_stub.dart'; + +void main() { + test('src/api/web_socket_stub_test', () { + expect( + () => connectWebSocket('fakeurl'), + throwsA(isA()), + ); + }); +} diff --git a/packages/stream_chat/test/src/api/websocket_test.dart b/packages/stream_chat/test/src/api/websocket_test.dart index 238cc850..b9c897a6 100644 --- a/packages/stream_chat/test/src/api/websocket_test.dart +++ b/packages/stream_chat/test/src/api/websocket_test.dart @@ -1,7 +1,7 @@ import 'dart:async'; import 'package:logging/logging.dart'; -import 'package:mockito/mockito.dart'; +import 'package:mocktail/mocktail.dart'; import 'package:stream_chat/src/api/connection_status.dart'; import 'package:stream_chat/src/api/websocket.dart'; import 'package:stream_chat/src/models/event.dart'; @@ -12,14 +12,12 @@ import 'package:web_socket_channel/web_socket_channel.dart'; class Functions { WebSocketChannel connectFunc( - String url, { - Iterable protocols, - Map headers, - Duration pingInterval, + String? url, { + Iterable? protocols, }) => - null; + WebSocketChannel.connect(Uri()); - void handleFunc(Event event) => null; + void handleFunc(Event event) {} } class MockFunctions extends Mock implements Functions {} @@ -28,35 +26,35 @@ class MockWSChannel extends Mock implements WebSocketChannel {} class MockWSSink extends Mock implements WebSocketSink {} +class FakeEvent extends Fake implements Event {} + void main() { group('src/api/websocket', () { - test('should connect with correct parameters', () async { - final ConnectWebSocket connectFunc = MockFunctions().connectFunc; + setUpAll(() { + registerFallbackValue(FakeEvent()); + }); + test('should connect with correct parameters', () async { + final connectFunc = MockFunctions().connectFunc; final ws = WebSocket( baseUrl: 'baseurl', user: User(id: 'testid'), logger: Logger('ws'), connectParams: {'test': 'true'}, connectPayload: {'payload': 'test'}, - handler: (e) { - print(e); - }, + handler: print, connectFunc: connectFunc, ); - final mockWSChannel = MockWSChannel(); - final streamController = StreamController.broadcast(); - const computedUrl = 'wss://baseurl/connect?test=true&json=%7B%22payload%22%3A%22test%22%2C%22user_details%22%3A%7B%22id%22%3A%22testid%22%7D%7D'; - when(connectFunc(computedUrl)).thenAnswer((_) => mockWSChannel); - when(mockWSChannel.sink).thenAnswer((_) => MockWSSink()); - when(mockWSChannel.stream).thenAnswer((_) { - return streamController.stream; - }); + when(() => connectFunc(computedUrl)).thenAnswer((_) => mockWSChannel); + when(() => mockWSChannel.sink).thenAnswer((_) => MockWSSink()); + when(() => mockWSChannel.stream).thenAnswer( + (_) => streamController.stream, + ); final timer = Timer.periodic( const Duration(milliseconds: 100), @@ -65,7 +63,7 @@ void main() { await ws.connect(); - verify(connectFunc(computedUrl)).called(1); + verify(() => connectFunc(computedUrl)).called(1); expect(ws.connectionStatus, ConnectionStatus.connected); await streamController.close(); @@ -75,8 +73,7 @@ void main() { test('should connect with correct parameters and handle events', () async { final handleFunc = MockFunctions().handleFunc; - final ConnectWebSocket connectFunc = MockFunctions().connectFunc; - + final connectFunc = MockFunctions().connectFunc; final ws = WebSocket( baseUrl: 'baseurl', user: User(id: 'testid'), @@ -86,27 +83,21 @@ void main() { handler: handleFunc, connectFunc: connectFunc, ); - final mockWSChannel = MockWSChannel(); - - final StreamController streamController = - StreamController.broadcast(); - - final computedUrl = + final streamController = StreamController.broadcast(); + const computedUrl = 'wss://baseurl/connect?test=true&json=%7B%22payload%22%3A%22test%22%2C%22user_details%22%3A%7B%22id%22%3A%22testid%22%7D%7D'; - when(connectFunc(computedUrl)).thenAnswer((_) => mockWSChannel); - when(mockWSChannel.sink).thenAnswer((_) => MockWSSink()); - when(mockWSChannel.stream).thenAnswer((_) { - return streamController.stream; - }); + when(() => connectFunc(computedUrl)).thenAnswer((_) => mockWSChannel); + when(() => mockWSChannel.sink).thenAnswer((_) => MockWSSink()); + when(() => mockWSChannel.stream).thenAnswer((_) => streamController.stream); final connect = ws.connect().then((_) { streamController.sink.add('{}'); - return Future.delayed(Duration(milliseconds: 200)); + return Future.delayed(const Duration(milliseconds: 200)); }).then((value) { - verify(connectFunc(computedUrl)).called(1); - verify(handleFunc(any)).called(greaterThan(0)); + verify(() => connectFunc(computedUrl)).called(1); + verify(() => handleFunc(any())).called(greaterThan(0)); return streamController.close(); }); @@ -118,9 +109,7 @@ void main() { test('should close correctly the controller', () async { final handleFunc = MockFunctions().handleFunc; - - final ConnectWebSocket connectFunc = MockFunctions().connectFunc; - + final connectFunc = MockFunctions().connectFunc; final ws = WebSocket( baseUrl: 'baseurl', user: User(id: 'testid'), @@ -130,27 +119,23 @@ void main() { handler: handleFunc, connectFunc: connectFunc, ); - final mockWSChannel = MockWSChannel(); - - final StreamController streamController = - StreamController.broadcast(); - - final computedUrl = + final streamController = StreamController.broadcast(); + const computedUrl = 'wss://baseurl/connect?test=true&json=%7B%22payload%22%3A%22test%22%2C%22user_details%22%3A%7B%22id%22%3A%22testid%22%7D%7D'; - when(connectFunc(computedUrl)).thenAnswer((_) => mockWSChannel); - when(mockWSChannel.sink).thenAnswer((_) => MockWSSink()); - when(mockWSChannel.stream).thenAnswer((_) { - return streamController.stream; - }); + final mockWSSink = MockWSSink(); + when(() => mockWSChannel.sink).thenAnswer((_) => mockWSSink); + when(() => mockWSSink.close(any(), any())).thenAnswer((_) async => null); + when(() => connectFunc(computedUrl)).thenAnswer((_) => mockWSChannel); + when(() => mockWSChannel.stream).thenAnswer((_) => streamController.stream); final connect = ws.connect().then((_) { streamController.sink.add('{}'); - return Future.delayed(Duration(milliseconds: 200)); + return Future.delayed(const Duration(milliseconds: 200)); }).then((value) { - verify(connectFunc(computedUrl)).called(1); - verify(handleFunc(any)).called(greaterThan(0)); + verify(() => connectFunc(computedUrl)).called(1); + verify(() => handleFunc(any())).called(greaterThan(0)); return streamController.close(); }); @@ -159,10 +144,11 @@ void main() { return connect; }); + test('should close correctly the controller while connecting', () async { final handleFunc = MockFunctions().handleFunc; - final ConnectWebSocket connectFunc = MockFunctions().connectFunc; + final connectFunc = MockFunctions().connectFunc; final ws = WebSocket( baseUrl: 'baseurl', @@ -176,31 +162,30 @@ void main() { final mockWSChannel = MockWSChannel(); - final StreamController streamController = - StreamController.broadcast(); + final streamController = StreamController.broadcast(); - final computedUrl = + const computedUrl = 'wss://baseurl/connect?test=true&json=%7B%22payload%22%3A%22test%22%2C%22user_details%22%3A%7B%22id%22%3A%22testid%22%7D%7D'; - when(connectFunc(computedUrl)).thenAnswer((_) => mockWSChannel); - when(mockWSChannel.sink).thenAnswer((_) => MockWSSink()); - when(mockWSChannel.stream).thenAnswer((_) { - return streamController.stream; - }); + final mockWSSink = MockWSSink(); + when(() => mockWSChannel.sink).thenAnswer((_) => mockWSSink); + when(() => mockWSSink.close(any(), any())).thenAnswer((_) async => null); + when(() => connectFunc(computedUrl)).thenAnswer((_) => mockWSChannel); + when(() => mockWSChannel.stream).thenAnswer((_) => streamController.stream); ws.connect(); await ws.disconnect(); streamController.add('{}'); - verify(connectFunc(computedUrl)).called(1); - verifyNever(handleFunc(any)); + verify(() => connectFunc(computedUrl)).called(1); + verifyNever(() => handleFunc(any())); + + addTearDown(streamController.close); }); test('should run correctly health check', () async { final handleFunc = MockFunctions().handleFunc; - - final ConnectWebSocket connectFunc = MockFunctions().connectFunc; - + final connectFunc = MockFunctions().connectFunc; final ws = WebSocket( baseUrl: 'baseurl', user: User(id: 'testid'), @@ -210,32 +195,28 @@ void main() { handler: handleFunc, connectFunc: connectFunc, ); - final mockWSChannel = MockWSChannel(); final mockWSSink = MockWSSink(); - - final StreamController streamController = - StreamController.broadcast(); - - final computedUrl = + final streamController = StreamController.broadcast(); + const computedUrl = 'wss://baseurl/connect?test=true&json=%7B%22payload%22%3A%22test%22%2C%22user_details%22%3A%7B%22id%22%3A%22testid%22%7D%7D'; - when(connectFunc(computedUrl)).thenAnswer((_) => mockWSChannel); - when(mockWSChannel.stream).thenAnswer((_) { - return streamController.stream; - }); - when(mockWSChannel.sink).thenReturn(mockWSSink); + when(() => connectFunc(computedUrl)).thenAnswer((_) => mockWSChannel); + when(() => mockWSChannel.stream).thenAnswer((_) => streamController.stream); + when(() => mockWSChannel.sink).thenAnswer((_) => mockWSSink); + when(() => mockWSSink.close(any(), any())).thenAnswer((_) async => null); final timer = Timer.periodic( - Duration(milliseconds: 1000), + const Duration(milliseconds: 1000), (_) => streamController.sink.add('{}'), ); final connect = ws.connect().then((_) { streamController.sink.add('{}'); - return Future.delayed(Duration(milliseconds: 200)); + return Future.delayed(const Duration(milliseconds: 200)); }).then((value) async { - verify(mockWSSink.add("{'type': 'health.check'}")).called(greaterThan(0)); + verify(() => mockWSSink.add("{'type': 'health.check'}")) + .called(greaterThan(0)); timer.cancel(); await streamController.close(); @@ -249,9 +230,7 @@ void main() { test('should run correctly reconnection check', () async { final handleFunc = MockFunctions().handleFunc; - - final ConnectWebSocket connectFunc = MockFunctions().connectFunc; - + final connectFunc = MockFunctions().connectFunc; Logger.root.level = Level.ALL; final ws = WebSocket( baseUrl: 'baseurl', @@ -262,34 +241,29 @@ void main() { handler: handleFunc, connectFunc: connectFunc, reconnectionMonitorTimeout: 1, - reconnectionMonitorInterval: 1, ); - final mockWSChannel = MockWSChannel(); final mockWSSink = MockWSSink(); - - StreamController streamController = - StreamController.broadcast(); - - final computedUrl = + var streamController = StreamController.broadcast(); + const computedUrl = 'wss://baseurl/connect?test=true&json=%7B%22payload%22%3A%22test%22%2C%22user_details%22%3A%7B%22id%22%3A%22testid%22%7D%7D'; - when(connectFunc(computedUrl)).thenAnswer((_) => mockWSChannel); - when(mockWSChannel.stream).thenAnswer((_) { - return streamController.stream; - }); - when(mockWSChannel.sink).thenReturn(mockWSSink); + when(() => connectFunc(computedUrl)).thenAnswer((_) => mockWSChannel); + when(() => mockWSChannel.stream).thenAnswer((_) => streamController.stream); + when(() => mockWSChannel.sink).thenAnswer((_) => mockWSSink); + when(() => mockWSSink.close(any(), any())).thenAnswer((_) async => null); final connect = ws.connect().then((_) { streamController.sink.add('{}'); streamController.close(); streamController = StreamController.broadcast(); streamController.sink.add('{}'); - return Future.delayed(Duration(milliseconds: 200)); + return Future.delayed(const Duration(milliseconds: 200)); }).then((value) async { - verify(mockWSSink.add("{'type': 'health.check'}")).called(greaterThan(0)); + verify(() => mockWSSink.add("{'type': 'health.check'}")) + .called(greaterThan(0)); - verify(connectFunc(computedUrl)).called(2); + verify(() => connectFunc(computedUrl)).called(2); await streamController.close(); return mockWSSink.close(); @@ -302,9 +276,7 @@ void main() { test('should close correctly the controller', () async { final handleFunc = MockFunctions().handleFunc; - - final ConnectWebSocket connectFunc = MockFunctions().connectFunc; - + final connectFunc = MockFunctions().connectFunc; final ws = WebSocket( baseUrl: 'baseurl', user: User(id: 'testid'), @@ -314,28 +286,23 @@ void main() { handler: handleFunc, connectFunc: connectFunc, ); - final mockWSChannel = MockWSChannel(); final mockWSSink = MockWSSink(); - - final StreamController streamController = - StreamController.broadcast(); - - final computedUrl = + final streamController = StreamController.broadcast(); + const computedUrl = 'wss://baseurl/connect?test=true&json=%7B%22payload%22%3A%22test%22%2C%22user_details%22%3A%7B%22id%22%3A%22testid%22%7D%7D'; - when(connectFunc(computedUrl)).thenAnswer((_) => mockWSChannel); - when(mockWSChannel.stream).thenAnswer((_) { - return streamController.stream; - }); - when(mockWSChannel.sink).thenReturn(mockWSSink); + when(() => connectFunc(computedUrl)).thenAnswer((_) => mockWSChannel); + when(() => mockWSChannel.stream).thenAnswer((_) => streamController.stream); + when(() => mockWSChannel.sink).thenAnswer((_) => mockWSSink); + when(() => mockWSSink.close(any(), any())).thenAnswer((_) async => null); final connect = ws.connect().then((_) { streamController.sink.add('{}'); - return Future.delayed(Duration(milliseconds: 200)); + return Future.delayed(const Duration(milliseconds: 200)); }).then((value) async { await ws.disconnect(); - verify(mockWSSink.close()).called(greaterThan(0)); + verify(mockWSSink.close).called(greaterThan(0)); await streamController.close(); await mockWSSink.close(); @@ -347,42 +314,35 @@ void main() { }); test('should throw an error', () async { - final ConnectWebSocket connectFunc = MockFunctions().connectFunc; - + final connectFunc = MockFunctions().connectFunc; final ws = WebSocket( baseUrl: 'baseurl', user: User(id: 'testid'), logger: Logger('ws'), connectParams: {'test': 'true'}, connectPayload: {'payload': 'test'}, - handler: (e) { - print(e); - }, + handler: print, connectFunc: connectFunc, ); - final mockWSChannel = MockWSChannel(); - final streamController = StreamController.broadcast(); - - final computedUrl = + const computedUrl = 'wss://baseurl/connect?test=true&json=%7B%22payload%22%3A%22test%22%2C%22user_details%22%3A%7B%22id%22%3A%22testid%22%7D%7D'; - when(connectFunc(computedUrl)).thenAnswer((_) => mockWSChannel); - when(mockWSChannel.sink).thenAnswer((_) => MockWSSink()); - when(mockWSChannel.stream).thenAnswer((_) { - return streamController.stream; - }); + when(() => connectFunc(computedUrl)).thenAnswer((_) => mockWSChannel); + when(() => mockWSChannel.sink).thenAnswer((_) => MockWSSink()); + when(() => mockWSChannel.stream).thenAnswer((_) => streamController.stream); Future.delayed( - Duration(milliseconds: 1000), + const Duration(milliseconds: 1000), () => streamController.sink.addError('test error'), ); try { expect(await ws.connect(), throwsA(isA())); } catch (e) { - verify(connectFunc(computedUrl)).called(greaterThanOrEqualTo(1)); + verify(() => connectFunc(computedUrl)).called(greaterThanOrEqualTo(1)); + streamController.close(); } }); } diff --git a/packages/stream_chat/test/src/client_test.dart b/packages/stream_chat/test/src/client_test.dart index 21b9d09b..b2a5bbac 100644 --- a/packages/stream_chat/test/src/client_test.dart +++ b/packages/stream_chat/test/src/client_test.dart @@ -1,34 +1,44 @@ import 'dart:async'; import 'dart:convert'; +import 'dart:typed_data'; import 'package:dio/dio.dart'; import 'package:dio/native_imp.dart'; import 'package:logging/logging.dart'; -import 'package:mockito/mockito.dart'; +import 'package:mocktail/mocktail.dart'; import 'package:stream_chat/src/api/requests.dart'; import 'package:stream_chat/src/client.dart'; import 'package:stream_chat/src/exceptions.dart'; +import 'package:stream_chat/src/models/channel_model.dart'; +import 'package:stream_chat/src/models/filter.dart'; import 'package:stream_chat/src/models/message.dart'; import 'package:stream_chat/src/models/user.dart'; -import 'package:stream_chat/src/models/channel_model.dart'; import 'package:test/test.dart'; class MockDio extends Mock implements DioForNative {} +class FakeRequestOptions extends Fake implements RequestOptions {} + class MockHttpClientAdapter extends Mock implements HttpClientAdapter {} class Functions { - Future tokenProvider(String userId) => null; + Future tokenProvider(String userId) async => ''; } class MockFunctions extends Mock implements Functions {} void main() { group('src/client', () { - group('constructor', () { - final List log = []; + setUpAll(() { + registerFallbackValue(FakeRequestOptions()); + registerFallbackValue>(const Stream.empty()); + registerFallbackValue>(Future.value()); + }); - overridePrint(testFn()) => () { + group('constructor', () { + final log = []; + + dynamic overridePrint(testFn()) => () { log.clear(); final spec = ZoneSpecification(print: (_, __, ___, String msg) { // Add to log instead of printing to stdout @@ -37,9 +47,7 @@ void main() { return Zone.current.fork(specification: spec).run(testFn); }; - tearDown(() { - log.clear(); - }); + tearDown(log.clear); test('should create the object correctly', () { final client = StreamChatClient('api-key'); @@ -52,14 +60,14 @@ void main() { }); test('should create the object correctly', overridePrint(() { - final LogHandlerFunction logHandler = (LogRecord record) { + void logHandler(LogRecord record) { print(record.message); - }; + } final client = StreamChatClient( 'api-key', - connectTimeout: Duration(seconds: 10), - receiveTimeout: Duration(seconds: 12), + connectTimeout: const Duration(seconds: 10), + receiveTimeout: const Duration(seconds: 12), logLevel: Level.INFO, baseURL: 'test.com', logHandlerFunction: logHandler, @@ -74,13 +82,15 @@ void main() { client.logger.warning('test'); client.logger.config('test config'); - expect([log[log.length - 2], log[log.length - 1]], - ['instantiating new client', 'test']); + expect( + [log[log.length - 2], log[log.length - 1]], + ['instantiating new client', 'test'], + ); })); test('Channel', () { final client = StreamChatClient('test'); - final Map data = {'test': 1}; + final data = {'test': 1}; final channelClient = client.channel('type', id: 'id', extraData: data); expect(channelClient.type, 'type'); expect(channelClient.id, 'id'); @@ -91,71 +101,71 @@ void main() { test('should pass right default parameters', () async { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); - - final client = StreamChatClient( - 'api-key', - httpClient: mockDio, - ); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); + final client = StreamChatClient('api-key', httpClient: mockDio); final queryParams = { 'payload': json.encode({ - "filter_conditions": null, - "sort": null, - "state": true, - "watch": true, - "presence": false, - "limit": 10, - "offset": 0, + 'filter_conditions': null, + 'sort': null, + 'state': true, + 'watch': true, + 'presence': false, + 'limit': 10, + 'offset': 0, }), }; - when(mockDio.get('/channels', queryParameters: queryParams)) - .thenAnswer((_) async => Response(data: '{}', statusCode: 200)); + when( + () => mockDio.get('/channels', queryParameters: queryParams), + ).thenAnswer( + (_) async => Response( + data: '{}', + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), + ); - await client.queryChannelsOnline(filter: null, waitForConnect: false); + await client.queryChannelsOnline(waitForConnect: false); - verify(mockDio.get('/channels', queryParameters: queryParams)) + verify(() => + mockDio.get('/channels', queryParameters: queryParams)) .called(1); }); test('should pass right parameters', () async { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); - final client = StreamChatClient( - 'api-key', - httpClient: mockDio, - ); - - final queryFilter = { - "id": { - "\$in": ["test"], - }, - }; + final client = StreamChatClient('api-key', httpClient: mockDio); + final queryFilter = Filter.in_('id', const ['test']); final sortOptions = >[]; - final options = {"state": false, "watch": false, "presence": true}; - final paginationParams = PaginationParams( - limit: 10, - offset: 2, - ); + final options = {'state': false, 'watch': false, 'presence': true}; + const paginationParams = PaginationParams(offset: 2); final queryParams = { 'payload': json.encode({ - "filter_conditions": queryFilter, - "sort": sortOptions, + 'filter_conditions': queryFilter, + 'sort': sortOptions, } ..addAll(options) - ..addAll(paginationParams.toJson())), + ..addAll(paginationParams + .toJson() + .map((key, value) => MapEntry(key, value as Object)))), }; - when(mockDio.get('/channels', queryParameters: queryParams)) - .thenAnswer((_) async { - return Response(data: '{"channels":[]}', statusCode: 200); - }); + when( + () => mockDio.get('/channels', queryParameters: queryParams), + ).thenAnswer( + (_) async => Response( + data: '{"channels":[]}', + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), + ); await client.queryChannelsOnline( filter: queryFilter, @@ -165,7 +175,8 @@ void main() { waitForConnect: false, ); - verify(mockDio.get('/channels', queryParameters: queryParams)) + verify(() => + mockDio.get('/channels', queryParameters: queryParams)) .called(1); }); }); @@ -174,22 +185,12 @@ void main() { test('should pass right default parameters', () async { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); - - final client = StreamChatClient( - 'api-key', - httpClient: mockDio, - ); - - final filter = { - 'cid': { - r'$in': ['messaging:testId'] - } - }; + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); + final client = StreamChatClient('api-key', httpClient: mockDio); + final filter = Filter.in_('cid', const ['messaging:testId']); const query = 'hello'; - final queryParams = { 'payload': json.encode({ 'filter_conditions': filter, @@ -197,34 +198,33 @@ void main() { }), }; - when(mockDio.get('/search', queryParameters: queryParams)) - .thenAnswer((_) async => Response(data: '{}', statusCode: 200)); + when( + () => mockDio.get('/search', queryParameters: queryParams), + ).thenAnswer( + (_) async => Response( + data: '{}', + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), + ); await client.search(filter, query: query); - verify(mockDio.get('/search', queryParameters: queryParams)) + verify(() => + mockDio.get('/search', queryParameters: queryParams)) .called(1); }); test('should pass right parameters', () async { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); - - final client = StreamChatClient( - 'api-key', - httpClient: mockDio, - ); - - final filters = { - "id": { - "\$in": ["test"], - }, - }; - final sortOptions = [SortOption('name')]; - final query = 'query'; + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); + final client = StreamChatClient('api-key', httpClient: mockDio); + final filters = Filter.in_('id', const ['test']); + const sortOptions = [SortOption('name')]; + const query = 'query'; final queryParams = { 'payload': json.encode({ 'filter_conditions': filters, @@ -235,18 +235,26 @@ void main() { }), }; - when(mockDio.get('/search', queryParameters: queryParams)) - .thenAnswer((_) async => Response(data: '{}', statusCode: 200)); + when( + () => mockDio.get('/search', queryParameters: queryParams), + ).thenAnswer( + (_) async => Response( + data: '{}', + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), + ); await client.search( filters, sort: sortOptions, query: query, - paginationParams: PaginationParams(), + paginationParams: const PaginationParams(), ); - verify(mockDio.get('/search', queryParameters: queryParams)) - .called(1); + verify( + () => mockDio.get('/search', queryParameters: queryParams), + ).called(1); }); }); @@ -254,23 +262,28 @@ void main() { test('addDevice', () async { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); - final client = StreamChatClient( - 'api-key', - httpClient: mockDio, + final client = StreamChatClient('api-key', httpClient: mockDio); + + when( + () => mockDio.post('/devices', data: { + 'id': 'test-id', + 'push_provider': 'firebase', + }), + ).thenAnswer( + (_) async => Response( + data: '{}', + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), ); - when(mockDio.post('/devices', data: { - 'id': 'test-id', - 'push_provider': 'firebase', - })).thenAnswer((_) async => Response(data: '{}', statusCode: 200)); - await client.addDevice('test-id', PushProvider.firebase); verify( - mockDio.post( + () => mockDio.post( '/devices', data: {'id': 'test-id', 'push_provider': 'firebase'}, ), @@ -280,41 +293,53 @@ void main() { test('getDevices', () async { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); - final client = StreamChatClient( - 'api-key', - httpClient: mockDio, + final client = StreamChatClient('api-key', httpClient: mockDio); + + when(() => mockDio.get('/devices')).thenAnswer( + (_) async => Response( + data: '{}', + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), ); - when(mockDio.get('/devices')) - .thenAnswer((_) async => Response(data: '{}', statusCode: 200)); - await client.getDevices(); - verify(mockDio.get('/devices')).called(1); + verify(() => mockDio.get('/devices')).called(1); }); test('removeDevice', () async { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); - final client = StreamChatClient( - 'api-key', - httpClient: mockDio, + final client = StreamChatClient('api-key', httpClient: mockDio); + + when( + () => mockDio.delete( + '/devices', + queryParameters: {'id': 'test-id'}, + ), + ).thenAnswer( + (_) async => Response( + data: '{}', + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), ); - when(mockDio - .delete('/devices', queryParameters: {'id': 'test-id'})) - .thenAnswer((_) async => Response(data: '{}', statusCode: 200)); - await client.removeDevice('test-id'); - verify(mockDio.delete('/devices', - queryParameters: {'id': 'test-id'})).called(1); + verify( + () => mockDio.delete( + '/devices', + queryParameters: {'id': 'test-id'}, + ), + ).called(1); }); }); @@ -324,7 +349,7 @@ void main() { expect( token, - 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoidGVzdCJ9.devtoken', + '''eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoidGVzdCJ9.devtoken''', ); }); @@ -332,62 +357,64 @@ void main() { test('should pass right default parameters', () async { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); - - final client = StreamChatClient( - 'api-key', - httpClient: mockDio, - ); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); + final client = StreamChatClient('api-key', httpClient: mockDio); final queryParams = { 'payload': json.encode({ - "filter_conditions": {}, - "sort": null, - "presence": false, + 'filter_conditions': null, + 'sort': null, + 'presence': false, }), }; - when(mockDio.get('/users', queryParameters: queryParams)) - .thenAnswer( - (_) async => Response(data: '{"users":[]}', statusCode: 200)); + when( + () => mockDio.get('/users', queryParameters: queryParams), + ).thenAnswer( + (_) async => Response( + data: '{"users":[]}', + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), + ); await client.queryUsers(); - verify(mockDio.get('/users', queryParameters: queryParams)) - .called(1); + verify( + () => mockDio.get( + '/users', + queryParameters: queryParams, + ), + ).called(1); }); test('should pass right parameters', () async { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); - final client = StreamChatClient( - 'api-key', - httpClient: mockDio, - ); - - final Map queryFilter = { - "id": { - "\$in": ["test"], - }, - }; - final List sortOptions = []; - final options = {"presence": true}; - - final Map queryParams = { + final client = StreamChatClient('api-key', httpClient: mockDio); + final queryFilter = Filter.in_('id', const ['test']); + const sortOptions = []; + final options = {'presence': true}; + final queryParams = { 'payload': json.encode({ - "filter_conditions": queryFilter, - "sort": sortOptions, + 'filter_conditions': queryFilter, + 'sort': sortOptions, }..addAll(options)), }; - when(mockDio.get('/users', queryParameters: queryParams)) - .thenAnswer((_) async { - return Response(data: '{"users":[]}', statusCode: 200); - }); + when( + () => mockDio.get('/users', queryParameters: queryParams), + ).thenAnswer( + (_) async => Response( + data: '{"users":[]}', + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), + ); await client.queryUsers( filter: queryFilter, @@ -395,7 +422,8 @@ void main() { options: options, ); - verify(mockDio.get('/users', queryParameters: queryParams)) + verify(() => + mockDio.get('/users', queryParameters: queryParams)) .called(1); }); }); @@ -404,34 +432,37 @@ void main() { test('connectUser should throw exception', () async { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); - final client = StreamChatClient( - 'api-key', - httpClient: mockDio, + final client = StreamChatClient('api-key', httpClient: mockDio); + + when( + () => mockDio.post( + '/moderation/flag', + data: {'target_user_id': 'test-id'}, + ), + ).thenAnswer( + (_) async => Response( + data: '{}', + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), ); - when(mockDio.post('/moderation/flag', - data: {'target_user_id': 'test-id'})) - .thenAnswer((_) async => Response(data: '{}', statusCode: 200)); - await client.flagUser('test-id'); - verify(mockDio.post('/moderation/flag', + verify(() => mockDio.post('/moderation/flag', data: {'target_user_id': 'test-id'})).called(1); }); test('flagUser', () async { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); - final client = StreamChatClient( - 'api-key', - httpClient: mockDio, - ); + final client = StreamChatClient('api-key', httpClient: mockDio); expect(() => client.connectUserWithProvider(User(id: 'test-id')), throwsA(isA())); @@ -440,60 +471,62 @@ void main() { test('unflagUser', () async { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); - final client = StreamChatClient( - 'api-key', - httpClient: mockDio, + final client = StreamChatClient('api-key', httpClient: mockDio); + + when( + () => mockDio.post( + '/moderation/unflag', + data: {'target_user_id': 'test-id'}, + ), + ).thenAnswer( + (_) async => Response( + data: '{}', + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), ); - when(mockDio.post('/moderation/unflag', - data: {'target_user_id': 'test-id'})) - .thenAnswer((_) async => Response(data: '{}', statusCode: 200)); - await client.unflagUser('test-id'); - verify(mockDio.post('/moderation/unflag', + verify(() => mockDio.post('/moderation/unflag', data: {'target_user_id': 'test-id'})).called(1); }); test('updateUser', () async { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); - - final client = StreamChatClient( - 'api-key', - httpClient: mockDio, - ); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); + final client = StreamChatClient('api-key', httpClient: mockDio); final user = User(id: 'test-id'); - final data = { 'users': {user.id: user.toJson()}, }; - when(mockDio.post('/users', data: data)) - .thenAnswer((_) async => Response(data: '{}', statusCode: 200)); + when(() => mockDio.post('/users', data: data)).thenAnswer( + (_) async => Response( + data: '{}', + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), + ); await client.updateUser(user); - verify(mockDio.post('/users', data: data)).called(1); + verify(() => mockDio.post('/users', data: data)).called(1); }); test('updateUsers', () async { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); - - final client = StreamChatClient( - 'api-key', - httpClient: mockDio, - ); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); + final client = StreamChatClient('api-key', httpClient: mockDio); final user = User(id: 'test-id'); final user2 = User(id: 'test-id2'); @@ -504,53 +537,70 @@ void main() { }, }; - when(mockDio.post('/users', data: data)) - .thenAnswer((_) async => Response(data: '{}', statusCode: 200)); + when(() => mockDio.post('/users', data: data)).thenAnswer( + (_) async => Response( + data: '{}', + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), + ); await client.updateUsers([user, user2]); - verify(mockDio.post('/users', data: data)).called(1); + verify(() => mockDio.post('/users', data: data)).called(1); }); test('banUser', () async { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); - final client = StreamChatClient( - 'api-key', - httpClient: mockDio, + final client = StreamChatClient('api-key', httpClient: mockDio); + + when( + () => mockDio.post( + '/moderation/ban', + data: {'test': true, 'target_user_id': 'test-id'}, + ), + ).thenAnswer( + (_) async => Response( + data: '{}', + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), ); - when(mockDio.post('/moderation/ban', - data: {'test': true, 'target_user_id': 'test-id'})) - .thenAnswer((_) async => Response(data: '{}', statusCode: 200)); - await client.banUser('test-id', {'test': true}); - verify(mockDio.post('/moderation/ban', + verify(() => mockDio.post('/moderation/ban', data: {'test': true, 'target_user_id': 'test-id'})).called(1); }); test('unbanUser', () async { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); - final client = StreamChatClient( - 'api-key', - httpClient: mockDio, + final client = StreamChatClient('api-key', httpClient: mockDio); + + when( + () => mockDio.delete( + '/moderation/ban', + queryParameters: {'test': true, 'target_user_id': 'test-id'}, + ), + ).thenAnswer( + (_) async => Response( + data: '{}', + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), ); - when(mockDio.delete('/moderation/ban', - queryParameters: {'test': true, 'target_user_id': 'test-id'})) - .thenAnswer((_) async => Response(data: '{}', statusCode: 200)); - await client.unbanUser('test-id', {'test': true}); - verify(mockDio.delete('/moderation/ban', + verify(() => mockDio.delete('/moderation/ban', queryParameters: {'test': true, 'target_user_id': 'test-id'})) .called(1); }); @@ -558,42 +608,54 @@ void main() { test('muteUser', () async { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); - final client = StreamChatClient( - 'api-key', - httpClient: mockDio, + final client = StreamChatClient('api-key', httpClient: mockDio); + + when( + () => mockDio.post( + '/moderation/mute', + data: {'target_id': 'test-id'}, + ), + ).thenAnswer( + (_) async => Response( + data: '{}', + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), ); - when(mockDio.post('/moderation/mute', - data: {'target_id': 'test-id'})) - .thenAnswer((_) async => Response(data: '{}', statusCode: 200)); - await client.muteUser('test-id'); - verify(mockDio.post('/moderation/mute', + verify(() => mockDio.post('/moderation/mute', data: {'target_id': 'test-id'})).called(1); }); test('unmuteUser', () async { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); - final client = StreamChatClient( - 'api-key', - httpClient: mockDio, + final client = StreamChatClient('api-key', httpClient: mockDio); + + when( + () => mockDio.post( + '/moderation/unmute', + data: {'target_id': 'test-id'}, + ), + ).thenAnswer( + (_) async => Response( + data: '{}', + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), ); - when(mockDio.post('/moderation/unmute', - data: {'target_id': 'test-id'})) - .thenAnswer((_) async => Response(data: '{}', statusCode: 200)); - await client.unmuteUser('test-id'); - verify(mockDio.post('/moderation/unmute', + verify(() => mockDio.post('/moderation/unmute', data: {'target_id': 'test-id'})).called(1); }); }); @@ -602,131 +664,150 @@ void main() { test('flagMessage', () async { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); - final client = StreamChatClient( - 'api-key', - httpClient: mockDio, + final client = StreamChatClient('api-key', httpClient: mockDio); + + when( + () => mockDio.post( + '/moderation/flag', + data: {'target_message_id': 'test-id'}, + ), + ).thenAnswer( + (_) async => Response( + data: '{}', + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), ); - when(mockDio.post('/moderation/flag', - data: {'target_message_id': 'test-id'})) - .thenAnswer((_) async => Response(data: '{}', statusCode: 200)); - await client.flagMessage('test-id'); - verify(mockDio.post('/moderation/flag', + verify(() => mockDio.post('/moderation/flag', data: {'target_message_id': 'test-id'})).called(1); }); test('unflagMessage', () async { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); - final client = StreamChatClient( - 'api-key', - httpClient: mockDio, + final client = StreamChatClient('api-key', httpClient: mockDio); + + when( + () => mockDio.post( + '/moderation/unflag', + data: {'target_message_id': 'test-id'}, + ), + ).thenAnswer( + (_) async => Response( + data: '{}', + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), ); - when(mockDio.post('/moderation/unflag', - data: {'target_message_id': 'test-id'})) - .thenAnswer((_) async => Response(data: '{}', statusCode: 200)); - await client.unflagMessage('test-id'); - verify(mockDio.post('/moderation/unflag', + verify(() => mockDio.post('/moderation/unflag', data: {'target_message_id': 'test-id'})).called(1); }); test('updateMessage', () async { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); - - final client = StreamChatClient( - 'api-key', - httpClient: mockDio, - ); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); + final client = StreamChatClient('api-key', httpClient: mockDio); final message = Message( id: 'test', - updatedAt: DateTime.now(), ); - when(mockDio.post( - '/messages/${message.id}', - data: {'message': anything}, - )).thenAnswer((_) async => Response(data: '{}', statusCode: 200)); + when( + () => mockDio.post( + '/messages/${message.id}', + data: {'message': anything}, + ), + ).thenAnswer( + (_) async => Response( + data: jsonEncode({'message': message}), + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), + ); await client.updateMessage(message); - verify(mockDio.post('/messages/${message.id}', + verify(() => mockDio.post('/messages/${message.id}', data: {'message': anything})).called(1); }); test('deleteMessage', () async { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); - final client = StreamChatClient( - 'api-key', - httpClient: mockDio, + final client = StreamChatClient('api-key', httpClient: mockDio); + const messageId = 'test'; + + when(() => mockDio.delete('/messages/$messageId')).thenAnswer( + (_) async => Response( + data: '{}', + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), ); - final messageId = 'test'; - - when(mockDio.delete('/messages/$messageId')) - .thenAnswer((_) async => Response(data: '{}', statusCode: 200)); - await client.deleteMessage(Message(id: messageId)); - verify(mockDio.delete('/messages/$messageId')).called(1); + verify(() => mockDio.delete('/messages/$messageId')).called(1); }); test('getMessage', () async { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); - final client = StreamChatClient( - 'api-key', - httpClient: mockDio, + final client = StreamChatClient('api-key', httpClient: mockDio); + const messageId = 'test'; + + when(() => mockDio.get('/messages/$messageId')).thenAnswer( + (_) async => Response( + data: jsonEncode({'message': Message(id: messageId)}), + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), ); - final messageId = 'test'; - - when(mockDio.get('/messages/$messageId')) - .thenAnswer((_) async => Response(data: '{}', statusCode: 200)); - await client.getMessage(messageId); - verify(mockDio.get('/messages/$messageId')).called(1); + verify(() => mockDio.get('/messages/$messageId')).called(1); }); test('markAllRead', () async { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); - final client = StreamChatClient( - 'api-key', - httpClient: mockDio, + final client = StreamChatClient('api-key', httpClient: mockDio); + + when(() => mockDio.post('/channels/read')).thenAnswer( + (_) async => Response( + data: '{}', + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), ); - when(mockDio.post('/channels/read')) - .thenAnswer((_) async => Response(data: '{}', statusCode: 200)); - await client.markAllRead(); - verify(mockDio.post('/channels/read')).called(1); + verify(() => mockDio.post('/channels/read')).called(1); }); }); @@ -735,43 +816,53 @@ void main() { test('should put the correct parameters', () async { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); - final client = StreamChatClient( - 'api-key', - httpClient: mockDio, + final client = StreamChatClient('api-key', httpClient: mockDio); + final queryParams = {'test': 1}; + + when( + () => mockDio.get('/test', queryParameters: queryParams), + ).thenAnswer( + (_) async => Response( + data: '{}', + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), ); - final Map queryParams = { - 'test': 1, - }; - - when(mockDio.get('/test', queryParameters: queryParams)) - .thenAnswer((_) async { - return Response(data: '{}', statusCode: 200); - }); - await client.get('/test', queryParameters: queryParams); - verify(mockDio.get('/test', queryParameters: queryParams)) + verify(() => + mockDio.get('/test', queryParameters: queryParams)) .called(1); }); test('should catch the error', () async { - final dioHttp = Dio(); - final mockHttpClientAdapter = MockHttpClientAdapter(); - dioHttp.httpClientAdapter = mockHttpClientAdapter; + final mockDio = MockDio(); - final client = StreamChatClient( - 'api-key', - httpClient: dioHttp, + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); + + final client = StreamChatClient('api-key', httpClient: mockDio); + + when(() => mockDio.get(any())).thenThrow( + DioError( + type: DioErrorType.response, + response: Response( + data: 'test error', + statusCode: 400, + requestOptions: FakeRequestOptions(), + ), + requestOptions: FakeRequestOptions(), + ), ); - when(mockHttpClientAdapter.fetch(any, any, any)).thenAnswer( - (_) async => ResponseBody.fromString('test error', 400)); - - expect(client.get('/test'), throwsA(ApiError('test error', 400))); + expect( + client.get('/test'), + throwsA(ApiError('test error', 400)), + ); }); }); @@ -779,40 +870,48 @@ void main() { test('should put the correct parameters', () async { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); + + final client = StreamChatClient('api-key', httpClient: mockDio); + final data = {'test': 1}; + + when(() => mockDio.post('/test', data: data)).thenAnswer( + (_) async => Response( + data: '{}', + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), + ); + + await client.post('/test', data: data); + + verify(() => mockDio.post('/test', data: data)).called(1); + }); + + test('should catch the error', () async { + final mockDio = MockDio(); + + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); final client = StreamChatClient( 'api-key', httpClient: mockDio, ); - final Map data = { - 'test': 1, - }; - - when(mockDio.post('/test', data: data)).thenAnswer((_) async { - return Response(data: '{}', statusCode: 200); - }); - - await client.post('/test', data: data); - - verify(mockDio.post('/test', data: data)).called(1); - }); - - test('should catch the error', () async { - final dioHttp = Dio(); - final mockHttpClientAdapter = MockHttpClientAdapter(); - dioHttp.httpClientAdapter = mockHttpClientAdapter; - - final client = StreamChatClient( - 'api-key', - httpClient: dioHttp, + when(() => mockDio.post(any())).thenThrow( + DioError( + type: DioErrorType.response, + response: Response( + data: 'test error', + statusCode: 400, + requestOptions: FakeRequestOptions(), + ), + requestOptions: FakeRequestOptions(), + ), ); - when(mockHttpClientAdapter.fetch(any, any, any)).thenAnswer( - (_) async => ResponseBody.fromString('test error', 400)); - expect(client.post('/test'), throwsA(ApiError('test error', 400))); }); }); @@ -821,40 +920,45 @@ void main() { test('should put the correct parameters', () async { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); - final client = StreamChatClient( - 'api-key', - httpClient: mockDio, + final client = StreamChatClient('api-key', httpClient: mockDio); + final data = {'test': 1}; + + when(() => mockDio.put('/test', data: data)).thenAnswer( + (_) async => Response( + data: '{}', + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), ); - final Map data = { - 'test': 1, - }; - - when(mockDio.put('/test', data: data)).thenAnswer((_) async { - return Response(data: '{}', statusCode: 200); - }); - await client.put('/test', data: data); - verify(mockDio.put('/test', data: data)).called(1); + verify(() => mockDio.put('/test', data: data)).called(1); }); test('should catch the error', () async { - final dioHttp = Dio(); - final mockHttpClientAdapter = MockHttpClientAdapter(); - dioHttp.httpClientAdapter = mockHttpClientAdapter; + final mockDio = MockDio(); - final client = StreamChatClient( - 'api-key', - httpClient: dioHttp, + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); + + final client = StreamChatClient('api-key', httpClient: mockDio); + + when(() => mockDio.put(any())).thenThrow( + DioError( + type: DioErrorType.response, + response: Response( + data: 'test error', + statusCode: 400, + requestOptions: FakeRequestOptions(), + ), + requestOptions: FakeRequestOptions(), + ), ); - when(mockHttpClientAdapter.fetch(any, any, any)).thenAnswer( - (_) async => ResponseBody.fromString('test error', 400)); - expect(client.put('/test'), throwsA(ApiError('test error', 400))); }); }); @@ -863,41 +967,48 @@ void main() { test('should put the correct parameters', () async { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); - final client = StreamChatClient( - 'api-key', - httpClient: mockDio, - ); + final client = StreamChatClient('api-key', httpClient: mockDio); - final Map data = { + final data = { 'test': 1, }; - when(mockDio.patch('/test', data: data)) - .thenAnswer((_) async { - return Response(data: '{}', statusCode: 200); - }); + when(() => mockDio.patch('/test', data: data)).thenAnswer( + (_) async => Response( + data: '{}', + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), + ); await client.patch('/test', data: data); - verify(mockDio.patch('/test', data: data)).called(1); + verify(() => mockDio.patch('/test', data: data)).called(1); }); test('should catch the error', () async { - final dioHttp = Dio(); - final mockHttpClientAdapter = MockHttpClientAdapter(); - dioHttp.httpClientAdapter = mockHttpClientAdapter; + final mockDio = MockDio(); - final client = StreamChatClient( - 'api-key', - httpClient: dioHttp, + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); + + final client = StreamChatClient('api-key', httpClient: mockDio); + + when(() => mockDio.patch(any())).thenThrow( + DioError( + type: DioErrorType.response, + response: Response( + data: 'test error', + statusCode: 400, + requestOptions: FakeRequestOptions(), + ), + requestOptions: FakeRequestOptions(), + ), ); - when(mockHttpClientAdapter.fetch(any, any, any)).thenAnswer( - (_) async => ResponseBody.fromString('test error', 400)); - expect(client.patch('/test'), throwsA(ApiError('test error', 400))); }); }); @@ -906,42 +1017,55 @@ void main() { test('should put the correct parameters', () async { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); final client = StreamChatClient( 'api-key', httpClient: mockDio, ); - final Map queryParams = { + final queryParams = { 'test': 1, }; - when(mockDio.delete('/test', queryParameters: queryParams)) - .thenAnswer((_) async { - return Response(data: '{}', statusCode: 200); - }); + when( + () => mockDio.delete('/test', queryParameters: queryParams), + ).thenAnswer( + (_) async => Response( + data: '{}', + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), + ); await client.delete('/test', queryParameters: queryParams); - verify(mockDio.delete('/test', queryParameters: queryParams)) + verify(() => + mockDio.delete('/test', queryParameters: queryParams)) .called(1); }); test('should catch the error', () async { - final dioHttp = Dio(); - final mockHttpClientAdapter = MockHttpClientAdapter(); - dioHttp.httpClientAdapter = mockHttpClientAdapter; + final mockDio = MockDio(); - final client = StreamChatClient( - 'api-key', - httpClient: dioHttp, + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); + + final client = StreamChatClient('api-key', httpClient: mockDio); + + when(() => mockDio.delete(any())).thenThrow( + DioError( + type: DioErrorType.response, + response: Response( + data: 'test error', + statusCode: 400, + requestOptions: FakeRequestOptions(), + ), + requestOptions: FakeRequestOptions(), + ), ); - when(mockHttpClientAdapter.fetch(any, any, any)).thenAnswer( - (_) async => ResponseBody.fromString('test error', 400)); - expect(client.delete('/test'), throwsA(ApiError('test error', 400))); }); }); @@ -949,8 +1073,8 @@ void main() { group('pin message', () { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); final client = StreamChatClient( 'api-key', @@ -966,33 +1090,48 @@ void main() { }); test('should complete successfully', () async { - final timeout = 30; + const timeout = 30; final message = Message(text: 'Hello'); - when(mockDio.post( - '/messages/${message.id}', - data: anything, - )).thenAnswer((_) async => Response(data: '{}', statusCode: 200)); + when( + () => mockDio.post( + '/messages/${message.id}', + data: anything, + ), + ).thenAnswer( + (_) async => Response( + data: jsonEncode({'message': message}), + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), + ); await client.pinMessage(message, timeout); - verify(mockDio.post('/messages/${message.id}', + verify(() => mockDio.post('/messages/${message.id}', data: {'message': anything})).called(1); }); test('should unpin message successfully', () async { final message = Message(text: 'Hello'); - when(mockDio.post( - '/messages/${message.id}', - data: anything, - )).thenAnswer((_) async => Response(data: '{}', statusCode: 200)); + when( + () => mockDio.post( + '/messages/${message.id}', + data: anything, + ), + ).thenAnswer( + (_) async => Response( + data: jsonEncode({'message': message}), + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), + ); await client.unpinMessage(message); - verify(mockDio.post('/messages/${message.id}', - data: anything)) - .called(1); + verify(() => mockDio.post('/messages/${message.id}', + data: anything)).called(1); }); }); }); @@ -1001,31 +1140,44 @@ void main() { test('should update channel', () async { final mockDio = MockDio(); - when(mockDio.options).thenReturn(BaseOptions()); - when(mockDio.interceptors).thenReturn(Interceptors()); + when(() => mockDio.options).thenReturn(BaseOptions()); + when(() => mockDio.interceptors).thenReturn(Interceptors()); final client = StreamChatClient( 'api-key', httpClient: mockDio, ); - final channelClient = - client.channel('type', id: 'id', extraData: {'name': 'init'}); + final channelClient = client.channel( + 'type', + id: 'id', + extraData: {'name': 'init'}, + ); - var update = { + const update = { 'set': {'name': 'demo'} }; - when(mockDio.patch( - '/channels/${channelClient.type}/${channelClient.id}', - data: update, - )).thenAnswer((_) async => Response(data: '{}', statusCode: 200)); + when( + () => mockDio.patch( + '/channels/${channelClient.type}/${channelClient.id}', + data: update, + ), + ).thenAnswer( + (_) async => Response( + data: jsonEncode({'channel': ChannelModel(cid: 'messaging:test')}), + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), + ); await channelClient.updatePartial(update); - verify(mockDio.patch( - '/channels/${channelClient.type}/${channelClient.id}', - data: update)) - .called(1); + verify( + () => mockDio.patch( + '/channels/${channelClient.type}/${channelClient.id}', + data: update, + ), + ).called(1); }); }); }); diff --git a/packages/stream_chat/test/src/models/action_test.dart b/packages/stream_chat/test/src/models/action_test.dart index 4859fcbe..22e66078 100644 --- a/packages/stream_chat/test/src/models/action_test.dart +++ b/packages/stream_chat/test/src/models/action_test.dart @@ -5,7 +5,8 @@ import 'package:stream_chat/src/models/action.dart'; void main() { group('src/models/action', () { - const jsonExample = r'''{ + const jsonExample = ''' + { "name": "name", "style": "style", "text": "text", diff --git a/packages/stream_chat/test/src/models/attachment_test.dart b/packages/stream_chat/test/src/models/attachment_test.dart index b15458ad..1342e72e 100644 --- a/packages/stream_chat/test/src/models/attachment_test.dart +++ b/packages/stream_chat/test/src/models/attachment_test.dart @@ -1,12 +1,13 @@ -import 'package:stream_chat/src/models/attachment.dart'; -import 'package:stream_chat/src/models/action.dart'; import 'dart:convert'; +import 'package:stream_chat/src/models/action.dart'; +import 'package:stream_chat/src/models/attachment.dart'; import 'package:test/test.dart'; void main() { group('src/models/attachment', () { - const jsonExample = r'''{ + const jsonExample = ''' + { "type": "giphy", "title": "awesome", "title_link": "https://giphy.com/gifs/nrkp3-dance-happy-3o7TKnCdBx5cMg0qti", @@ -38,22 +39,27 @@ void main() { test('should parse json correctly', () { final attachment = Attachment.fromJson(json.decode(jsonExample)); - expect(attachment.type, "giphy"); - expect(attachment.title, "awesome"); - expect(attachment.titleLink, - "https://giphy.com/gifs/nrkp3-dance-happy-3o7TKnCdBx5cMg0qti"); - expect(attachment.thumbUrl, - "https://media0.giphy.com/media/3o7TKnCdBx5cMg0qti/giphy.gif"); + expect(attachment.type, 'giphy'); + expect(attachment.title, 'awesome'); + expect( + attachment.titleLink, + 'https://giphy.com/gifs/nrkp3-dance-happy-3o7TKnCdBx5cMg0qti', + ); + expect( + attachment.thumbUrl, + 'https://media0.giphy.com/media/3o7TKnCdBx5cMg0qti/giphy.gif', + ); expect(attachment.actions, hasLength(3)); expect(attachment.actions[0], isA()); }); test('should serialize to json correctly', () { final channel = Attachment( - type: "image", - title: "soo", - titleLink: - "https://giphy.com/gifs/nrkp3-dance-happy-3o7TKnCdBx5cMg0qti"); + type: 'image', + title: 'soo', + titleLink: + 'https://giphy.com/gifs/nrkp3-dance-happy-3o7TKnCdBx5cMg0qti', + ); expect( channel.toJson(), @@ -61,7 +67,8 @@ void main() { 'type': 'image', 'title': 'soo', 'title_link': - 'https://giphy.com/gifs/nrkp3-dance-happy-3o7TKnCdBx5cMg0qti' + 'https://giphy.com/gifs/nrkp3-dance-happy-3o7TKnCdBx5cMg0qti', + 'actions': [], }, ); }); diff --git a/packages/stream_chat/test/src/models/channel_state_test.dart b/packages/stream_chat/test/src/models/channel_state_test.dart index 0a14cb76..a93166b4 100644 --- a/packages/stream_chat/test/src/models/channel_state_test.dart +++ b/packages/stream_chat/test/src/models/channel_state_test.dart @@ -1,16 +1,17 @@ import 'dart:convert'; -import 'package:test/test.dart'; import 'package:stream_chat/src/models/channel_config.dart'; import 'package:stream_chat/src/models/channel_state.dart'; import 'package:stream_chat/src/models/command.dart'; import 'package:stream_chat/src/models/message.dart'; import 'package:stream_chat/src/models/user.dart'; import 'package:stream_chat/stream_chat.dart'; +import 'package:test/test.dart'; void main() { group('src/models/channel_state', () { - const jsonExample = r'''{ + const jsonExample = ''' + { "channel": { "id": "dev", "type": "team", @@ -844,37 +845,41 @@ void main() { test('should parse json correctly', () { final channelState = ChannelState.fromJson(json.decode(jsonExample)); - expect(channelState.channel.cid, 'team:dev'); - expect(channelState.channel.id, 'dev'); - expect(channelState.channel.team, 'test'); - expect(channelState.channel.type, 'team'); - expect(channelState.channel.config, isA()); - expect(channelState.channel.config, isNotNull); - expect(channelState.channel.config.commands, hasLength(1)); - expect(channelState.channel.config.commands[0], isA()); - expect(channelState.channel.lastMessageAt, - DateTime.parse("2020-01-30T13:43:41.062362Z")); - expect(channelState.channel.createdAt, - DateTime.parse("2019-04-03T18:43:33.213373Z")); - expect(channelState.channel.updatedAt, - DateTime.parse("2019-04-03T18:43:33.213374Z")); - expect(channelState.channel.createdBy, isA()); - expect(channelState.channel.frozen, true); - expect(channelState.channel.extraData['example'], 1); - expect(channelState.channel.extraData['name'], "#dev"); - expect(channelState.channel.extraData['image'], - "https://cdn.chrisshort.net/testing-certificate-chains-in-go/GOPHER_MIC_DROP.png"); + expect(channelState.channel?.cid, 'team:dev'); + expect(channelState.channel?.id, 'dev'); + expect(channelState.channel?.team, 'test'); + expect(channelState.channel?.type, 'team'); + expect(channelState.channel?.config, isA()); + expect(channelState.channel?.config, isNotNull); + expect(channelState.channel?.config.commands, hasLength(1)); + expect(channelState.channel?.config.commands[0], isA()); + expect(channelState.channel?.lastMessageAt, + DateTime.parse('2020-01-30T13:43:41.062362Z')); + expect(channelState.channel?.createdAt, + DateTime.parse('2019-04-03T18:43:33.213373Z')); + expect(channelState.channel?.updatedAt, + DateTime.parse('2019-04-03T18:43:33.213374Z')); + expect(channelState.channel?.createdBy, isA()); + expect(channelState.channel?.frozen, true); + expect(channelState.channel?.extraData['example'], 1); + expect(channelState.channel?.extraData['name'], '#dev'); + expect( + channelState.channel?.extraData['image'], + 'https://cdn.chrisshort.net/testing-certificate-chains-in-go/GOPHER_MIC_DROP.png', + ); expect(channelState.messages, hasLength(25)); expect(channelState.messages[0], isA()); expect(channelState.messages[0], isNotNull); - expect(channelState.messages[0].createdAt, - DateTime.parse("2020-01-29T03:23:02.843948Z")); + expect( + channelState.messages[0].createdAt, + DateTime.parse('2020-01-29T03:23:02.843948Z'), + ); expect(channelState.messages[0].user, isA()); expect(channelState.watcherCount, 5); }); test('should serialize to json correctly', () { - const toJsonExample = r''' + const toJsonExample = ''' { "channel": { "id": "dev", @@ -884,8 +889,8 @@ void main() { "image": "https://cdn.chrisshort.net/testing-certificate-chains-in-go/GOPHER_MIC_DROP.png", "example": 1 }, - "watchers": null, - "read": null, + "watchers": [], + "read": [], "messages": [ { "id": "dry-meadow-0-2b73cc8b-cd86-4a01-8d40-bd82ad07a030", @@ -897,7 +902,7 @@ void main() { "show_in_channel": null, "mentioned_users": [], "status": "SENT", - "skip_push": null, + "skip_push": false, "silent": false, "pinned": false, "pinned_at": null, @@ -914,7 +919,7 @@ void main() { "show_in_channel": null, "mentioned_users": [], "status": "SENT", - "skip_push": null, + "skip_push": false, "silent": false, "pinned": false, "pinned_at": null, @@ -924,7 +929,7 @@ void main() { { "id": "dry-meadow-0-53e6299f-9b97-4a9c-a27e-7e2dde49b7e0", "text": "test message", - "skip_push": null, + "skip_push": false, "attachments": [], "parent_id": null, "quoted_message": null, @@ -947,7 +952,7 @@ void main() { "quoted_message_id": null, "show_in_channel": null, "mentioned_users": [], - "skip_push": null, + "skip_push": false, "status": "SENT", "silent": false, "pinned": false, @@ -959,7 +964,7 @@ void main() { "id": "dry-meadow-0-64d7970f-ede8-4b31-9738-1bc1756d2bfe", "text": "test", "attachments": [], - "skip_push": null, + "skip_push": false, "parent_id": null, "quoted_message": null, "quoted_message_id": null, @@ -977,7 +982,7 @@ void main() { "text": "hi", "attachments": [], "parent_id": null, - "skip_push": null, + "skip_push": false, "quoted_message": null, "quoted_message_id": null, "show_in_channel": null, @@ -995,7 +1000,7 @@ void main() { "attachments": [], "parent_id": null, "quoted_message": null, - "skip_push": null, + "skip_push": false, "quoted_message_id": null, "show_in_channel": null, "mentioned_users": [], @@ -1018,7 +1023,7 @@ void main() { "status": "SENT", "silent": false, "pinned": false, - "skip_push": null, + "skip_push": false, "pinned_at": null, "pin_expires": null, "pinned_by": null @@ -1036,7 +1041,7 @@ void main() { "silent": false, "pinned": false, "pinned_at": null, - "skip_push": null, + "skip_push": false, "pin_expires": null, "pinned_by": null }, @@ -1048,7 +1053,7 @@ void main() { "quoted_message": null, "quoted_message_id": null, "show_in_channel": null, - "skip_push": null, + "skip_push": false, "mentioned_users": [], "status": "SENT", "silent": false, @@ -1066,7 +1071,7 @@ void main() { "quoted_message_id": null, "show_in_channel": null, "mentioned_users": [], - "skip_push": null, + "skip_push": false, "status": "SENT", "silent": false, "pinned": false, @@ -1085,7 +1090,7 @@ void main() { "mentioned_users": [], "status": "SENT", "silent": false, - "skip_push": null, + "skip_push": false, "pinned": false, "pinned_at": null, "pin_expires": null, @@ -1095,7 +1100,7 @@ void main() { "id": "icy-recipe-7-935c396e-ddf8-4a9a-951c-0a12fa5bf055", "text": "what are you doing?", "attachments": [], - "skip_push": null, + "skip_push": false, "parent_id": null, "quoted_message": null, "quoted_message_id": null, @@ -1113,7 +1118,7 @@ void main() { "text": "👍", "attachments": [], "parent_id": null, - "skip_push": null, + "skip_push": false, "quoted_message": null, "quoted_message_id": null, "show_in_channel": null, @@ -1129,7 +1134,7 @@ void main() { "id": "snowy-credit-3-3e0c1a0d-d22f-42ee-b2a1-f9f49477bf21", "text": "sdasas", "attachments": [], - "skip_push": null, + "skip_push": false, "parent_id": null, "quoted_message": null, "quoted_message_id": null, @@ -1150,7 +1155,7 @@ void main() { "quoted_message": null, "quoted_message_id": null, "show_in_channel": null, - "skip_push": null, + "skip_push": false, "mentioned_users": [], "status": "SENT", "silent": false, @@ -1163,7 +1168,7 @@ void main() { "id": "snowy-credit-3-cfaf0b46-1daa-49c5-947c-b16d6697487d", "text": "nhisagdhsadz", "attachments": [], - "skip_push": null, + "skip_push": false, "parent_id": null, "quoted_message": null, "quoted_message_id": null, @@ -1182,7 +1187,7 @@ void main() { "attachments": [], "parent_id": null, "quoted_message": null, - "skip_push": null, + "skip_push": false, "quoted_message_id": null, "show_in_channel": null, "mentioned_users": [], @@ -1199,7 +1204,7 @@ void main() { "attachments": [], "parent_id": null, "quoted_message": null, - "skip_push": null, + "skip_push": false, "quoted_message_id": null, "show_in_channel": null, "mentioned_users": [], @@ -1207,7 +1212,7 @@ void main() { "silent": false, "pinned": false, "pinned_at": null, - "skip_push": null, + "skip_push": false, "pin_expires": null, "pinned_by": null }, @@ -1224,7 +1229,7 @@ void main() { "silent": false, "pinned": false, "pinned_at": null, - "skip_push": null, + "skip_push": false, "pin_expires": null, "pinned_by": null }, @@ -1241,7 +1246,7 @@ void main() { "silent": false, "pinned": false, "pinned_at": null, - "skip_push": null, + "skip_push": false, "pin_expires": null, "pinned_by": null }, @@ -1258,7 +1263,7 @@ void main() { "silent": false, "pinned": false, "pinned_at": null, - "skip_push": null, + "skip_push": false, "pin_expires": null, "pinned_by": null }, @@ -1275,7 +1280,7 @@ void main() { "silent": false, "pinned": false, "pinned_at": null, - "skip_push": null, + "skip_push": false, "pin_expires": null, "pinned_by": null }, @@ -1292,7 +1297,7 @@ void main() { "silent": false, "pinned": false, "pinned_at": null, - "skip_push": null, + "skip_push": false, "pin_expires": null, "pinned_by": null }, @@ -1309,7 +1314,7 @@ void main() { "silent": false, "pinned": false, "pinned_at": null, - "skip_push": null, + "skip_push": false, "pin_expires": null, "pinned_by": null } @@ -1325,10 +1330,10 @@ void main() { members: [], messages: (j['messages'] as List).map((m) => Message.fromJson(m)).toList(), - read: null, + read: [], watcherCount: 5, pinnedMessages: [], - watchers: null, + watchers: [], ); expect( diff --git a/packages/stream_chat/test/src/models/channel_test.dart b/packages/stream_chat/test/src/models/channel_test.dart index 69b18197..1e69565c 100644 --- a/packages/stream_chat/test/src/models/channel_test.dart +++ b/packages/stream_chat/test/src/models/channel_test.dart @@ -1,7 +1,7 @@ import 'dart:convert'; -import 'package:test/test.dart'; import 'package:stream_chat/src/models/channel_model.dart'; +import 'package:test/test.dart'; void main() { group('src/models/channel', () { @@ -9,7 +9,7 @@ void main() { { "id": "test", "type": "livestream", - "cid": "test:livestream", + "cid": "livestream:test", "cats": true, "fruit": ["bananas", "apples"] } @@ -17,34 +17,33 @@ void main() { test('should parse json correctly', () { final channel = ChannelModel.fromJson(json.decode(jsonExample)); - expect(channel.id, equals("test")); - expect(channel.type, equals("livestream")); - expect(channel.cid, equals("test:livestream")); - expect(channel.extraData["cats"], equals(true)); - expect(channel.extraData["fruit"], equals(["bananas", "apples"])); + expect(channel.id, equals('test')); + expect(channel.type, equals('livestream')); + expect(channel.cid, equals('livestream:test')); + expect(channel.extraData['cats'], equals(true)); + expect(channel.extraData['fruit'], equals(['bananas', 'apples'])); }); test('should serialize to json correctly', () { final channel = ChannelModel( - type: "type", - id: "id", - cid: "a:a", - extraData: {"name": "cool"}, + type: 'type', + id: 'id', + cid: 'a:a', + extraData: {'name': 'cool'}, ); expect( channel.toJson(), - {'id': 'id', 'type': 'type', 'name': 'cool'}, + {'id': 'id', 'type': 'type', 'frozen': false, 'name': 'cool'}, ); }); test('should serialize to json correctly when frozen is provided', () { final channel = ChannelModel( - type: "type", - id: "id", - cid: "a:a", - extraData: {"name": "cool"}, - frozen: false, + type: 'type', + id: 'id', + cid: 'a:a', + extraData: {'name': 'cool'}, ); expect( diff --git a/packages/stream_chat/test/src/models/command_test.dart b/packages/stream_chat/test/src/models/command_test.dart index dc44f736..8fafd192 100644 --- a/packages/stream_chat/test/src/models/command_test.dart +++ b/packages/stream_chat/test/src/models/command_test.dart @@ -1,6 +1,6 @@ -import 'package:stream_chat/src/models/command.dart'; import 'dart:convert'; +import 'package:stream_chat/src/models/command.dart'; import 'package:test/test.dart'; void main() { @@ -30,9 +30,9 @@ void main() { expect( command.toJson(), { - "name": "giphy", - "description": "Post a random gif to the channel", - "args": "[text]", + 'name': 'giphy', + 'description': 'Post a random gif to the channel', + 'args': '[text]', }, ); }); diff --git a/packages/stream_chat/test/src/models/device_test.dart b/packages/stream_chat/test/src/models/device_test.dart index 3f8de16e..5cbf015d 100644 --- a/packages/stream_chat/test/src/models/device_test.dart +++ b/packages/stream_chat/test/src/models/device_test.dart @@ -5,7 +5,8 @@ import 'package:stream_chat/src/models/device.dart'; void main() { group('src/models/device', () { - const jsonExample = r'''{ + const jsonExample = ''' + { "id": "device-id", "push_provider": "push-provider" }'''; diff --git a/packages/stream_chat/test/src/models/event_test.dart b/packages/stream_chat/test/src/models/event_test.dart index db99b01a..96efa844 100644 --- a/packages/stream_chat/test/src/models/event_test.dart +++ b/packages/stream_chat/test/src/models/event_test.dart @@ -1,9 +1,9 @@ import 'dart:convert'; -import 'package:test/test.dart'; import 'package:stream_chat/src/models/event.dart'; import 'package:stream_chat/src/models/own_user.dart'; import 'package:stream_chat/stream_chat.dart'; +import 'package:test/test.dart'; void main() { group('src/models/event', () { @@ -47,6 +47,7 @@ void main() { expect(event.createdAt, isA()); expect(event.me, isA()); expect(event.user, isA()); + expect(event.isLocal, false); }); test('should serialize to json correctly', () { @@ -55,7 +56,7 @@ void main() { type: 'type', cid: 'cid', connectionId: 'connectionId', - createdAt: DateTime.parse("2020-01-29T03:22:47.63613Z"), + createdAt: DateTime.parse('2020-01-29T03:22:47.63613Z'), me: OwnUser(id: 'id2'), totalUnreadCount: 1, unreadChannels: 1, @@ -77,11 +78,11 @@ void main() { 'total_unread_count': 1, 'unread_channels': 1, 'online': true, - 'is_local': true, 'member': null, 'channel_id': null, 'channel_type': null, 'parent_id': null, + 'is_local': true, }, ); }); diff --git a/packages/stream_chat/test/src/models/filter_test.dart b/packages/stream_chat/test/src/models/filter_test.dart new file mode 100644 index 00000000..c73c0d0b --- /dev/null +++ b/packages/stream_chat/test/src/models/filter_test.dart @@ -0,0 +1,212 @@ +import 'dart:convert'; + +import 'package:test/test.dart'; +import 'package:stream_chat/src/models/filter.dart'; + +void main() { + group('operators', () { + test('equal', () { + const key = 'testKey'; + const value = 'testValue'; + final filter = Filter.equal(key, value); + expect(filter.key, key); + expect(filter.value, value); + expect(filter.operator, FilterOperator.equal.rawValue); + }); + + test('notEqual', () { + const key = 'testKey'; + const value = 'testValue'; + final filter = Filter.notEqual(key, value); + expect(filter.key, key); + expect(filter.value, value); + expect(filter.operator, FilterOperator.notEqual.rawValue); + }); + + test('greater', () { + const key = 'testKey'; + const value = 'testValue'; + final filter = Filter.greater(key, value); + expect(filter.key, key); + expect(filter.value, value); + expect(filter.operator, FilterOperator.greater.rawValue); + }); + + test('greaterOrEqual', () { + const key = 'testKey'; + const value = 'testValue'; + final filter = Filter.greaterOrEqual(key, value); + expect(filter.key, key); + expect(filter.value, value); + expect(filter.operator, FilterOperator.greaterOrEqual.rawValue); + }); + + test('less', () { + const key = 'testKey'; + const value = 'testValue'; + final filter = Filter.less(key, value); + expect(filter.key, key); + expect(filter.value, value); + expect(filter.operator, FilterOperator.less.rawValue); + }); + + test('lessOrEqual', () { + const key = 'testKey'; + const value = 'testValue'; + final filter = Filter.lessOrEqual(key, value); + expect(filter.key, key); + expect(filter.value, value); + expect(filter.operator, FilterOperator.lessOrEqual.rawValue); + }); + + test('in', () { + const key = 'testKey'; + const values = ['testValue']; + final filter = Filter.in_(key, values); + expect(filter.key, key); + expect(filter.value, values); + expect(filter.operator, FilterOperator.in_.rawValue); + }); + + test('in', () { + const key = 'testKey'; + const values = ['testValue']; + final filter = Filter.in_(key, values); + expect(filter.key, key); + expect(filter.value, values); + expect(filter.operator, FilterOperator.in_.rawValue); + }); + + test('notIn', () { + const key = 'testKey'; + const values = ['testValue']; + final filter = Filter.notIn(key, values); + expect(filter.key, key); + expect(filter.value, values); + expect(filter.operator, FilterOperator.notIn.rawValue); + }); + + test('query', () { + const key = 'testKey'; + const value = 'testQuery'; + final filter = Filter.query(key, value); + expect(filter.key, key); + expect(filter.value, value); + expect(filter.operator, FilterOperator.query.rawValue); + }); + + test('autoComplete', () { + const key = 'testKey'; + const value = 'testQuery'; + final filter = Filter.autoComplete(key, value); + expect(filter.key, key); + expect(filter.value, value); + expect(filter.operator, FilterOperator.autoComplete.rawValue); + }); + + test('exists', () { + const key = 'testKey'; + final filter = Filter.exists(key); + expect(filter.key, key); + expect(filter.value, isTrue); + expect(filter.operator, FilterOperator.exists.rawValue); + }); + + test('notExists', () { + const key = 'testKey'; + final filter = Filter.exists(key, exists: false); + expect(filter.key, key); + expect(filter.value, isFalse); + expect(filter.operator, FilterOperator.exists.rawValue); + }); + + test('custom', () { + const key = 'testKey'; + const value = 'testValue'; + const operator = '\$customOperator'; + const filter = Filter.custom(operator: operator, key: key, value: value); + expect(filter.key, key); + expect(filter.value, value); + expect(filter.operator, operator); + }); + + group('groupedOperator', () { + final filter1 = Filter.equal('testKey', 'testValue'); + final filter2 = Filter.in_('testKey', const ['testValue']); + final filters = [filter1, filter2]; + + test('and', () { + final filter = Filter.and(filters); + expect(filter.key, isNull); + expect(filter.value, filters); + expect(filter.operator, FilterOperator.and.rawValue); + }); + + test('or', () { + final filter = Filter.or(filters); + expect(filter.key, isNull); + expect(filter.value, filters); + expect(filter.operator, FilterOperator.or.rawValue); + }); + + test('nor', () { + final filter = Filter.nor(filters); + expect(filter.key, isNull); + expect(filter.value, filters); + expect(filter.operator, FilterOperator.nor.rawValue); + }); + }); + }); + + group('encoding', () { + group('nonGroupedFilter', () { + test('simpleValue', () { + const key = 'testKey'; + const value = 'testValue'; + final filter = Filter.equal(key, value); + final encoded = json.encode(filter); + expect( + encoded, + '{"$key":{"${FilterOperator.equal.rawValue}":${json.encode(value)}}}', + ); + }); + test('listValue', () { + const key = 'testKey'; + const values = ['testValue']; + final filter = Filter.in_(key, values); + final encoded = json.encode(filter); + expect( + encoded, + '{"$key":{"${FilterOperator.in_.rawValue}":${json.encode(values)}}}', + ); + }); + }); + + test('groupedFilter', () { + final filter1 = Filter.equal('testKey', 'testValue'); + final filter2 = Filter.in_('testKey', const ['testValue']); + final filters = [filter1, filter2]; + + final filter = Filter.and(filters); + final encoded = json.encode(filter); + expect( + encoded, + '{"${FilterOperator.and.rawValue}":${json.encode(filters)}}', + ); + }); + + group('equality', () { + test('simpleFilter', () { + final filter1 = Filter.equal('testKey', 'testValue'); + final filter2 = Filter.equal('testKey', 'testValue'); + expect(filter1, filter2); + }); + + test('groupedFilter', () { + final filter1 = Filter.and([Filter.equal('testKey', 'testValue')]); + final filter2 = Filter.and([Filter.equal('testKey', 'testValue')]); + expect(filter1, filter2); + }); + }); + }); +} diff --git a/packages/stream_chat/test/src/models/member_test.dart b/packages/stream_chat/test/src/models/member_test.dart index 237f0030..25e1d78e 100644 --- a/packages/stream_chat/test/src/models/member_test.dart +++ b/packages/stream_chat/test/src/models/member_test.dart @@ -28,8 +28,8 @@ void main() { final member = Member.fromJson(json.decode(jsonExample)); expect(member.user, isA()); expect(member.role, 'member'); - expect(member.createdAt, DateTime.parse("2020-01-28T22:17:30.95443Z")); - expect(member.updatedAt, DateTime.parse("2020-01-28T22:17:30.95443Z")); + expect(member.createdAt, DateTime.parse('2020-01-28T22:17:30.95443Z')); + expect(member.updatedAt, DateTime.parse('2020-01-28T22:17:30.95443Z')); }); }); } diff --git a/packages/stream_chat/test/src/models/message_test.dart b/packages/stream_chat/test/src/models/message_test.dart index 3541b2c0..1219e137 100644 --- a/packages/stream_chat/test/src/models/message_test.dart +++ b/packages/stream_chat/test/src/models/message_test.dart @@ -1,14 +1,15 @@ import 'dart:convert'; -import 'package:test/test.dart'; import 'package:stream_chat/src/models/attachment.dart'; import 'package:stream_chat/src/models/message.dart'; import 'package:stream_chat/src/models/reaction.dart'; import 'package:stream_chat/src/models/user.dart'; +import 'package:test/test.dart'; void main() { group('src/models/message', () { - const jsonExample = r'''{ + const jsonExample = r''' + { "id": "4637f7e4-a06b-42db-ba5a-8d8270dd926f", "text": "https://giphy.com/gifs/the-lion-king-live-action-5zvN79uTGfLMOVfQaA", "type": "regular", @@ -76,10 +77,10 @@ void main() { test('should parse json correctly', () { final message = Message.fromJson(json.decode(jsonExample)); - expect(message.id, "4637f7e4-a06b-42db-ba5a-8d8270dd926f"); + expect(message.id, '4637f7e4-a06b-42db-ba5a-8d8270dd926f'); expect(message.text, - "https://giphy.com/gifs/the-lion-king-live-action-5zvN79uTGfLMOVfQaA"); - expect(message.type, "regular"); + 'https://giphy.com/gifs/the-lion-king-live-action-5zvN79uTGfLMOVfQaA'); + expect(message.type, 'regular'); expect(message.user, isA()); expect(message.silent, isA()); expect(message.attachments, isA>()); @@ -87,8 +88,8 @@ void main() { expect(message.ownReactions, isA>()); expect(message.reactionCounts, {'love': 1}); expect(message.reactionScores, {'love': 1}); - expect(message.createdAt, DateTime.parse("2020-01-28T22:17:31.107978Z")); - expect(message.updatedAt, DateTime.parse("2020-01-28T22:17:31.130506Z")); + expect(message.createdAt, DateTime.parse('2020-01-28T22:17:31.107978Z')); + expect(message.updatedAt, DateTime.parse('2020-01-28T22:17:31.130506Z')); expect(message.mentionedUsers, isA>()); expect(message.pinned, false); expect(message.pinnedAt, null); @@ -98,43 +99,41 @@ void main() { test('should serialize to json correctly', () { final message = Message( - id: "4637f7e4-a06b-42db-ba5a-8d8270dd926f", + id: '4637f7e4-a06b-42db-ba5a-8d8270dd926f', text: - "https://giphy.com/gifs/the-lion-king-live-action-5zvN79uTGfLMOVfQaA", - silent: false, + 'https://giphy.com/gifs/the-lion-king-live-action-5zvN79uTGfLMOVfQaA', attachments: [ - Attachment.fromJson({ - "type": "video", - "author_name": "GIPHY", - "title": "The Lion King Disney GIF - Find \u0026 Share on GIPHY", - "title_link": - "https://media.giphy.com/media/5zvN79uTGfLMOVfQaA/giphy.gif", - "text": - "Discover \u0026 share this Lion King Live Action GIF with everyone you know. GIPHY is how you search, share, discover, and create GIFs.", - "image_url": - "https://media.giphy.com/media/5zvN79uTGfLMOVfQaA/giphy.gif", - "thumb_url": - "https://media.giphy.com/media/5zvN79uTGfLMOVfQaA/giphy.gif", - "asset_url": - "https://media.giphy.com/media/5zvN79uTGfLMOVfQaA/giphy.mp4", - "og_scrape_url": - "https://giphy.com/gifs/the-lion-king-live-action-5zvN79uTGfLMOVfQaA" + Attachment.fromJson(const { + 'type': 'video', + 'author_name': 'GIPHY', + 'title': 'The Lion King Disney GIF - Find \u0026 Share on GIPHY', + 'title_link': + 'https://media.giphy.com/media/5zvN79uTGfLMOVfQaA/giphy.gif', + 'text': + '''Discover \u0026 share this Lion King Live Action GIF with everyone you know. GIPHY is how you search, share, discover, and create GIFs.''', + 'image_url': + 'https://media.giphy.com/media/5zvN79uTGfLMOVfQaA/giphy.gif', + 'thumb_url': + 'https://media.giphy.com/media/5zvN79uTGfLMOVfQaA/giphy.gif', + 'asset_url': + 'https://media.giphy.com/media/5zvN79uTGfLMOVfQaA/giphy.mp4', + 'og_scrape_url': + 'https://giphy.com/gifs/the-lion-king-live-action-5zvN79uTGfLMOVfQaA' }) ], showInChannel: true, parentId: 'parentId', - extraData: {'hey': 'test'}, - status: MessageSendingStatus.sent, + extraData: const {'hey': 'test'}, ); expect( message.toJson(), - json.decode(r''' + json.decode(''' { "id": "4637f7e4-a06b-42db-ba5a-8d8270dd926f", "text": "https://giphy.com/gifs/the-lion-king-live-action-5zvN79uTGfLMOVfQaA", "silent": false, - "skip_push": null, + "skip_push": false, "attachments": [ { "type": "video", @@ -145,10 +144,11 @@ void main() { "og_scrape_url": "https://giphy.com/gifs/the-lion-king-live-action-5zvN79uTGfLMOVfQaA", "image_url": "https://media.giphy.com/media/5zvN79uTGfLMOVfQaA/giphy.gif", "author_name": "GIPHY", - "asset_url": "https://media.giphy.com/media/5zvN79uTGfLMOVfQaA/giphy.mp4" + "asset_url": "https://media.giphy.com/media/5zvN79uTGfLMOVfQaA/giphy.mp4", + "actions": [] } ], - "mentioned_users": null, + "mentioned_users": [], "parent_id": "parentId", "quoted_message": null, "quoted_message_id": null, diff --git a/packages/stream_chat/test/src/models/reaction_test.dart b/packages/stream_chat/test/src/models/reaction_test.dart index 8bc92081..795e2967 100644 --- a/packages/stream_chat/test/src/models/reaction_test.dart +++ b/packages/stream_chat/test/src/models/reaction_test.dart @@ -1,8 +1,8 @@ -import 'package:test/test.dart'; -import 'package:stream_chat/src/models/reaction.dart'; import 'dart:convert'; +import 'package:stream_chat/src/models/reaction.dart'; import 'package:stream_chat/src/models/user.dart'; +import 'package:test/test.dart'; void main() { group('src/models/reaction', () { @@ -30,13 +30,13 @@ void main() { test('should parse json correctly', () { final reaction = Reaction.fromJson(json.decode(jsonExample)); expect(reaction.messageId, '76cd8c82-b557-4e48-9d12-87995d3a0e04'); - expect(reaction.createdAt, DateTime.parse("2020-01-28T22:17:31.108742Z")); + expect(reaction.createdAt, DateTime.parse('2020-01-28T22:17:31.108742Z')); expect(reaction.type, 'wow'); expect( - reaction.user.toJson(), - User.init("2de0297c-f3f2-489d-b930-ef77342edccf", extraData: { - "image": "https://randomuser.me/api/portraits/women/45.jpg", - "name": "Daisy Morgan" + reaction.user?.toJson(), + User(id: '2de0297c-f3f2-489d-b930-ef77342edccf', extraData: { + 'image': 'https://randomuser.me/api/portraits/women/45.jpg', + 'name': 'Daisy Morgan' }).toJson(), ); expect(reaction.score, 1); @@ -47,13 +47,13 @@ void main() { test('should serialize to json correctly', () { final reaction = Reaction( messageId: '76cd8c82-b557-4e48-9d12-87995d3a0e04', - createdAt: DateTime.parse("2020-01-28T22:17:31.108742Z"), + createdAt: DateTime.parse('2020-01-28T22:17:31.108742Z'), type: 'wow', - user: User.init("2de0297c-f3f2-489d-b930-ef77342edccf", extraData: { - "image": "https://randomuser.me/api/portraits/women/45.jpg", - "name": "Daisy Morgan" + user: User(id: '2de0297c-f3f2-489d-b930-ef77342edccf', extraData: { + 'image': 'https://randomuser.me/api/portraits/women/45.jpg', + 'name': 'Daisy Morgan' }), - userId: "2de0297c-f3f2-489d-b930-ef77342edccf", + userId: '2de0297c-f3f2-489d-b930-ef77342edccf', extraData: {'bananas': 'yes'}, score: 1, ); @@ -61,10 +61,10 @@ void main() { expect( reaction.toJson(), { - "message_id": "76cd8c82-b557-4e48-9d12-87995d3a0e04", - "type": "wow", - "score": 1, - "bananas": 'yes', + 'message_id': '76cd8c82-b557-4e48-9d12-87995d3a0e04', + 'type': 'wow', + 'score': 1, + 'bananas': 'yes', }, ); }); diff --git a/packages/stream_chat/test/src/models/read_test.dart b/packages/stream_chat/test/src/models/read_test.dart index b15ce66a..66efe809 100644 --- a/packages/stream_chat/test/src/models/read_test.dart +++ b/packages/stream_chat/test/src/models/read_test.dart @@ -26,13 +26,13 @@ void main() { test('should serialize to json correctly', () { final read = Read( lastRead: DateTime.parse('2020-01-28T22:17:30.966485504Z'), - user: User.init('bbb19d9a-ee50-45bc-84e5-0584e79d0c9e'), + user: User(id: 'bbb19d9a-ee50-45bc-84e5-0584e79d0c9e'), unreadMessages: 10, ); expect(read.toJson(), { - "user": {"id": "bbb19d9a-ee50-45bc-84e5-0584e79d0c9e"}, - "last_read": "2020-01-28T22:17:30.966485Z", + 'user': {'id': 'bbb19d9a-ee50-45bc-84e5-0584e79d0c9e'}, + 'last_read': '2020-01-28T22:17:30.966485Z', 'unread_messages': 10, }); }); diff --git a/packages/stream_chat/test/src/models/serialization_test.dart b/packages/stream_chat/test/src/models/serialization_test.dart index e8a2de3f..9e64caad 100644 --- a/packages/stream_chat/test/src/models/serialization_test.dart +++ b/packages/stream_chat/test/src/models/serialization_test.dart @@ -49,12 +49,12 @@ void main() { }); test('should return null', () { - final result = Serialization.moveToExtraDataFromRoot(null, [ + final result = Serialization.moveToExtraDataFromRoot({}, [ 'prop1', 'prop2', ]); - expect(result, null); + expect(result, {'extra_data': {}}); }); }); } diff --git a/packages/stream_chat/test/src/models/user_test.dart b/packages/stream_chat/test/src/models/user_test.dart index 6100b218..8f56f7e1 100644 --- a/packages/stream_chat/test/src/models/user_test.dart +++ b/packages/stream_chat/test/src/models/user_test.dart @@ -1,13 +1,14 @@ import 'dart:convert'; -import 'package:test/test.dart'; import 'package:stream_chat/src/models/user.dart'; +import 'package:test/test.dart'; void main() { group('src/models/user', () { const jsonExample = ''' { - "id": "bbb19d9a-ee50-45bc-84e5-0584e79d0c9e" + "id": "bbb19d9a-ee50-45bc-84e5-0584e79d0c9e", + "role": "test-role" } '''; @@ -17,11 +18,13 @@ void main() { }); test('should serialize to json correctly', () { - final user = - User(id: 'bbb19d9a-ee50-45bc-84e5-0584e79d0c9e', role: "abc"); + final user = User( + id: 'bbb19d9a-ee50-45bc-84e5-0584e79d0c9e', + role: 'abc', + ); expect(user.toJson(), { - 'id': "bbb19d9a-ee50-45bc-84e5-0584e79d0c9e", + 'id': 'bbb19d9a-ee50-45bc-84e5-0584e79d0c9e', }); }); }); diff --git a/packages/stream_chat/test/version_test.dart b/packages/stream_chat/test/version_test.dart index 55b1818a..34ecc750 100644 --- a/packages/stream_chat/test/version_test.dart +++ b/packages/stream_chat/test/version_test.dart @@ -1,8 +1,7 @@ import 'dart:io'; -import 'package:rxdart/rxdart.dart'; -import 'package:test/test.dart'; import 'package:stream_chat/version.dart'; +import 'package:test/test.dart'; void prepareTest() { // https://github.com/flutter/flutter/issues/20907 @@ -14,11 +13,12 @@ void prepareTest() { void main() { prepareTest(); test('stream chat version matches pubspec', () { - final String pubspecPath = '${Directory.current.path}/pubspec.yaml'; - final String pubspec = File(pubspecPath).readAsStringSync(); - final RegExp regex = RegExp('version:\s*(.*)'); - final RegExpMatch match = regex.firstMatch(pubspec); + final pubspecPath = '${Directory.current.path}/pubspec.yaml'; + final pubspec = File(pubspecPath).readAsStringSync(); + // ignore: unnecessary_string_escapes + final regex = RegExp('version:\s*(.*)'); + final match = regex.firstMatch(pubspec); expect(match, isNotNull); - expect(PACKAGE_VERSION, match.group(1).trim()); + expect(PACKAGE_VERSION, match?.group(1)?.trim()); }); } diff --git a/packages/stream_chat_flutter/CHANGELOG.md b/packages/stream_chat_flutter/CHANGELOG.md index efbdfccc..414f9121 100644 --- a/packages/stream_chat_flutter/CHANGELOG.md +++ b/packages/stream_chat_flutter/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.0.0-nullsafety.0 + +- Migrate this package to null safety +- Updated `stream_chat_core` dependency + ## 1.5.3 - Updated `stream_chat_core` dependency diff --git a/packages/stream_chat_flutter/analysis_options.yaml b/packages/stream_chat_flutter/analysis_options.yaml index 36d1fbb7..370654dc 100644 --- a/packages/stream_chat_flutter/analysis_options.yaml +++ b/packages/stream_chat_flutter/analysis_options.yaml @@ -6,6 +6,7 @@ analyzer: exclude: - lib/**/*.g.dart - example/** + - lib/src/emoji linter: rules: diff --git a/packages/stream_chat_flutter/example/.fvm/flutter_sdk b/packages/stream_chat_flutter/example/.fvm/flutter_sdk deleted file mode 120000 index cdf17889..00000000 --- a/packages/stream_chat_flutter/example/.fvm/flutter_sdk +++ /dev/null @@ -1 +0,0 @@ -/Users/salvatoregiordano/fvm/versions/beta \ No newline at end of file diff --git a/packages/stream_chat_flutter/example/.fvm/fvm_config.json b/packages/stream_chat_flutter/example/.fvm/fvm_config.json deleted file mode 100644 index 6504dcd0..00000000 --- a/packages/stream_chat_flutter/example/.fvm/fvm_config.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "flutterSdkVersion": "beta" -} \ No newline at end of file diff --git a/packages/stream_chat_flutter/example/lib/main.dart b/packages/stream_chat_flutter/example/lib/main.dart index 731ef080..1f3b51d2 100644 --- a/packages/stream_chat_flutter/example/lib/main.dart +++ b/packages/stream_chat_flutter/example/lib/main.dart @@ -61,8 +61,8 @@ class MyApp extends StatelessWidget { themeMode: ThemeMode.system, builder: (context, widget) { return StreamChat( - child: widget, client: client, + child: widget, ); }, home: StreamChannel( @@ -80,7 +80,7 @@ class MyApp extends StatelessWidget { class ChannelPage extends StatelessWidget { /// Creates the page that shows the list of messages const ChannelPage({ - Key key, + Key? key, }) : super(key: key); @override diff --git a/packages/stream_chat_flutter/example/lib/split_view.dart b/packages/stream_chat_flutter/example/lib/split_view.dart index 2026b101..1c833e23 100644 --- a/packages/stream_chat_flutter/example/lib/split_view.dart +++ b/packages/stream_chat_flutter/example/lib/split_view.dart @@ -25,8 +25,8 @@ class MyApp extends StatelessWidget { Widget build(BuildContext context) { return MaterialApp( builder: (context, child) => StreamChat( - child: child, client: client, + child: child, ), home: SplitView(), ); @@ -39,7 +39,7 @@ class SplitView extends StatefulWidget { } class _SplitViewState extends State { - Channel selectedChannel; + Channel? selectedChannel; @override Widget build(BuildContext context) { @@ -47,6 +47,7 @@ class _SplitViewState extends State { direction: Axis.horizontal, children: [ Flexible( + flex: 1, child: ChannelListPage( onTap: (channel) { setState(() { @@ -54,15 +55,15 @@ class _SplitViewState extends State { }); }, ), - flex: 1, ), Flexible( + flex: 2, child: Scaffold( body: selectedChannel != null ? StreamChannel( - key: ValueKey(selectedChannel.cid), + key: ValueKey(selectedChannel!.cid), + channel: selectedChannel!, child: ChannelPage(), - channel: selectedChannel, ) : Center( child: Text( @@ -71,7 +72,6 @@ class _SplitViewState extends State { ), ), ), - flex: 2, ), ], ); @@ -79,7 +79,7 @@ class _SplitViewState extends State { } class ChannelListPage extends StatelessWidget { - final void Function(Channel) onTap; + final void Function(Channel)? onTap; ChannelListPage({this.onTap}); @@ -90,14 +90,13 @@ class ChannelListPage extends StatelessWidget { child: ChannelListView( onChannelTap: onTap != null ? (channel, _) { - onTap(channel); + onTap!(channel); } : null, - filter: { - 'members': { - '\$in': [StreamChat.of(context).user.id], - } - }, + filter: Filter.in_( + 'members', + [StreamChat.of(context).user!.id], + ), sort: [SortOption('last_message_at')], pagination: PaginationParams( limit: 20, @@ -110,7 +109,7 @@ class ChannelListPage extends StatelessWidget { class ChannelPage extends StatelessWidget { const ChannelPage({ - Key key, + Key? key, }) : super(key: key); @override diff --git a/packages/stream_chat_flutter/example/lib/tutorial-part-1.dart b/packages/stream_chat_flutter/example/lib/tutorial-part-1.dart index 1fd53f55..2a720c70 100644 --- a/packages/stream_chat_flutter/example/lib/tutorial-part-1.dart +++ b/packages/stream_chat_flutter/example/lib/tutorial-part-1.dart @@ -55,8 +55,8 @@ class MyApp extends StatelessWidget { return MaterialApp( builder: (context, widget) { return StreamChat( - child: widget, client: client, + child: widget, ); }, home: StreamChannel( @@ -69,7 +69,7 @@ class MyApp extends StatelessWidget { class ChannelPage extends StatelessWidget { const ChannelPage({ - Key key, + Key? key, }) : super(key: key); @override diff --git a/packages/stream_chat_flutter/example/lib/tutorial-part-2.dart b/packages/stream_chat_flutter/example/lib/tutorial-part-2.dart index 8b3009c8..d1b39774 100644 --- a/packages/stream_chat_flutter/example/lib/tutorial-part-2.dart +++ b/packages/stream_chat_flutter/example/lib/tutorial-part-2.dart @@ -56,11 +56,10 @@ class ChannelListPage extends StatelessWidget { return Scaffold( body: ChannelsBloc( child: ChannelListView( - filter: { - // 'members': { - // '\$in': [StreamChat.of(context).user.id], - // } - }, + filter: Filter.in_( + 'members', + [StreamChat.of(context).user.id], + ), sort: [SortOption('last_message_at')], pagination: PaginationParams( limit: 20, @@ -74,7 +73,7 @@ class ChannelListPage extends StatelessWidget { class ChannelPage extends StatelessWidget { const ChannelPage({ - Key key, + Key? key, }) : super(key: key); @override diff --git a/packages/stream_chat_flutter/example/lib/tutorial-part-3.dart b/packages/stream_chat_flutter/example/lib/tutorial-part-3.dart index 3a34dc01..fc55f6d9 100644 --- a/packages/stream_chat_flutter/example/lib/tutorial-part-3.dart +++ b/packages/stream_chat_flutter/example/lib/tutorial-part-3.dart @@ -1,4 +1,5 @@ // ignore_for_file: public_member_api_docs +import 'package:collection/collection.dart' show IterableExtension; import 'package:flutter/material.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; @@ -43,8 +44,8 @@ class MyApp extends StatelessWidget { Widget build(BuildContext context) { return MaterialApp( builder: (context, child) => StreamChat( - child: child, client: client, + child: child, ), home: ChannelListPage(), ); @@ -57,11 +58,10 @@ class ChannelListPage extends StatelessWidget { return Scaffold( body: ChannelsBloc( child: ChannelListView( - filter: { - 'members': { - '\$in': [StreamChat.of(context).user.id], - } - }, + filter: Filter.in_( + 'members', + [StreamChat.of(context).user!.id], + ), channelPreviewBuilder: _channelPreviewBuilder, // sort: [SortOption('last_message_at')], pagination: PaginationParams( @@ -74,13 +74,12 @@ class ChannelListPage extends StatelessWidget { } Widget _channelPreviewBuilder(BuildContext context, Channel channel) { - final lastMessage = channel.state.messages.reversed.firstWhere( + final lastMessage = channel.state?.messages.reversed.firstWhereOrNull( (message) => !message.isDeleted, - orElse: () => null, ); - final subtitle = (lastMessage == null ? 'nothing yet' : lastMessage.text); - final opacity = channel.state.unreadCount > .0 ? 1.0 : 0.5; + final subtitle = (lastMessage == null ? 'nothing yet' : lastMessage.text!); + final opacity = (channel.state?.unreadCount ?? 0) > 0 ? 1.0 : 0.5; return ListTile( onTap: () { @@ -88,8 +87,8 @@ class ChannelListPage extends StatelessWidget { context, MaterialPageRoute( builder: (_) => StreamChannel( - child: ChannelPage(), channel: channel, + child: ChannelPage(), ), ), ); @@ -99,7 +98,7 @@ class ChannelListPage extends StatelessWidget { ), title: ChannelName( textStyle: - StreamChatTheme.of(context).channelPreviewTheme.title.copyWith( + StreamChatTheme.of(context).channelPreviewTheme.title!.copyWith( color: StreamChatTheme.of(context) .colorTheme .black @@ -107,10 +106,10 @@ class ChannelListPage extends StatelessWidget { ), ), subtitle: Text(subtitle), - trailing: channel.state.unreadCount > 0 + trailing: channel.state!.unreadCount! > 0 ? CircleAvatar( radius: 10, - child: Text(channel.state.unreadCount.toString()), + child: Text(channel.state!.unreadCount.toString()), ) : SizedBox(), ); @@ -119,7 +118,7 @@ class ChannelListPage extends StatelessWidget { class ChannelPage extends StatelessWidget { const ChannelPage({ - Key key, + Key? key, }) : super(key: key); @override diff --git a/packages/stream_chat_flutter/example/lib/tutorial-part-4.dart b/packages/stream_chat_flutter/example/lib/tutorial-part-4.dart index 5bdd5472..5a85339f 100644 --- a/packages/stream_chat_flutter/example/lib/tutorial-part-4.dart +++ b/packages/stream_chat_flutter/example/lib/tutorial-part-4.dart @@ -33,8 +33,8 @@ class MyApp extends StatelessWidget { Widget build(BuildContext context) { return MaterialApp( builder: (context, child) => StreamChat( - child: child, client: client, + child: child, ), home: Container( child: ChannelListPage(), @@ -49,11 +49,10 @@ class ChannelListPage extends StatelessWidget { return Scaffold( body: ChannelsBloc( child: ChannelListView( - filter: { - 'members': { - '\$in': [StreamChat.of(context).user.id], - } - }, + filter: Filter.in_( + 'members', + [StreamChat.of(context).user!.id], + ), sort: [SortOption('last_message_at')], pagination: PaginationParams( limit: 20, @@ -67,7 +66,7 @@ class ChannelListPage extends StatelessWidget { class ChannelPage extends StatelessWidget { const ChannelPage({ - Key key, + Key? key, }) : super(key: key); @override @@ -93,10 +92,10 @@ class ChannelPage extends StatelessWidget { } class ThreadPage extends StatelessWidget { - final Message parent; + final Message? parent; ThreadPage({ - Key key, + Key? key, this.parent, }) : super(key: key); @@ -104,7 +103,7 @@ class ThreadPage extends StatelessWidget { Widget build(BuildContext context) { return Scaffold( appBar: ThreadHeader( - parent: parent, + parent: parent!, ), body: Column( children: [ diff --git a/packages/stream_chat_flutter/example/lib/tutorial-part-5.dart b/packages/stream_chat_flutter/example/lib/tutorial-part-5.dart index 18ae5ff8..7a75c037 100644 --- a/packages/stream_chat_flutter/example/lib/tutorial-part-5.dart +++ b/packages/stream_chat_flutter/example/lib/tutorial-part-5.dart @@ -38,8 +38,8 @@ class MyApp extends StatelessWidget { Widget build(BuildContext context) { return MaterialApp( builder: (context, child) => StreamChat( - child: child, client: client, + child: child, ), home: ChannelListPage(), ); @@ -52,11 +52,10 @@ class ChannelListPage extends StatelessWidget { return Scaffold( body: ChannelsBloc( child: ChannelListView( - filter: { - 'members': { - '\$in': [StreamChat.of(context).user.id], - } - }, + filter: Filter.in_( + 'members', + [StreamChat.of(context).user!.id], + ), sort: [SortOption('last_message_at')], pagination: PaginationParams( limit: 20, @@ -70,7 +69,7 @@ class ChannelListPage extends StatelessWidget { class ChannelPage extends StatelessWidget { const ChannelPage({ - Key key, + Key? key, }) : super(key: key); @override @@ -96,7 +95,7 @@ class ChannelPage extends StatelessWidget { List messages, ) { final message = details.message; - final isCurrentUser = StreamChat.of(context).user.id == message.user.id; + final isCurrentUser = StreamChat.of(context).user!.id == message.user!.id; final textAlign = isCurrentUser ? TextAlign.right : TextAlign.left; final color = isCurrentUser ? Colors.blueGrey : Colors.blue; @@ -111,11 +110,11 @@ class ChannelPage extends StatelessWidget { ), child: ListTile( title: Text( - message.text, + message.text!, textAlign: textAlign, ), subtitle: Text( - message.user.extraData['name'], + message.user!.extraData['name'] as String, textAlign: textAlign, ), ), diff --git a/packages/stream_chat_flutter/example/lib/tutorial-part-6.dart b/packages/stream_chat_flutter/example/lib/tutorial-part-6.dart index 110cc1dc..c49ffaf1 100644 --- a/packages/stream_chat_flutter/example/lib/tutorial-part-6.dart +++ b/packages/stream_chat_flutter/example/lib/tutorial-part-6.dart @@ -64,9 +64,9 @@ class MyApp extends StatelessWidget { theme: themeData, builder: (context, child) { return StreamChat( - child: child, client: client, streamChatThemeData: customTheme, + child: child, ); }, home: ChannelListPage(), @@ -80,11 +80,10 @@ class ChannelListPage extends StatelessWidget { return Scaffold( body: ChannelsBloc( child: ChannelListView( - filter: { - 'members': { - '\$in': [StreamChat.of(context).user.id], - } - }, + filter: Filter.in_( + 'members', + [StreamChat.of(context).user!.id], + ), sort: [SortOption('last_message_at')], pagination: PaginationParams( limit: 20, @@ -98,7 +97,7 @@ class ChannelListPage extends StatelessWidget { class ChannelPage extends StatelessWidget { const ChannelPage({ - Key key, + Key? key, }) : super(key: key); @override @@ -124,10 +123,10 @@ class ChannelPage extends StatelessWidget { } class ThreadPage extends StatelessWidget { - final Message parent; + final Message? parent; ThreadPage({ - Key key, + Key? key, this.parent, }) : super(key: key); @@ -135,7 +134,7 @@ class ThreadPage extends StatelessWidget { Widget build(BuildContext context) { return Scaffold( appBar: ThreadHeader( - parent: parent, + parent: parent!, ), body: Column( children: [ diff --git a/packages/stream_chat_flutter/example/pubspec.yaml b/packages/stream_chat_flutter/example/pubspec.yaml index 8a5270df..3feec144 100644 --- a/packages/stream_chat_flutter/example/pubspec.yaml +++ b/packages/stream_chat_flutter/example/pubspec.yaml @@ -18,7 +18,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev version: 1.0.0+1 environment: - sdk: ">=2.7.0 <3.0.0" + sdk: '>=2.12.0 <3.0.0' dependencies: flutter: @@ -26,22 +26,12 @@ dependencies: stream_chat_flutter: path: ../ stream_chat_persistence: - git: - url: https://github.com/GetStream/stream-chat-flutter.git - ref: develop - path: packages/stream_chat_persistence + path: ../../stream_chat_persistence # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^1.0.0 - -dependency_overrides: - stream_chat: - path: ../../stream_chat - stream_chat_flutter_core: - path: ../../stream_chat_flutter_core - stream_chat_persistence: - path: ../../stream_chat_persistence + cupertino_icons: ^1.0.2 + collection: ^1.15.0 dev_dependencies: flutter_test: diff --git a/packages/stream_chat_flutter/lib/src/attachment/attachment_title.dart b/packages/stream_chat_flutter/lib/src/attachment/attachment_title.dart index 176bbbd3..e2686638 100644 --- a/packages/stream_chat_flutter/lib/src/attachment/attachment_title.dart +++ b/packages/stream_chat_flutter/lib/src/attachment/attachment_title.dart @@ -6,9 +6,9 @@ import '../utils.dart'; class AttachmentTitle extends StatelessWidget { const AttachmentTitle({ - Key key, - @required this.attachment, - @required this.messageTheme, + Key? key, + required this.attachment, + required this.messageTheme, }) : super(key: key); final MessageTheme messageTheme; @@ -19,7 +19,7 @@ class AttachmentTitle extends StatelessWidget { return GestureDetector( onTap: () { if (attachment.titleLink != null) { - launchURL(context, attachment.titleLink); + launchURL(context, attachment.titleLink!); } }, child: Padding( @@ -28,17 +28,18 @@ class AttachmentTitle extends StatelessWidget { mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - Text( - attachment.title, - overflow: TextOverflow.ellipsis, - style: messageTheme.messageText.copyWith( - color: StreamChatTheme.of(context).colorTheme.accentBlue, - fontWeight: FontWeight.bold, + if (attachment.title != null) + Text( + attachment.title!, + overflow: TextOverflow.ellipsis, + style: messageTheme.messageText?.copyWith( + color: StreamChatTheme.of(context).colorTheme.accentBlue, + fontWeight: FontWeight.bold, + ), ), - ), if (attachment.titleLink != null || attachment.ogScrapeUrl != null) Text( - Uri.parse(attachment.titleLink ?? attachment.ogScrapeUrl) + Uri.parse(attachment.titleLink ?? attachment.ogScrapeUrl!) .authority .split('.') .reversed diff --git a/packages/stream_chat_flutter/lib/src/attachment/attachment_upload_state_builder.dart b/packages/stream_chat_flutter/lib/src/attachment/attachment_upload_state_builder.dart index 2425b966..2aa71516 100644 --- a/packages/stream_chat_flutter/lib/src/attachment/attachment_upload_state_builder.dart +++ b/packages/stream_chat_flutter/lib/src/attachment/attachment_upload_state_builder.dart @@ -8,55 +8,52 @@ typedef FailedBuilder = Widget Function(BuildContext, String); class AttachmentUploadStateBuilder extends StatelessWidget { final Message message; final Attachment attachment; - final FailedBuilder failedBuilder; - final WidgetBuilder successBuilder; - final InProgressBuilder inProgressBuilder; - final WidgetBuilder preparingBuilder; + final FailedBuilder? failedBuilder; + final WidgetBuilder? successBuilder; + final InProgressBuilder? inProgressBuilder; + final WidgetBuilder? preparingBuilder; const AttachmentUploadStateBuilder({ - Key key, - @required this.message, - @required this.attachment, + Key? key, + required this.message, + required this.attachment, this.failedBuilder, this.successBuilder, this.inProgressBuilder, this.preparingBuilder, - }) : assert(message != null), - assert(attachment != null), - super(key: key); + }) : super(key: key); @override Widget build(BuildContext context) { - if (message.status == null || message.status == MessageSendingStatus.sent) { + if (message.status == MessageSendingStatus.sent) { return Offstage(); } final messageId = message.id; final attachmentId = attachment.id; - var inProgress = inProgressBuilder; - inProgress ??= (context, int sent, int total) { - return _InProgressState( - sent: sent, - total: total, - attachmentId: attachmentId, - ); - }; + final inProgress = inProgressBuilder ?? + (context, int sent, int total) { + return _InProgressState( + sent: sent, + total: total, + attachmentId: attachmentId, + ); + }; - var failed = failedBuilder; - failed ??= (context, error) { - return _FailedState( - error: error, - messageId: messageId, - attachmentId: attachmentId, - ); - }; + final failed = failedBuilder ?? + (context, error) { + return _FailedState( + error: error, + messageId: messageId, + attachmentId: attachmentId, + ); + }; - var success = successBuilder; - success ??= (context) => _SuccessState(); + final success = successBuilder ?? (context) => _SuccessState(); - var preparing = preparingBuilder; - preparing ??= (context) => _PreparingState(attachmentId: attachmentId); + final preparing = preparingBuilder ?? + (context) => _PreparingState(attachmentId: attachmentId); return attachment.uploadState.when( preparing: () => preparing(context), @@ -68,13 +65,13 @@ class AttachmentUploadStateBuilder extends StatelessWidget { } class _IconButton extends StatelessWidget { - final Widget icon; + final Widget? icon; final double iconSize; - final VoidCallback onPressed; - final Color fillColor; + final VoidCallback? onPressed; + final Color? fillColor; const _IconButton({ - Key key, + Key? key, this.icon, this.iconSize = 24.0, this.onPressed, @@ -95,7 +92,9 @@ class _IconButton extends StatelessWidget { onPressed: onPressed, fillColor: fillColor ?? StreamChatTheme.of(context).colorTheme.overlayDark, - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + ), child: icon, ), ); @@ -106,8 +105,8 @@ class _PreparingState extends StatelessWidget { final String attachmentId; const _PreparingState({ - Key key, - @required this.attachmentId, + Key? key, + required this.attachmentId, }) : super(key: key); @override @@ -144,10 +143,10 @@ class _InProgressState extends StatelessWidget { final String attachmentId; const _InProgressState({ - Key key, - @required this.sent, - @required this.total, - @required this.attachmentId, + Key? key, + required this.sent, + required this.total, + required this.attachmentId, }) : super(key: key); @override @@ -179,15 +178,15 @@ class _InProgressState extends StatelessWidget { } class _FailedState extends StatelessWidget { - final String error; + final String? error; final String messageId; final String attachmentId; const _FailedState({ - Key key, + Key? key, this.error, - @required this.messageId, - @required this.attachmentId, + required this.messageId, + required this.attachmentId, }) : super(key: key); @override @@ -203,7 +202,7 @@ class _FailedState extends StatelessWidget { color: theme.colorTheme.white, ), onPressed: () { - return channel.retryAttachmentUpload(messageId, attachmentId); + channel.retryAttachmentUpload(messageId, attachmentId); }, ), Center( @@ -213,7 +212,10 @@ class _FailedState extends StatelessWidget { color: theme.colorTheme.overlayDark.withOpacity(0.6), ), child: Padding( - padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 12), + padding: const EdgeInsets.symmetric( + vertical: 6, + horizontal: 12, + ), child: Text( 'UPLOAD ERROR', style: theme.textTheme.footnote.copyWith( diff --git a/packages/stream_chat_flutter/lib/src/attachment/attachment_widget.dart b/packages/stream_chat_flutter/lib/src/attachment/attachment_widget.dart index bd58462d..d80109c2 100644 --- a/packages/stream_chat_flutter/lib/src/attachment/attachment_widget.dart +++ b/packages/stream_chat_flutter/lib/src/attachment/attachment_widget.dart @@ -13,15 +13,9 @@ extension AttachmentSourceX on AttachmentSource { /// Its prototype depends on the AttachmentSource defined. // ignore: missing_return T when({ - @required T Function() local, - @required T Function() network, + required T Function() local, + required T Function() network, }) { - assert(() { - if (local == null || network == null) { - throw 'check for all possible cases'; - } - return true; - }()); switch (this) { case AttachmentSource.local: return local(); @@ -32,30 +26,32 @@ extension AttachmentSourceX on AttachmentSource { } abstract class AttachmentWidget extends StatelessWidget { - final Size size; + final Size? size; + final AttachmentSource? _source; final Message message; final Attachment attachment; - final AttachmentSource _source; - AttachmentSource get source => _source ?? attachment.file != null - ? AttachmentSource.local - : AttachmentSource.network; + AttachmentSource get source => + _source ?? + (attachment.file != null + ? AttachmentSource.local + : AttachmentSource.network); const AttachmentWidget({ - Key key, - @required this.message, - @required this.attachment, + Key? key, + required this.message, + required this.attachment, this.size, - AttachmentSource source, + AttachmentSource? source, }) : _source = source, super(key: key); } class AttachmentError extends StatelessWidget { - final Size size; + final Size? size; const AttachmentError({ - Key key, + Key? key, this.size, }) : super(key: key); diff --git a/packages/stream_chat_flutter/lib/src/attachment/file_attachment.dart b/packages/stream_chat_flutter/lib/src/attachment/file_attachment.dart index 1003c767..90ef92b1 100644 --- a/packages/stream_chat_flutter/lib/src/attachment/file_attachment.dart +++ b/packages/stream_chat_flutter/lib/src/attachment/file_attachment.dart @@ -11,19 +11,24 @@ import '../upload_progress_indicator.dart'; import 'attachment_widget.dart'; class FileAttachment extends AttachmentWidget { - final Widget title; - final Widget trailing; - final VoidCallback onAttachmentTap; + final Widget? title; + final Widget? trailing; + final VoidCallback? onAttachmentTap; const FileAttachment({ - Key key, - @required Message message, - @required Attachment attachment, - Size size, + Key? key, + required Message message, + required Attachment attachment, + Size? size, this.title, this.trailing, this.onAttachmentTap, - }) : super(key: key, message: message, attachment: attachment, size: size); + }) : super( + key: key, + message: message, + attachment: attachment, + size: size, + ); bool get isVideoAttachment => attachment.title?.mimeType?.type == 'video'; @@ -31,6 +36,7 @@ class FileAttachment extends AttachmentWidget { @override Widget build(BuildContext context) { + final colorTheme = StreamChatTheme.of(context).colorTheme; return Material( child: GestureDetector( onTap: onAttachmentTap, @@ -38,10 +44,10 @@ class FileAttachment extends AttachmentWidget { width: size?.width ?? 100, height: 56.0, decoration: BoxDecoration( - color: StreamChatTheme.of(context).colorTheme.white, + color: colorTheme.white, borderRadius: BorderRadius.circular(12), border: Border.all( - color: StreamChatTheme.of(context).colorTheme.greyWhisper, + color: colorTheme.greyWhisper, ), ), child: Row( @@ -60,7 +66,7 @@ class FileAttachment extends AttachmentWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - attachment?.title ?? 'File', + attachment.title ?? 'File', style: StreamChatTheme.of(context).textTheme.bodyBold, maxLines: 1, overflow: TextOverflow.ellipsis, @@ -93,34 +99,51 @@ class FileAttachment extends AttachmentWidget { type: MaterialType.transparency, shape: _getDefaultShape(context), child: source.when( - local: () => Image.memory( - attachment.file.bytes, - fit: BoxFit.cover, - errorBuilder: (_, obj, trace) { - return getFileTypeImage(attachment.extraData['other']); - }, - ), - network: () => CachedNetworkImage( - imageUrl: attachment.imageUrl ?? - attachment.assetUrl ?? - attachment.thumbUrl, - fit: BoxFit.cover, - errorWidget: (_, obj, trace) { - return getFileTypeImage(attachment.extraData['other']); - }, - placeholder: (_, __) { - return Shimmer.fromColors( - baseColor: StreamChatTheme.of(context).colorTheme.greyGainsboro, - highlightColor: - StreamChatTheme.of(context).colorTheme.whiteSmoke, - child: Image.asset( + local: () { + if (attachment.file?.bytes == null) { + return getFileTypeImage(attachment.extraData['other'] as String?); + } + return Image.memory( + attachment.file!.bytes!, + fit: BoxFit.cover, + errorBuilder: (_, obj, trace) { + return getFileTypeImage( + attachment.extraData['other'] as String?); + }, + ); + }, + network: () { + if ((attachment.imageUrl ?? + attachment.assetUrl ?? + attachment.thumbUrl) == + null) { + return getFileTypeImage(attachment.extraData['other'] as String?); + } + return CachedNetworkImage( + imageUrl: attachment.imageUrl ?? + attachment.assetUrl ?? + attachment.thumbUrl!, + fit: BoxFit.cover, + errorWidget: (_, obj, trace) { + return getFileTypeImage( + attachment.extraData['other'] as String?); + }, + placeholder: (_, __) { + final image = Image.asset( 'images/placeholder.png', fit: BoxFit.cover, package: 'stream_chat_flutter', - ), - ); - }, - ), + ); + + final colorTheme = StreamChatTheme.of(context).colorTheme; + return Shimmer.fromColors( + baseColor: colorTheme.greyGainsboro, + highlightColor: colorTheme.whiteSmoke, + child: image, + ); + }, + ); + }, ), ); } @@ -132,7 +155,7 @@ class FileAttachment extends AttachmentWidget { shape: _getDefaultShape(context), child: source.when( local: () => VideoThumbnailImage( - video: attachment.file.path, + video: attachment.file!.path!, placeholderBuilder: (_) { return Center( child: Container( @@ -144,7 +167,7 @@ class FileAttachment extends AttachmentWidget { }, ), network: () => VideoThumbnailImage( - video: attachment.assetUrl, + video: attachment.assetUrl!, placeholderBuilder: (_) { return Center( child: Container( @@ -158,14 +181,14 @@ class FileAttachment extends AttachmentWidget { ), ); } - return getFileTypeImage(attachment.extraData['mime_type']); + return getFileTypeImage(attachment.extraData['mime_type'] as String?); } Widget _buildButton({ - Widget icon, + Widget? icon, double iconSize = 24.0, - VoidCallback onPressed, - Color fillColor, + VoidCallback? onPressed, + Color? fillColor, }) { return Container( height: iconSize, @@ -189,7 +212,7 @@ class FileAttachment extends AttachmentWidget { final channel = StreamChannel.of(context).channel; final attachmentId = attachment.id; var trailingWidget = trailing; - trailingWidget ??= attachment.uploadState?.when( + trailingWidget ??= attachment.uploadState.when( preparing: () => Padding( padding: const EdgeInsets.all(8.0), child: _buildButton( @@ -220,7 +243,7 @@ class FileAttachment extends AttachmentWidget { icon: StreamSvgIcon.retry(color: theme.colorTheme.white), fillColor: theme.colorTheme.overlayDark, onPressed: () => channel.retryAttachmentUpload( - message?.id, + message.id, attachmentId, ), ), @@ -236,9 +259,7 @@ class FileAttachment extends AttachmentWidget { }, ); - if (message != null && - (message.status == null || - message.status == MessageSendingStatus.sent)) { + if (message.status == MessageSendingStatus.sent) { trailingWidget = IconButton( icon: StreamSvgIcon.cloudDownload(color: theme.colorTheme.black), padding: const EdgeInsets.all(8), @@ -262,11 +283,8 @@ class FileAttachment extends AttachmentWidget { final textStyle = theme.textTheme.footnote.copyWith( color: theme.colorTheme.grey, ); - return attachment.uploadState?.when( + return attachment.uploadState.when( preparing: () { - if (message == null) { - return Text('${fileSize(size, 2)}', style: textStyle); - } return UploadProgressIndicator( uploaded: 0, total: double.maxFinite.toInt(), diff --git a/packages/stream_chat_flutter/lib/src/attachment/giphy_attachment.dart b/packages/stream_chat_flutter/lib/src/attachment/giphy_attachment.dart index 8b8bb4c1..8e2ede71 100644 --- a/packages/stream_chat_flutter/lib/src/attachment/giphy_attachment.dart +++ b/packages/stream_chat_flutter/lib/src/attachment/giphy_attachment.dart @@ -9,30 +9,33 @@ import '../stream_svg_icon.dart'; import 'attachment_widget.dart'; class GiphyAttachment extends AttachmentWidget { - final MessageTheme messageTheme; - final ShowMessageCallback onShowMessage; - final ValueChanged onReturnAction; - final VoidCallback onAttachmentTap; + final ShowMessageCallback? onShowMessage; + final ValueChanged? onReturnAction; + final VoidCallback? onAttachmentTap; const GiphyAttachment({ - Key key, - @required Message message, - @required Attachment attachment, - Size size, - this.messageTheme, + Key? key, + required Message message, + required Attachment attachment, + Size? size, this.onShowMessage, this.onReturnAction, this.onAttachmentTap, - }) : super(key: key, message: message, attachment: attachment, size: size); + }) : super( + key: key, + message: message, + attachment: attachment, + size: size, + ); @override Widget build(BuildContext context) { final imageUrl = attachment.thumbUrl ?? attachment.imageUrl ?? attachment.assetUrl; - if (imageUrl == null && source == AttachmentSource.network) { + if (imageUrl == null) { return AttachmentError(); } - if (attachment.actions != null) { + if (attachment.actions.isNotEmpty) { return _buildSendingAttachment(context, imageUrl); } return _buildSentAttachment(context, imageUrl); @@ -73,7 +76,7 @@ class GiphyAttachment extends AttachmentWidget { if (attachment.title != null) Flexible( child: Text( - attachment.title, + attachment.title!, style: TextStyle( color: StreamChatTheme.of(context) .colorTheme @@ -175,6 +178,7 @@ class GiphyAttachment extends AttachmentWidget { .black .withOpacity(0.5), ), + maxLines: 1, ), ), ), @@ -199,10 +203,11 @@ class GiphyAttachment extends AttachmentWidget { child: Text( 'Send', style: TextStyle( - color: StreamChatTheme.of(context) - .colorTheme - .accentBlue, - fontWeight: FontWeight.bold), + color: StreamChatTheme.of(context) + .colorTheme + .accentBlue, + fontWeight: FontWeight.bold, + ), ), ), ), @@ -259,8 +264,7 @@ class GiphyAttachment extends AttachmentWidget { channel: channel, child: FullScreenMedia( mediaAttachments: [attachment], - userName: message.user.name, - sentAt: message.createdAt, + userName: message.user?.name, message: message, onShowMessage: onShowMessage, ), @@ -268,7 +272,7 @@ class GiphyAttachment extends AttachmentWidget { }, ), ); - if (res != null) onReturnAction(res); + if (res != null) onReturnAction?.call(res); } Widget _buildSentAttachment(BuildContext context, String imageUrl) { @@ -282,14 +286,13 @@ class GiphyAttachment extends AttachmentWidget { channel: channel, child: FullScreenMedia( mediaAttachments: [attachment], - userName: message.user.name, - sentAt: message.createdAt, + userName: message.user?.name, message: message, onShowMessage: onShowMessage, ), ); })); - if (res != null) onReturnAction(res); + if (res != null) onReturnAction!(res); }, child: Stack( children: [ @@ -297,16 +300,17 @@ class GiphyAttachment extends AttachmentWidget { height: size?.height, width: size?.width, placeholder: (_, __) { + final image = Image.asset( + 'images/placeholder.png', + fit: BoxFit.cover, + package: 'stream_chat_flutter', + ); + + final colorTheme = StreamChatTheme.of(context).colorTheme; return Shimmer.fromColors( - baseColor: - StreamChatTheme.of(context).colorTheme.greyGainsboro, - highlightColor: - StreamChatTheme.of(context).colorTheme.whiteSmoke, - child: Image.asset( - 'images/placeholder.png', - fit: BoxFit.cover, - package: 'stream_chat_flutter', - ), + baseColor: colorTheme.greyGainsboro, + highlightColor: colorTheme.whiteSmoke, + child: image, ); }, imageUrl: imageUrl, diff --git a/packages/stream_chat_flutter/lib/src/attachment/image_attachment.dart b/packages/stream_chat_flutter/lib/src/attachment/image_attachment.dart index aa8d1246..1ff9b8a9 100644 --- a/packages/stream_chat_flutter/lib/src/attachment/image_attachment.dart +++ b/packages/stream_chat_flutter/lib/src/attachment/image_attachment.dart @@ -12,33 +12,38 @@ import 'attachment_widget.dart'; class ImageAttachment extends AttachmentWidget { final MessageTheme messageTheme; final bool showTitle; - final ShowMessageCallback onShowMessage; - final ValueChanged onReturnAction; - final VoidCallback onAttachmentTap; + final ShowMessageCallback? onShowMessage; + final ValueChanged? onReturnAction; + final VoidCallback? onAttachmentTap; const ImageAttachment({ - Key key, - @required Message message, - @required Attachment attachment, - Size size, - this.messageTheme, + Key? key, + required Message message, + required Attachment attachment, + required this.messageTheme, + Size? size, this.showTitle = false, this.onShowMessage, this.onReturnAction, this.onAttachmentTap, - }) : super(key: key, message: message, attachment: attachment, size: size); + }) : super( + key: key, + message: message, + attachment: attachment, + size: size, + ); @override Widget build(BuildContext context) { return source.when( local: () { - if (attachment.localUri == null) { + if (attachment.localUri == null || attachment.file?.bytes == null) { return AttachmentError(size: size); } return _buildImageAttachment( context, Image.memory( - attachment.file.bytes, + attachment.file!.bytes!, height: size?.height, width: size?.width, fit: BoxFit.cover, @@ -85,15 +90,16 @@ class ImageAttachment extends AttachmentWidget { height: size?.height, width: size?.width, placeholder: (_, __) { + final image = Image.asset( + 'images/placeholder.png', + fit: BoxFit.cover, + package: 'stream_chat_flutter', + ); + final colorTheme = StreamChatTheme.of(context).colorTheme; return Shimmer.fromColors( - baseColor: StreamChatTheme.of(context).colorTheme.greyGainsboro, - highlightColor: - StreamChatTheme.of(context).colorTheme.whiteSmoke, - child: Image.asset( - 'images/placeholder.png', - fit: BoxFit.cover, - package: 'stream_chat_flutter', - ), + baseColor: colorTheme.greyGainsboro, + highlightColor: colorTheme.whiteSmoke, + child: image, ); }, imageUrl: imageUrl, @@ -109,7 +115,7 @@ class ImageAttachment extends AttachmentWidget { Widget _buildImageAttachment(BuildContext context, Widget imageWidget) { return ConstrainedBox( - constraints: BoxConstraints.loose(size), + constraints: BoxConstraints.loose(size!), child: Column( children: [ Expanded( @@ -127,8 +133,7 @@ class ImageAttachment extends AttachmentWidget { channel: channel, child: FullScreenMedia( mediaAttachments: [attachment], - userName: message.user.name, - sentAt: message.createdAt, + userName: message.user?.name, message: message, onShowMessage: onShowMessage, ), @@ -136,7 +141,7 @@ class ImageAttachment extends AttachmentWidget { }, ), ); - if (result != null) onReturnAction(result); + if (result != null) onReturnAction?.call(result); }, child: imageWidget, ), diff --git a/packages/stream_chat_flutter/lib/src/attachment/video_attachment.dart b/packages/stream_chat_flutter/lib/src/attachment/video_attachment.dart index d2ffc335..4ffbee1b 100644 --- a/packages/stream_chat_flutter/lib/src/attachment/video_attachment.dart +++ b/packages/stream_chat_flutter/lib/src/attachment/video_attachment.dart @@ -9,20 +9,25 @@ import 'attachment_widget.dart'; class VideoAttachment extends AttachmentWidget { final MessageTheme messageTheme; - final ShowMessageCallback onShowMessage; - final ValueChanged onReturnAction; - final VoidCallback onAttachmentTap; + final ShowMessageCallback? onShowMessage; + final ValueChanged? onReturnAction; + final VoidCallback? onAttachmentTap; const VideoAttachment({ - Key key, - @required Message message, - @required Attachment attachment, - Size size, - this.messageTheme, + Key? key, + required Message message, + required Attachment attachment, + required this.messageTheme, + Size? size, this.onShowMessage, this.onReturnAction, this.onAttachmentTap, - }) : super(key: key, message: message, attachment: attachment, size: size); + }) : super( + key: key, + message: message, + attachment: attachment, + size: size, + ); @override Widget build(BuildContext context) { @@ -34,7 +39,7 @@ class VideoAttachment extends AttachmentWidget { return _buildVideoAttachment( context, VideoThumbnailImage( - video: attachment.file.path, + video: attachment.file!.path!, height: size?.height, width: size?.width, fit: BoxFit.cover, @@ -49,7 +54,7 @@ class VideoAttachment extends AttachmentWidget { return _buildVideoAttachment( context, VideoThumbnailImage( - video: attachment.assetUrl, + video: attachment.assetUrl!, height: size?.height, width: size?.width, fit: BoxFit.cover, @@ -62,7 +67,7 @@ class VideoAttachment extends AttachmentWidget { Widget _buildVideoAttachment(BuildContext context, Widget videoWidget) { return ConstrainedBox( - constraints: BoxConstraints.loose(size), + constraints: BoxConstraints.loose(size ?? Size.infinite), child: Column( children: [ Expanded( @@ -77,15 +82,14 @@ class VideoAttachment extends AttachmentWidget { channel: channel, child: FullScreenMedia( mediaAttachments: [attachment], - userName: message.user.name, - sentAt: message.createdAt, + userName: message.user?.name, message: message, onShowMessage: onShowMessage, ), ), ), ); - if (res != null) onReturnAction(res); + if (res != null) onReturnAction?.call(res); }, child: Stack( children: [ diff --git a/packages/stream_chat_flutter/lib/src/attachment_actions_modal.dart b/packages/stream_chat_flutter/lib/src/attachment_actions_modal.dart index 0c8eac7d..a91c18df 100644 --- a/packages/stream_chat_flutter/lib/src/attachment_actions_modal.dart +++ b/packages/stream_chat_flutter/lib/src/attachment_actions_modal.dart @@ -12,7 +12,7 @@ import 'extension.dart'; /// Callback to download an attachment asset typedef AttachmentDownloader = Future Function( Attachment attachment, { - ProgressCallback progressCallback, + ProgressCallback? progressCallback, }); /// Widget that shows the options in the gallery view @@ -21,25 +21,25 @@ class AttachmentActionsModal extends StatelessWidget { final Message message; /// Current page index - final currentIndex; + final int currentIndex; /// Callback to show the message - final VoidCallback onShowMessage; + final VoidCallback? onShowMessage; /// Callback to download images - final AttachmentDownloader imageDownloader; + final AttachmentDownloader? imageDownloader; /// Callback to provide download files - final AttachmentDownloader fileDownloader; + final AttachmentDownloader? fileDownloader; /// Returns a new [AttachmentActionsModal] const AttachmentActionsModal({ - @required this.currentIndex, - this.message, + required this.currentIndex, + required this.message, this.onShowMessage, this.imageDownloader, this.fileDownloader, - }) : assert(currentIndex != null, 'currentIndex cannot be null'); + }); @override Widget build(BuildContext context) { @@ -99,11 +99,16 @@ class AttachmentActionsModal extends StatelessWidget { () { final attachment = message.attachments[currentIndex]; final isImage = attachment.type == 'image'; - final saveFile = fileDownloader ?? _downloadAttachment; - final saveImage = imageDownloader ?? _downloadAttachment; + final Future Function(Attachment, + {void Function(int, int) progressCallback}) + saveFile = fileDownloader ?? _downloadAttachment; + final Future Function(Attachment, + {void Function(int, int) progressCallback}) + saveImage = imageDownloader ?? _downloadAttachment; final downloader = isImage ? saveImage : saveFile; - final progressNotifier = ValueNotifier<_DownloadProgress>( + final progressNotifier = + ValueNotifier<_DownloadProgress?>( _DownloadProgress.initial(), ); @@ -115,7 +120,7 @@ class AttachmentActionsModal extends StatelessWidget { received, ); }, - ).catchError((_) { + ).catchError((e, stk) { progressNotifier.value = null; }); @@ -134,7 +139,7 @@ class AttachmentActionsModal extends StatelessWidget { ); }, ), - if (StreamChat.of(context).user.id == message.user.id) + if (StreamChat.of(context).user?.id == message.user?.id) _buildButton( context, 'Delete', @@ -164,8 +169,10 @@ class AttachmentActionsModal extends StatelessWidget { color: theme.colorTheme.accentRed, ), ] - .map((e) => - Align(alignment: Alignment.centerRight, child: e)) + .map((e) => Align( + alignment: Alignment.centerRight, + child: e, + )) .insertBetween( Container( height: 1, @@ -184,9 +191,9 @@ class AttachmentActionsModal extends StatelessWidget { context, String title, StreamSvgIcon icon, - VoidCallback onTap, { - Color color, - Key key, + VoidCallback? onTap, { + Color? color, + Key? key, }) { return Material( key: key, @@ -215,99 +222,96 @@ class AttachmentActionsModal extends StatelessWidget { Widget _buildDownloadProgressDialog( BuildContext context, - ValueNotifier<_DownloadProgress> progressNotifier, + ValueNotifier<_DownloadProgress?> progressNotifier, ) { final theme = StreamChatTheme.of(context); - return WillPopScope( - onWillPop: () => Future.value(false), - child: ValueListenableBuilder( - valueListenable: progressNotifier, - builder: (_, _DownloadProgress progress, __) { - // Pop the dialog in case the progress is null or it's completed. - if (progress == null || progress?.toProgressIndicatorValue == 1.0) { - Future.delayed( - const Duration(milliseconds: 500), - Navigator.of(context).maybePop, - ); - } - return Material( - type: MaterialType.transparency, - child: Center( - child: Container( - height: 182, - width: 182, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(16), - color: theme.colorTheme.white, - ), - child: Center( - child: progress == null - ? Container( - height: 100, - width: 100, - child: StreamSvgIcon.error( - color: theme.colorTheme.greyGainsboro, - ), - ) - : progress.toProgressIndicatorValue == 1.0 - ? Container( - key: Key('completedIcon'), - height: 160, - width: 160, - child: StreamSvgIcon.check( - color: theme.colorTheme.greyGainsboro, - ), - ) - : Container( - height: 100, - width: 100, - child: Stack( - fit: StackFit.expand, - children: [ - CircularProgressIndicator( - value: progress.toProgressIndicatorValue, - strokeWidth: 8.0, - valueColor: AlwaysStoppedAnimation( - theme.colorTheme.accentBlue, - ), - ), - Center( - child: Text( - '${progress.toPercentage}%', - style: theme.textTheme.headline.copyWith( - color: theme.colorTheme.grey, - ), - ), - ), - ], - ), + return ValueListenableBuilder( + valueListenable: progressNotifier, + builder: (_, _DownloadProgress? progress, __) { + // Pop the dialog in case the progress is null or it's completed. + if (progress == null || progress.toProgressIndicatorValue == 1.0) { + Future.delayed( + const Duration(milliseconds: 500), + () => Navigator.of(context).maybePop(), + ); + } + return Material( + type: MaterialType.transparency, + child: Center( + child: Container( + height: 182, + width: 182, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(16), + color: theme.colorTheme.white, + ), + child: Center( + child: progress == null + ? Container( + height: 100, + width: 100, + child: StreamSvgIcon.error( + color: theme.colorTheme.greyGainsboro, + ), + ) + : progress.toProgressIndicatorValue == 1.0 + ? Container( + key: Key('completedIcon'), + height: 160, + width: 160, + child: StreamSvgIcon.check( + color: theme.colorTheme.greyGainsboro, ), - ), + ) + : Container( + height: 100, + width: 100, + child: Stack( + fit: StackFit.expand, + children: [ + CircularProgressIndicator( + value: progress.toProgressIndicatorValue, + strokeWidth: 8.0, + valueColor: AlwaysStoppedAnimation( + theme.colorTheme.accentBlue, + ), + ), + Center( + child: Text( + '${progress.toPercentage}%', + style: theme.textTheme.headline.copyWith( + color: theme.colorTheme.grey, + ), + ), + ), + ], + ), + ), ), ), - ); - }, - ), + ), + ); + }, ); } - Future _downloadAttachment( + Future _downloadAttachment( Attachment attachment, { - ProgressCallback progressCallback, + ProgressCallback? progressCallback, }) async { - String filePath; + String? filePath; final appDocDir = await getTemporaryDirectory(); await Dio().download( - attachment.assetUrl ?? attachment.imageUrl ?? attachment.thumbUrl, + attachment.assetUrl ?? attachment.imageUrl ?? attachment.thumbUrl!, (Headers responseHeaders) { - final contentType = responseHeaders[Headers.contentTypeHeader]; - final mimeType = contentType.first?.split('/')?.last; + final contentType = responseHeaders[Headers.contentTypeHeader]!; + final mimeType = contentType.first.split('/').last; filePath ??= '${appDocDir.path}/${attachment.id}.$mimeType'; - return filePath; + return filePath!; }, onReceiveProgress: progressCallback, ); - final result = await ImageGallerySaver.saveFile(filePath); + final result = await ImageGallerySaver.saveFile(filePath!); return (result as Map)['filePath']; } } diff --git a/packages/stream_chat_flutter/lib/src/back_button.dart b/packages/stream_chat_flutter/lib/src/back_button.dart index f49fa98c..3d38364a 100644 --- a/packages/stream_chat_flutter/lib/src/back_button.dart +++ b/packages/stream_chat_flutter/lib/src/back_button.dart @@ -6,17 +6,17 @@ import '../stream_chat_flutter.dart'; class StreamBackButton extends StatelessWidget { const StreamBackButton({ - Key key, + Key? key, this.onPressed, this.showUnreads = false, this.cid, }) : super(key: key); - final VoidCallback onPressed; + final VoidCallback? onPressed; final bool showUnreads; /// Channel cid used to retrieve unread count - final String cid; + final String? cid; @override Widget build(BuildContext context) { @@ -34,7 +34,7 @@ class StreamBackButton extends StatelessWidget { hoverElevation: 0, onPressed: () { if (onPressed != null) { - onPressed(); + onPressed!(); } else { Navigator.maybePop(context); } diff --git a/packages/stream_chat_flutter/lib/src/channel_bottom_sheet.dart b/packages/stream_chat_flutter/lib/src/channel_bottom_sheet.dart index 2f3a26b2..c4218632 100644 --- a/packages/stream_chat_flutter/lib/src/channel_bottom_sheet.dart +++ b/packages/stream_chat_flutter/lib/src/channel_bottom_sheet.dart @@ -5,7 +5,7 @@ import 'channel_info.dart'; import 'option_list_tile.dart'; class ChannelBottomSheet extends StatefulWidget { - final VoidCallback onViewInfoTap; + final VoidCallback? onViewInfoTap; ChannelBottomSheet({this.onViewInfoTap}); @@ -18,13 +18,13 @@ class _ChannelBottomSheetState extends State { @override Widget build(BuildContext context) { - var channel = StreamChannel.of(context).channel; + final channel = StreamChannel.of(context).channel; - var members = channel.state.members; + final members = channel.state?.members ?? []; - var userAsMember = - members.firstWhere((e) => e.user.id == StreamChat.of(context).user.id); - var isOwner = userAsMember.role == 'owner'; + final userAsMember = members + .firstWhere((e) => e.user?.id == StreamChat.of(context).user?.id); + final isOwner = userAsMember.role == 'owner'; return Material( color: StreamChatTheme.of(context).colorTheme.white, @@ -73,8 +73,8 @@ class _ChannelBottomSheetState extends State { UserAvatar( user: members .firstWhere( - (e) => e.user.id != userAsMember.user.id) - .user, + (e) => e.user?.id != userAsMember.user?.id) + .user!, constraints: BoxConstraints( maxHeight: 64.0, maxWidth: 64.0, @@ -88,10 +88,11 @@ class _ChannelBottomSheetState extends State { ), Text( members - .firstWhere( - (e) => e.user.id != userAsMember.user.id) - .user - .name, + .firstWhere( + (e) => e.user?.id != userAsMember.user?.id) + .user + ?.name ?? + '', style: StreamChatTheme.of(context).textTheme.footnoteBold, maxLines: 1, @@ -113,7 +114,7 @@ class _ChannelBottomSheetState extends State { child: Column( children: [ UserAvatar( - user: members[index].user, + user: members[index].user!, constraints: BoxConstraints.tightFor( height: 64.0, width: 64.0, @@ -126,7 +127,7 @@ class _ChannelBottomSheetState extends State { height: 6.0, ), Text( - members[index].user.name, + members[index].user?.name ?? '', style: StreamChatTheme.of(context) .textTheme .footnoteBold, @@ -220,7 +221,7 @@ class _ChannelBottomSheetState extends State { color: StreamChatTheme.of(context).colorTheme.accentRed, ), ); - var channel = StreamChannel.of(context).channel; + final channel = StreamChannel.of(context).channel; if (res == true) { await channel.delete(); Navigator.pop(context); @@ -240,7 +241,10 @@ class _ChannelBottomSheetState extends State { ); if (res == true) { final channel = StreamChannel.of(context).channel; - await channel.removeMembers([StreamChat.of(context).user.id]); + final user = StreamChat.of(context).user; + if (user != null) { + await channel.removeMembers([user.id]); + } Navigator.pop(context); } } diff --git a/packages/stream_chat_flutter/lib/src/channel_header.dart b/packages/stream_chat_flutter/lib/src/channel_header.dart index c2927a6e..82270e3b 100644 --- a/packages/stream_chat_flutter/lib/src/channel_header.dart +++ b/packages/stream_chat_flutter/lib/src/channel_header.dart @@ -58,13 +58,13 @@ class ChannelHeader extends StatelessWidget implements PreferredSizeWidget { /// Callback to call when pressing the back button. /// By default it calls [Navigator.pop] - final VoidCallback onBackPressed; + final VoidCallback? onBackPressed; /// Callback to call when the header is tapped. - final VoidCallback onTitleTap; + final VoidCallback? onTitleTap; /// Callback to call when the image is tapped. - final VoidCallback onImageTap; + final VoidCallback? onImageTap; /// If true the typing indicator will be rendered if a user is typing final bool showTypingIndicator; @@ -72,21 +72,21 @@ class ChannelHeader extends StatelessWidget implements PreferredSizeWidget { final bool showConnectionStateTile; /// Title widget - final Widget title; + final Widget? title; /// Subtitle widget - final Widget subtitle; + final Widget? subtitle; /// Leading widget - final Widget leading; + final Widget? leading; /// AppBar actions /// By default it shows the [ChannelImage] - final List actions; + final List? actions; /// Creates a channel header ChannelHeader({ - Key key, + Key? key, this.showBackButton = true, this.onBackPressed, this.onTitleTap, @@ -151,12 +151,12 @@ class ChannelHeader extends StatelessWidget implements PreferredSizeWidget { .channelTheme .channelHeaderTheme .avatarTheme - .borderRadius, + ?.borderRadius, constraints: StreamChatTheme.of(context) .channelTheme .channelHeaderTheme .avatarTheme - .constraints, + ?.constraints, onTap: onImageTap, ), ), diff --git a/packages/stream_chat_flutter/lib/src/channel_image.dart b/packages/stream_chat_flutter/lib/src/channel_image.dart index 4b3edce3..e24fe08b 100644 --- a/packages/stream_chat_flutter/lib/src/channel_image.dart +++ b/packages/stream_chat_flutter/lib/src/channel_image.dart @@ -46,7 +46,7 @@ import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; class ChannelImage extends StatelessWidget { /// Instantiate a new ChannelImage const ChannelImage({ - Key key, + Key? key, this.channel, this.constraints, this.onTap, @@ -56,20 +56,20 @@ class ChannelImage extends StatelessWidget { this.selectionThickness = 4, }) : super(key: key); - final BorderRadius borderRadius; + final BorderRadius? borderRadius; /// The channel to show the image of - final Channel channel; + final Channel? channel; /// The diameter of the image - final BoxConstraints constraints; + final BoxConstraints? constraints; /// The function called when the image is tapped - final VoidCallback onTap; + final VoidCallback? onTap; final bool selected; - final Color selectionColor; + final Color? selectionColor; final double selectionThickness; @@ -81,30 +81,30 @@ class ChannelImage extends StatelessWidget { stream: channel.extraDataStream, initialData: channel.extraData, builder: (context, snapshot) { - String image; - if (snapshot.data?.containsKey('image') == true) { - image = snapshot.data['image']; - } else if (channel.state.members?.length == 2) { - final otherMember = channel.state.members - .firstWhere((member) => member.user.id != streamChat.user.id); + String? image; + if (snapshot.data!.containsKey('image') == true) { + image = snapshot.data!['image']; + } else if (channel.state?.members.length == 2) { + final otherMember = channel.state?.members + .firstWhere((member) => member.user?.id != streamChat.user?.id); return StreamBuilder( - stream: streamChat.client.state.usersStream - .map((users) => users[otherMember.userId]), - initialData: otherMember.user, + stream: streamChat.client.state.usersStream.map( + (users) => users[otherMember?.userId] ?? otherMember!.user!), + initialData: otherMember!.user, builder: (context, snapshot) { return UserAvatar( borderRadius: borderRadius ?? StreamChatTheme.of(context) .channelPreviewTheme .avatarTheme - .borderRadius, - user: snapshot.data ?? otherMember.user, + ?.borderRadius, + user: snapshot.data ?? otherMember.user!, constraints: constraints ?? StreamChatTheme.of(context) .channelPreviewTheme .avatarTheme - .constraints, - onTap: onTap != null ? (_) => onTap() : null, + ?.constraints, + onTap: onTap != null ? (_) => onTap!() : null, selected: selected, selectionColor: selectionColor ?? StreamChatTheme.of(context).colorTheme.accentBlue, @@ -112,25 +112,25 @@ class ChannelImage extends StatelessWidget { ); }); } else { - final images = channel.state.members + final images = channel.state?.members .where((member) => - member.user.id != streamChat.user.id && - member.user.extraData['image'] != null) + member.user?.id != streamChat.user?.id && + member.user?.extraData['image'] != null) .take(4) - .map((e) => e.user.extraData['image'] as String) + .map((e) => e.user?.extraData['image'] as String) .toList(); return GroupImage( - images: images, + images: images ?? [], borderRadius: borderRadius ?? StreamChatTheme.of(context) .channelPreviewTheme .avatarTheme - .borderRadius, + ?.borderRadius, constraints: constraints ?? StreamChatTheme.of(context) .channelPreviewTheme .avatarTheme - .constraints, + ?.constraints, onTap: onTap, selected: selected, selectionColor: selectionColor ?? @@ -144,13 +144,13 @@ class ChannelImage extends StatelessWidget { StreamChatTheme.of(context) .channelPreviewTheme .avatarTheme - .borderRadius, + ?.borderRadius, child: Container( constraints: constraints ?? StreamChatTheme.of(context) .channelPreviewTheme .avatarTheme - .constraints, + ?.constraints, decoration: BoxDecoration( color: StreamChatTheme.of(context).colorTheme.accentBlue, ), @@ -165,7 +165,7 @@ class ChannelImage extends StatelessWidget { return Center( child: Text( snapshot.data?.containsKey('name') ?? false - ? snapshot.data['name'][0] + ? snapshot.data!['name'][0] : '', style: TextStyle( color: StreamChatTheme.of(context) @@ -178,8 +178,10 @@ class ChannelImage extends StatelessWidget { }, fit: BoxFit.cover, ) - : StreamChatTheme.of(context) - .defaultChannelImage(context, channel), + : StreamChatTheme.of(context).defaultChannelImage( + context, + channel, + ), Material( color: Colors.transparent, child: InkWell( @@ -197,14 +199,15 @@ class ChannelImage extends StatelessWidget { StreamChatTheme.of(context) .ownMessageTheme .avatarTheme - .borderRadius) + + ?.borderRadius ?? + BorderRadius.zero) + BorderRadius.circular(selectionThickness), child: Container( constraints: constraints ?? StreamChatTheme.of(context) .ownMessageTheme .avatarTheme - .constraints, + ?.constraints, color: selectionColor ?? StreamChatTheme.of(context).colorTheme.accentBlue, child: Padding( diff --git a/packages/stream_chat_flutter/lib/src/channel_info.dart b/packages/stream_chat_flutter/lib/src/channel_info.dart index 1d6f6fe2..dfcaa749 100644 --- a/packages/stream_chat_flutter/lib/src/channel_info.dart +++ b/packages/stream_chat_flutter/lib/src/channel_info.dart @@ -1,3 +1,4 @@ +import 'package:collection/collection.dart' show IterableExtension; import 'package:flutter/material.dart'; import 'package:jiffy/jiffy.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; @@ -8,14 +9,14 @@ class ChannelInfo extends StatelessWidget { final Channel channel; /// The style of the text displayed - final TextStyle textStyle; + final TextStyle? textStyle; /// If true the typing indicator will be rendered if a user is typing final bool showTypingIndicator; const ChannelInfo({ - Key key, - @required this.channel, + Key? key, + required this.channel, this.textStyle, this.showTypingIndicator = true, }) : super(key: key); @@ -24,8 +25,8 @@ class ChannelInfo extends StatelessWidget { Widget build(BuildContext context) { final client = StreamChat.of(context).client; return StreamBuilder>( - stream: channel.state.membersStream, - initialData: channel.state.members, + stream: channel.state?.membersStream, + initialData: channel.state?.members, builder: (context, snapshot) { return ConnectionStatusBuilder( statusBuilder: (context, status) { @@ -45,12 +46,13 @@ class ChannelInfo extends StatelessWidget { ); } - Widget _buildConnectedTitleState(BuildContext context, List members) { + Widget _buildConnectedTitleState( + BuildContext context, List? members) { var alternativeWidget; - if (channel.memberCount != null && channel.memberCount > 2) { + if (channel.memberCount != null && channel.memberCount! > 2) { var text = '${channel.memberCount} Members'; - final watcherCount = channel.state.watcherCount ?? 0; + final watcherCount = channel.state?.watcherCount ?? 0; if (watcherCount > 0) text += ' $watcherCount Online'; alternativeWidget = Text( text, @@ -60,20 +62,19 @@ class ChannelInfo extends StatelessWidget { .subtitle, ); } else { - final otherMember = members.firstWhere( - (element) => element.userId != StreamChat.of(context).user.id, - orElse: () => null, + final otherMember = members?.firstWhereOrNull( + (element) => element.userId != StreamChat.of(context).user?.id, ); if (otherMember != null) { - if (otherMember.user.online) { + if (otherMember.user?.online == true) { alternativeWidget = Text( 'Online', style: textStyle, ); } else { alternativeWidget = Text( - 'Last seen ${Jiffy(otherMember.user.lastActive).fromNow()}', + 'Last seen ${Jiffy(otherMember.user?.lastActive).fromNow()}', style: textStyle, ); } @@ -112,7 +113,9 @@ class ChannelInfo extends StatelessWidget { } Widget _buildDisconnectedTitleState( - BuildContext context, StreamChatClient client) { + BuildContext context, + StreamChatClient client, + ) { return Row( mainAxisAlignment: MainAxisAlignment.center, children: [ @@ -131,11 +134,11 @@ class ChannelInfo extends StatelessWidget { ), onPressed: () async { await client.disconnect(); - return client.connect(); + await client.connect(); }, child: Text( 'Try Again', - style: textStyle.copyWith( + style: textStyle?.copyWith( color: StreamChatTheme.of(context).colorTheme.accentBlue, ), ), diff --git a/packages/stream_chat_flutter/lib/src/channel_list_header.dart b/packages/stream_chat_flutter/lib/src/channel_list_header.dart index 88b329e6..f3889e22 100644 --- a/packages/stream_chat_flutter/lib/src/channel_list_header.dart +++ b/packages/stream_chat_flutter/lib/src/channel_list_header.dart @@ -10,7 +10,7 @@ import 'connection_status_builder.dart'; import 'info_tile.dart'; import 'stream_chat.dart'; -typedef _TitleBuilder = Widget Function( +typedef TitleBuilder = Widget Function( BuildContext context, ConnectionStatus status, StreamChatClient client, @@ -50,7 +50,7 @@ typedef _TitleBuilder = Widget Function( class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget { /// Instantiates a ChannelListHeader const ChannelListHeader({ - Key key, + Key? key, this.client, this.titleBuilder, this.onUserAvatarTap, @@ -63,32 +63,32 @@ class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget { }) : super(key: key); /// Pass this if you don't have a [StreamChatClient] in your widget tree. - final StreamChatClient client; + final StreamChatClient? client; /// Use this to build your own title as per different [ConnectionStatus] - final _TitleBuilder titleBuilder; + final TitleBuilder? titleBuilder; /// Callback to call when pressing the user avatar button. /// By default it calls Scaffold.of(context).openDrawer() - final Function(User) onUserAvatarTap; + final Function(User)? onUserAvatarTap; /// Callback to call when pressing the new chat button. - final VoidCallback onNewChatButtonTap; + final VoidCallback? onNewChatButtonTap; final bool showConnectionStateTile; - final VoidCallback preNavigationCallback; + final VoidCallback? preNavigationCallback; /// Subtitle widget - final Widget subtitle; + final Widget? subtitle; /// Leading widget /// By default it shows the logged in user avatar - final Widget leading; + final Widget? leading; /// AppBar actions /// By default it shows the new chat button - final List actions; + final List? actions; @override Widget build(BuildContext context) { @@ -123,25 +123,27 @@ class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget { centerTitle: true, leading: leading ?? Center( - child: UserAvatar( - user: user, - showOnlineStatus: false, - onTap: onUserAvatarTap ?? - (_) { - if (preNavigationCallback != null) { - preNavigationCallback(); - } - Scaffold.of(context).openDrawer(); - }, - borderRadius: StreamChatTheme.of(context) - .channelListHeaderTheme - .avatarTheme - .borderRadius, - constraints: StreamChatTheme.of(context) - .channelListHeaderTheme - .avatarTheme - .constraints, - ), + child: user != null + ? UserAvatar( + user: user, + showOnlineStatus: false, + onTap: onUserAvatarTap ?? + (_) { + if (preNavigationCallback != null) { + preNavigationCallback!(); + } + Scaffold.of(context).openDrawer(); + }, + borderRadius: StreamChatTheme.of(context) + .channelListHeaderTheme + .avatarTheme + ?.borderRadius, + constraints: StreamChatTheme.of(context) + .channelListHeaderTheme + .avatarTheme + ?.constraints, + ) + : Offstage(), ), actions: actions ?? [ @@ -181,7 +183,7 @@ class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget { Builder( builder: (context) { if (titleBuilder != null) { - return titleBuilder(context, status, _client); + return titleBuilder!(context, status, _client); } switch (status) { case ConnectionStatus.connected: @@ -225,40 +227,46 @@ class ChannelListHeader extends StatelessWidget implements PreferredSizeWidget { SizedBox(width: 10), Text( 'Searching for Network', - style: - StreamChatTheme.of(context).channelListHeaderTheme.title.copyWith( - fontSize: 16, - fontWeight: FontWeight.bold, - ), + style: StreamChatTheme.of(context) + .channelListHeaderTheme + .title + ?.copyWith( + fontSize: 16, + fontWeight: FontWeight.bold, + ), ), ], ); } Widget _buildDisconnectedTitleState( - BuildContext context, StreamChatClient client) { + BuildContext context, + StreamChatClient client, + ) { return Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( 'Offline...', - style: - StreamChatTheme.of(context).channelListHeaderTheme.title.copyWith( - fontSize: 16, - fontWeight: FontWeight.bold, - ), + style: StreamChatTheme.of(context) + .channelListHeaderTheme + .title + ?.copyWith( + fontSize: 16, + fontWeight: FontWeight.bold, + ), ), TextButton( onPressed: () async { await client.disconnect(); - return client.connect(); + await client.connect(); }, child: Text( 'Try Again', style: StreamChatTheme.of(context) .channelListHeaderTheme .title - .copyWith( + ?.copyWith( fontSize: 16, fontWeight: FontWeight.bold, color: StreamChatTheme.of(context).colorTheme.accentBlue, diff --git a/packages/stream_chat_flutter/lib/src/channel_list_view.dart b/packages/stream_chat_flutter/lib/src/channel_list_view.dart index 9751ad6d..6d317d97 100644 --- a/packages/stream_chat_flutter/lib/src/channel_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/channel_list_view.dart @@ -1,3 +1,4 @@ +import 'package:collection/collection.dart' show IterableExtension; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_slidable/flutter_slidable.dart'; @@ -11,7 +12,7 @@ import 'channel_bottom_sheet.dart'; import 'channel_preview.dart'; /// Callback called when tapping on a channel -typedef ChannelTapCallback = void Function(Channel, Widget); +typedef ChannelTapCallback = void Function(Channel, Widget?); /// Builder used to create a custom [ChannelPreview] from a [Channel] typedef ChannelPreviewBuilder = Widget Function(BuildContext, Channel); @@ -54,7 +55,7 @@ typedef ViewInfoCallback = void Function(Channel); class ChannelListView extends StatefulWidget { /// Instantiate a new ChannelListView ChannelListView({ - Key key, + Key? key, this.filter, this.options, this.sort, @@ -84,73 +85,73 @@ class ChannelListView extends StatefulWidget { /// The query filters to use. /// You can query on any of the custom fields you've defined on the [Channel]. /// You can also filter other built-in channel fields. - final Map filter; + final Filter? filter; /// Query channels options. /// /// state: if true returns the Channel state /// watch: if true listen to changes to this Channel in real time. - final Map options; + final Map? options; /// The sorting used for the channels matching the filters. /// Sorting is based on field and direction, multiple sorting options can be provided. /// You can sort based on last_updated, last_message_at, updated_at, created_at or member_count. /// Direction can be ascending or descending. - final List> sort; + final List>? sort; /// Pagination parameters /// limit: the number of channels to return (max is 30) /// offset: the offset (max is 1000) /// message_limit: how many messages should be included to each channel - final PaginationParams pagination; + final PaginationParams? pagination; /// Function called when tapping on a channel /// By default it calls [Navigator.push] building a [MaterialPageRoute] /// with the widget [channelWidget] as child. - final ChannelTapCallback onChannelTap; + final ChannelTapCallback? onChannelTap; /// Function called when long pressing on a channel - final Function(Channel) onChannelLongPress; + final Function(Channel)? onChannelLongPress; /// Widget used when opening a channel - final Widget channelWidget; + final Widget? channelWidget; /// Builder used to create a custom channel preview - final ChannelPreviewBuilder channelPreviewBuilder; + final ChannelPreviewBuilder? channelPreviewBuilder; /// Builder used to create a custom item separator - final Function(BuildContext, int) separatorBuilder; + final Function(BuildContext, int)? separatorBuilder; /// The function called when the image is tapped - final Function(Channel) onImageTap; + final Function(Channel)? onImageTap; /// Set it to false to disable the pull-to-refresh widget final bool pullToRefresh; /// Callback used in the default empty list widget - final VoidCallback onStartChatPressed; + final VoidCallback? onStartChatPressed; /// The number of children in the cross axis. final int crossAxisCount; /// The amount of space by which to inset the children. - final EdgeInsetsGeometry padding; + final EdgeInsetsGeometry? padding; final List selectedChannels; - final ViewInfoCallback onViewInfoTap; + final ViewInfoCallback? onViewInfoTap; /// The builder that will be used in case of error - final ErrorBuilder errorBuilder; + final ErrorBuilder? errorBuilder; /// The builder that will be used in case of loading - final WidgetBuilder loadingBuilder; + final WidgetBuilder? loadingBuilder; /// The builder which is used when list of channels loads - final Function(BuildContext, List) listBuilder; + final Function(BuildContext, List)? listBuilder; /// The builder used when the channel list is empty. - final WidgetBuilder emptyBuilder; + final WidgetBuilder? emptyBuilder; @override _ChannelListViewState createState() => _ChannelListViewState(); @@ -164,7 +165,10 @@ class _ChannelListViewState extends State { @override Widget build(BuildContext context) { Widget child = ChannelListCore( - pagination: widget.pagination, + pagination: widget.pagination ?? + const PaginationParams( + limit: 25, + ), options: widget.options, sort: widget.sort, filter: widget.filter, @@ -177,26 +181,27 @@ class _ChannelListViewState extends State { if (widget.pullToRefresh) { child = RefreshIndicator( - onRefresh: () => _channelListController.loadData(), + onRefresh: () => _channelListController.loadData!(), child: child, ); } return LazyLoadScrollView( - onEndOfPage: () => _channelListController.paginateData(), + onEndOfPage: () => _channelListController.paginateData!(), child: child, ); } Widget _buildListView(BuildContext context, List channels) { - Widget child; + late Widget child; if (channels.isNotEmpty) { if (widget.crossAxisCount > 1) { child = GridView.builder( padding: widget.padding, gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: widget.crossAxisCount), + crossAxisCount: widget.crossAxisCount, + ), itemCount: channels.length, physics: AlwaysScrollableScrollPhysics(), itemBuilder: (context, index) { @@ -211,7 +216,7 @@ class _ChannelListViewState extends State { channels.isNotEmpty ? channels.length + 1 : channels.length, separatorBuilder: (_, index) { if (widget.separatorBuilder != null) { - return widget.separatorBuilder(context, index); + return widget.separatorBuilder!(context, index); } return _separatorBuilder(context, index); }, @@ -317,7 +322,7 @@ class _ChannelListViewState extends State { if (widget.crossAxisCount == 1) { if (i % 2 != 0) { if (widget.separatorBuilder != null) { - return widget.separatorBuilder(context, i); + return widget.separatorBuilder!(context, i); } return _separatorBuilder(context, i); } @@ -447,7 +452,7 @@ class _ChannelListViewState extends State { style: Theme.of(context).textTheme.headline6, ), TextButton( - onPressed: () => _channelListController.loadData(), + onPressed: () => _channelListController.loadData!(), child: Text('Retry'), ), ], @@ -459,24 +464,7 @@ class _ChannelListViewState extends State { final channelsProvider = ChannelsBloc.of(context); if (i < channels.length) { final channel = channels[i]; - ChannelTapCallback onTap; - if (widget.onChannelTap != null) { - onTap = widget.onChannelTap; - } else { - onTap = (client, _) { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) { - return StreamChannel( - channel: client, - child: widget.channelWidget, - ); - }, - ), - ); - }; - } + final onTap = _getChannelTap(context); final backgroundColor = StreamChatTheme.of(context).colorTheme.whiteSmoke; return StreamChannel( @@ -509,7 +497,7 @@ class _ChannelListViewState extends State { channel: channel, child: ChannelBottomSheet( onViewInfoTap: () { - widget.onViewInfoTap(channel); + widget.onViewInfoTap?.call(channel); }, ), ); @@ -520,9 +508,9 @@ class _ChannelListViewState extends State { if ([ 'admin', 'owner', - ].contains(channel.state.members - .firstWhere((m) => m.userId == channel.client.state.user.id, - orElse: () => null) + ].contains(channel.state!.members + .firstWhereOrNull( + (m) => m.userId == channel.client.state.user?.id) ?.role)) IconSlideAction( color: backgroundColor, @@ -567,10 +555,35 @@ class _ChannelListViewState extends State { } } - Widget _gridItemBuilder(BuildContext context, int i, List channels) { - var channel = channels[i]; + ChannelTapCallback _getChannelTap(BuildContext context) { + ChannelTapCallback onTap; + if (widget.onChannelTap != null) { + onTap = widget.onChannelTap!; + } else { + onTap = (client, _) { + if (widget.channelWidget == null) { + return; + } + Navigator.push( + context, + MaterialPageRoute( + builder: (context) { + return StreamChannel( + channel: client, + child: widget.channelWidget!, + ); + }, + ), + ); + }; + } + return onTap; + } - var selected = widget.selectedChannels.contains(channel); + Widget _gridItemBuilder(BuildContext context, int i, List channels) { + final channel = channels[i]; + + final selected = widget.selectedChannels.contains(channel); return Container( key: ValueKey('CHANNEL-${channel.id}'), @@ -586,7 +599,7 @@ class _ChannelListViewState extends State { width: 64, height: 64, ), - onTap: () => widget.onChannelTap(channel, null), + onTap: () => _getChannelTap(context), ), SizedBox(height: 7), Padding( @@ -628,7 +641,7 @@ class _ChannelListViewState extends State { ), ); } - return snapshot.data + return snapshot.data! ? Center( child: Padding( padding: const EdgeInsets.all(16.0), @@ -644,7 +657,7 @@ class _ChannelListViewState extends State { return Container( height: 1, - color: effect.color.withOpacity(effect.alpha ?? 1.0), + color: effect.color!.withOpacity(effect.alpha ?? 1.0), ); } } diff --git a/packages/stream_chat_flutter/lib/src/channel_name.dart b/packages/stream_chat_flutter/lib/src/channel_name.dart index 53817563..48657419 100644 --- a/packages/stream_chat_flutter/lib/src/channel_name.dart +++ b/packages/stream_chat_flutter/lib/src/channel_name.dart @@ -10,12 +10,12 @@ import '../stream_chat_flutter.dart'; class ChannelName extends StatelessWidget { /// Instantiate a new ChannelName const ChannelName({ - Key key, + Key? key, this.textStyle, }) : super(key: key); /// The style of the text displayed - final TextStyle textStyle; + final TextStyle? textStyle; @override Widget build(BuildContext context) { @@ -26,31 +26,31 @@ class ChannelName extends StatelessWidget { stream: channel.extraDataStream, initialData: channel.extraData, builder: (context, snapshot) { - return _buildName(snapshot.data, channel.state.members, client); + return _buildName(snapshot.data!, channel.state?.members, client); }, ); } Widget _buildName( Map extraData, - List members, + List? members, StreamChatState client, ) { return LayoutBuilder( builder: (context, constraints) { - String title; + String? title; if (extraData['name'] == null) { final otherMembers = - members.where((member) => member.userId != client.user.id); - if (otherMembers.length == 1) { - title = otherMembers.first.user.name; - } else if (otherMembers.isNotEmpty) { + members?.where((member) => member.userId != client.user!.id); + if (otherMembers?.length == 1) { + title = otherMembers!.first.user?.name; + } else if (otherMembers?.isNotEmpty == true) { final maxWidth = constraints.maxWidth; - final maxChars = maxWidth / textStyle.fontSize; + final maxChars = maxWidth / (textStyle?.fontSize ?? 1); var currentChars = 0; final currentMembers = []; - otherMembers.forEach((element) { - final newLength = currentChars + element.user.name.length; + otherMembers!.forEach((element) { + final newLength = currentChars + (element.user?.name.length ?? 0); if (newLength < maxChars) { currentChars = newLength; currentMembers.add(element); @@ -60,7 +60,7 @@ class ChannelName extends StatelessWidget { final exceedingMembers = otherMembers.length - currentMembers.length; title = - '${currentMembers.map((e) => e.user.name).join(', ')} ${exceedingMembers > 0 ? '+ $exceedingMembers' : ''}'; + '${currentMembers.map((e) => e.user?.name).join(', ')} ${exceedingMembers > 0 ? '+ $exceedingMembers' : ''}'; } else { title = 'No title'; } @@ -69,7 +69,7 @@ class ChannelName extends StatelessWidget { } return Text( - title, + title!, style: textStyle, overflow: TextOverflow.ellipsis, ); diff --git a/packages/stream_chat_flutter/lib/src/channel_preview.dart b/packages/stream_chat_flutter/lib/src/channel_preview.dart index 959342a6..f28e9368 100644 --- a/packages/stream_chat_flutter/lib/src/channel_preview.dart +++ b/packages/stream_chat_flutter/lib/src/channel_preview.dart @@ -1,3 +1,4 @@ +import 'package:collection/collection.dart' show IterableExtension; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'package:jiffy/jiffy.dart'; @@ -20,35 +21,35 @@ import 'channel_name.dart'; /// Modify it to change the widget appearance. class ChannelPreview extends StatelessWidget { /// Function called when tapping this widget - final void Function(Channel) onTap; + final void Function(Channel)? onTap; /// Function called when long pressing this widget - final void Function(Channel) onLongPress; + final void Function(Channel)? onLongPress; /// Channel displayed final Channel channel; /// The function called when the image is tapped - final VoidCallback onImageTap; + final VoidCallback? onImageTap; /// Widget rendering the title - final Widget title; + final Widget? title; /// Widget rendering the subtitle - final Widget subtitle; + final Widget? subtitle; /// Widget rendering the leading element, by default it shows the [ChannelImage] - final Widget leading; + final Widget? leading; /// Widget rendering the trailing element, by default it shows the last message date - final Widget trailing; + final Widget? trailing; /// Widget rendering the sending indicator, by default it uses the [SendingIndicator] widget - final Widget sendingIndicator; + final Widget? sendingIndicator; ChannelPreview({ - @required this.channel, - Key key, + required this.channel, + Key? key, this.onTap, this.onLongPress, this.onImageTap, @@ -67,7 +68,7 @@ class ChannelPreview extends StatelessWidget { initialData: channel.isMuted, builder: (context, snapshot) { return Opacity( - opacity: snapshot.data ? 0.5 : 1, + opacity: snapshot.data! ? 0.5 : 1, child: ListTile( visualDensity: VisualDensity.compact, contentPadding: const EdgeInsets.symmetric( @@ -75,12 +76,12 @@ class ChannelPreview extends StatelessWidget { ), onTap: () { if (onTap != null) { - onTap(channel); + onTap!(channel); } }, onLongPress: () { if (onLongPress != null) { - onLongPress(channel); + onLongPress!(channel); } }, leading: leading ?? @@ -97,13 +98,13 @@ class ChannelPreview extends StatelessWidget { ), ), StreamBuilder>( - stream: channel.state.membersStream, - initialData: channel.state.members, + stream: channel.state?.membersStream, + initialData: channel.state?.members, builder: (context, snapshot) { if (!snapshot.hasData || - snapshot.data.isEmpty || - !snapshot.data.any((Member e) => - e.user.id == channel.client.state.user.id)) { + snapshot.data!.isEmpty || + !snapshot.data!.any((Member e) => + e.user!.id == channel.client.state.user?.id)) { return SizedBox(); } return UnreadIndicator( @@ -120,24 +121,24 @@ class ChannelPreview extends StatelessWidget { sendingIndicator ?? Builder( builder: (context) { - final lastMessage = channel.state.messages.lastWhere( + final lastMessage = + channel.state?.messages.lastWhereOrNull( (m) => !m.isDeleted && m.shadowed != true, - orElse: () => null, ); if (lastMessage?.user?.id == - StreamChat.of(context).user.id) { + StreamChat.of(context).user?.id) { return Padding( padding: const EdgeInsets.only(right: 4.0), child: SendingIndicator( - message: lastMessage, + message: lastMessage!, size: channelPreviewTheme.indicatorIconSize, - isMessageRead: channel.state.read + isMessageRead: channel.state!.read ?.where((element) => element.user.id != - channel.client.state.user.id) - ?.where((element) => element.lastRead + channel.client.state.user!.id) + .where((element) => element.lastRead .isAfter(lastMessage.createdAt)) - ?.isNotEmpty == + .isNotEmpty == true, ), ); @@ -154,14 +155,14 @@ class ChannelPreview extends StatelessWidget { } Widget _buildDate(BuildContext context) { - return StreamBuilder( + return StreamBuilder( stream: channel.lastMessageAtStream, initialData: channel.lastMessageAt, builder: (context, snapshot) { if (!snapshot.hasData) { return SizedBox(); } - final lastMessageAt = snapshot.data.toLocal(); + final lastMessageAt = snapshot.data!.toLocal(); String stringDate; final now = DateTime.now(); @@ -211,60 +212,58 @@ class ChannelPreview extends StatelessWidget { } Widget _buildLastMessage(BuildContext context) { - return StreamBuilder>( - stream: channel.state.messagesStream, - initialData: channel.state.messages, + return StreamBuilder?>( + stream: channel.state!.messagesStream, + initialData: channel.state!.messages, builder: (context, snapshot) { - final lastMessage = snapshot.data?.lastWhere( - (m) => m.shadowed != true && !m.isDeleted, - orElse: () => null); + final lastMessage = snapshot.data + ?.lastWhereOrNull((m) => m.shadowed != true && !m.isDeleted); if (lastMessage == null) { return SizedBox(); } var text = lastMessage.text; - if (lastMessage.attachments != null) { - final parts = [ - ...lastMessage.attachments.map((e) { - if (e.type == 'image') { - return '📷'; - } else if (e.type == 'video') { - return '🎬'; - } else if (e.type == 'giphy') { - return '[GIF]'; - } - return e == lastMessage.attachments.last - ? (e.title ?? 'File') - : '${e.title ?? 'File'} , '; - }).where((e) => e != null), - lastMessage.text ?? '', - ]; + final parts = [ + ...lastMessage.attachments.map((e) { + if (e.type == 'image') { + return '📷'; + } else if (e.type == 'video') { + return '🎬'; + } else if (e.type == 'giphy') { + return '[GIF]'; + } + return e == lastMessage.attachments.last + ? (e.title ?? 'File') + : '${e.title ?? 'File'} , '; + }), + lastMessage.text ?? '', + ]; - text = parts.join(' '); - } + text = parts.join(' '); return Text.rich( _getDisplayText( text, lastMessage.mentionedUsers, lastMessage.attachments, - StreamChatTheme.of(context).channelPreviewTheme.subtitle.copyWith( + StreamChatTheme.of(context).channelPreviewTheme.subtitle?.copyWith( color: StreamChatTheme.of(context) .channelPreviewTheme .subtitle - .color, + ?.color, fontStyle: (lastMessage.isSystem || lastMessage.isDeleted) ? FontStyle.italic : FontStyle.normal), - StreamChatTheme.of(context).channelPreviewTheme.subtitle.copyWith( - color: StreamChatTheme.of(context) - .channelPreviewTheme - .subtitle - .color, - fontStyle: (lastMessage.isSystem || lastMessage.isDeleted) - ? FontStyle.italic - : FontStyle.normal, - fontWeight: FontWeight.bold), + StreamChatTheme.of(context).channelPreviewTheme.subtitle?.copyWith( + color: StreamChatTheme.of(context) + .channelPreviewTheme + .subtitle + ?.color, + fontStyle: (lastMessage.isSystem || lastMessage.isDeleted) + ? FontStyle.italic + : FontStyle.normal, + fontWeight: FontWeight.bold, + ), ), maxLines: 1, overflow: TextOverflow.ellipsis, @@ -274,29 +273,28 @@ class ChannelPreview extends StatelessWidget { } TextSpan _getDisplayText( - String text, - List mentions, - List attachments, - TextStyle normalTextStyle, - TextStyle mentionsTextStyle) { - var textList = text.split(' '); - var resList = []; - for (var e in textList) { - if (mentions != null && - mentions.isNotEmpty && + String text, + List mentions, + List attachments, + TextStyle? normalTextStyle, + TextStyle? mentionsTextStyle, + ) { + final textList = text.split(' '); + final resList = []; + for (final e in textList) { + if (mentions.isNotEmpty && mentions.any((element) => '@${element.name}' == e)) { resList.add(TextSpan( text: '$e ', style: mentionsTextStyle, )); - } else if (attachments != null && - attachments.isNotEmpty && + } else if (attachments.isNotEmpty && attachments .where((e) => e.title != null) .any((element) => element.title == e)) { resList.add(TextSpan( text: '$e ', - style: normalTextStyle.copyWith(fontStyle: FontStyle.italic), + style: normalTextStyle?.copyWith(fontStyle: FontStyle.italic), )); } else { resList.add(TextSpan( diff --git a/packages/stream_chat_flutter/lib/src/connection_status_builder.dart b/packages/stream_chat_flutter/lib/src/connection_status_builder.dart index 01632b1b..620c41ac 100644 --- a/packages/stream_chat_flutter/lib/src/connection_status_builder.dart +++ b/packages/stream_chat_flutter/lib/src/connection_status_builder.dart @@ -11,26 +11,25 @@ import 'stream_chat.dart'; class ConnectionStatusBuilder extends StatelessWidget { /// Creates a new ConnectionStatusBuilder const ConnectionStatusBuilder({ - Key key, - @required this.statusBuilder, + Key? key, + required this.statusBuilder, this.initialStatus = ConnectionStatus.disconnected, this.connectionStatusStream, this.errorBuilder, this.loadingBuilder, - }) : assert(statusBuilder != null), - super(key: key); + }) : super(key: key); /// The connection status that will be used to create the initial snapshot. final ConnectionStatus initialStatus; /// The asynchronous computation to which this builder is currently connected. - final Stream connectionStatusStream; + final Stream? connectionStatusStream; /// The builder that will be used in case of error - final Widget Function(BuildContext context, Object error) errorBuilder; + final Widget Function(BuildContext context, Object? error)? errorBuilder; /// The builder that will be used in case of loading - final WidgetBuilder loadingBuilder; + final WidgetBuilder? loadingBuilder; /// The builder that will be used in case of data final Widget Function(BuildContext context, ConnectionStatus status) @@ -46,15 +45,15 @@ class ConnectionStatusBuilder extends StatelessWidget { builder: (context, snapshot) { if (snapshot.hasError) { if (errorBuilder != null) { - return errorBuilder(context, snapshot.error); + return errorBuilder!(context, snapshot.error); } return Offstage(); } if (!snapshot.hasData) { - if (loadingBuilder != null) return loadingBuilder(context); + if (loadingBuilder != null) return loadingBuilder!(context); return Offstage(); } - return statusBuilder(context, snapshot.data); + return statusBuilder(context, snapshot.data!); }, ); } diff --git a/packages/stream_chat_flutter/lib/src/date_divider.dart b/packages/stream_chat_flutter/lib/src/date_divider.dart index 20b8151d..3674e085 100644 --- a/packages/stream_chat_flutter/lib/src/date_divider.dart +++ b/packages/stream_chat_flutter/lib/src/date_divider.dart @@ -8,8 +8,8 @@ class DateDivider extends StatelessWidget { final bool uppercase; const DateDivider({ - Key key, - @required this.dateTime, + Key? key, + required this.dateTime, this.uppercase = false, }) : super(key: key); diff --git a/packages/stream_chat_flutter/lib/src/deleted_message.dart b/packages/stream_chat_flutter/lib/src/deleted_message.dart index bd014770..b3071904 100644 --- a/packages/stream_chat_flutter/lib/src/deleted_message.dart +++ b/packages/stream_chat_flutter/lib/src/deleted_message.dart @@ -5,8 +5,8 @@ import 'package:stream_chat_flutter/src/stream_chat_theme.dart'; class DeletedMessage extends StatelessWidget { const DeletedMessage({ - Key key, - @required this.messageTheme, + Key? key, + required this.messageTheme, this.borderRadiusGeometry, this.shape, this.borderSide, @@ -17,13 +17,13 @@ class DeletedMessage extends StatelessWidget { final MessageTheme messageTheme; /// The border radius of the message text - final BorderRadiusGeometry borderRadiusGeometry; + final BorderRadiusGeometry? borderRadiusGeometry; /// The shape of the message text - final ShapeBorder shape; + final ShapeBorder? shape; /// The borderside of the message text - final BorderSide borderSide; + final BorderSide? borderSide; /// If true the widget will be mirrored final bool reverse; @@ -61,9 +61,9 @@ class DeletedMessage extends StatelessWidget { alignment: Alignment.center, child: Text( 'Message deleted', - style: messageTheme.messageText.copyWith( + style: messageTheme.messageText?.copyWith( fontStyle: FontStyle.italic, - color: messageTheme.createdAt.color, + color: messageTheme.createdAt?.color, ), ), ), diff --git a/packages/stream_chat_flutter/lib/src/emoji/emoji.dart b/packages/stream_chat_flutter/lib/src/emoji/emoji.dart new file mode 100644 index 00000000..617e372a --- /dev/null +++ b/packages/stream_chat_flutter/lib/src/emoji/emoji.dart @@ -0,0 +1,114464 @@ +// Copyright 2020 Naji. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Naji nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import 'package:collection/collection.dart' show IterableExtension; + +/// All Groups +enum EmojiGroup { + smileysEmotion, + activities, + peopleBody, + objects, + travelPlaces, + component, + animalsNature, + foodDrink, + symbols, + flags +} + +/// All Subgroups +enum EmojiSubgroup { + faceSmiling, + faceAffection, + faceSleepy, + faceTongue, + faceNeutralSkeptical, + faceGlasses, + faceHat, + faceConcerned, + faceNegative, + faceUnwell, + faceHand, + faceCostume, + event, + catFace, + hands, + handFingersClosed, + handFingersPartial, + handSingleFinger, + handFingersOpen, + bodyParts, + handProp, + clothing, + emotion, + personSymbol, + person, + personRole, + personFantasy, + personGesture, + personActivity, + family, + artsCrafts, + office, + hotel, + skyWeather, + hairStyle, + animalMammal, + animalAmphibian, + monkeyFace, + animalBird, + animalBug, + animalReptile, + animalMarine, + foodMarine, + plantOther, + foodVegetable, + placeBuilding, + plantFlower, + placeMap, + foodFruit, + foodAsian, + foodPrepared, + foodSweet, + drink, + dishware, + sport, + tool, + game, + transportGround, + personSport, + transportAir, + personResting, + awardMedal, + placeOther, + lightVideo, + music, + musicalInstrument, + transportWater, + otherObject, + placeGeographic, + placeReligious, + time, + phone, + computer, + science, + household, + money, + medical, + transportSign, + lock, + mail, + bookPaper, + sound, + writing, + religion, + zodiac, + alphanum, + warning, + avSymbol, + otherSymbol, + punctuation, + geometric, + keycap, + arrow, + math, + currency, + gender, + flag, + countryFlag, + subdivisionFlag, + skinTone, + regional +} + +/// List of All Emojis. +final List _emojis = [ + Emoji( + name: 'grinning face', + char: '\u{1F600}', + shortName: 'grinning', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceSmiling, + keywords: [ + 'face', + 'grin', + 'uc6', + 'smiley', + 'happy', + 'silly', + 'laugh', + 'thank you', + 'awesome', + 'smile', + 'friend', + 'pleased', + 'teeth', + 'pacman', + 'fun', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'hooray', + 'cheek', + 'cheeky', + 'excited', + 'feliz', + 'heureux', + 'cheerful', + 'delighted', + 'ecstatic', + 'elated', + 'glad', + 'joy', + 'merry', + 'funny', + 'laughing', + 'lol', + 'rofl', + 'lmao', + 'lmfao', + 'hilarious', + 'ha', + 'haha', + 'chuckle', + 'comedy', + 'giggle', + 'hehe', + 'joyful', + 'laugh out loud', + 'rire', + 'tee hee', + 'jaja', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'okay', + 'got it', + 'cool', + 'ok', + 'will do', + 'like', + 'bien', + 'yep', + 'yup', + 'smiles', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'please', + 'chill', + 'confident', + 'content', + 'dentist', + 'pac man' + ]), + Emoji( + name: 'grinning face with big eyes', + char: '\u{1F603}', + shortName: 'smiley', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceSmiling, + keywords: [ + 'face', + 'mouth', + 'open', + 'smile', + 'uc6', + 'smiley', + 'happy', + 'silly', + 'laugh', + 'good', + 'smile', + 'teeth', + 'fun', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'hooray', + 'cheek', + 'cheeky', + 'excited', + 'feliz', + 'heureux', + 'cheerful', + 'delighted', + 'ecstatic', + 'elated', + 'glad', + 'joy', + 'merry', + 'funny', + 'laughing', + 'lol', + 'rofl', + 'lmao', + 'lmfao', + 'hilarious', + 'ha', + 'haha', + 'chuckle', + 'comedy', + 'giggle', + 'hehe', + 'joyful', + 'laugh out loud', + 'rire', + 'tee hee', + 'jaja', + 'good job', + 'nice', + 'well done', + 'bravo', + 'congratulations', + 'congrats', + 'smiles', + 'dentist', + ':-D', + '=D' + ]), + Emoji( + name: 'grinning face with smiling eyes', + char: '\u{1F604}', + shortName: 'smile', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceSmiling, + keywords: [ + 'eye', + 'face', + 'mouth', + 'open', + 'smile', + 'uc6', + 'smiley', + 'happy', + 'laugh', + 'smile', + 'teeth', + 'fun', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'hooray', + 'cheek', + 'cheeky', + 'excited', + 'feliz', + 'heureux', + 'cheerful', + 'delighted', + 'ecstatic', + 'elated', + 'glad', + 'joy', + 'merry', + 'laughing', + 'lol', + 'rofl', + 'lmao', + 'lmfao', + 'hilarious', + 'ha', + 'haha', + 'chuckle', + 'comedy', + 'giggle', + 'hehe', + 'joyful', + 'laugh out loud', + 'rire', + 'tee hee', + 'jaja', + 'smiles', + 'dentist', + ':D' + ]), + Emoji( + name: 'beaming face with smiling eyes', + char: '\u{1F601}', + shortName: 'grin', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceSmiling, + keywords: [ + 'eye', + 'face', + 'grin', + 'smile', + 'uc6', + 'smiley', + 'happy', + 'silly', + 'laugh', + 'thank you', + 'good', + 'beautiful', + 'selfie', + 'smile', + 'friend', + 'teeth', + 'dumb', + 'grimace', + 'fun', + 'proud', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'hooray', + 'cheek', + 'cheeky', + 'excited', + 'feliz', + 'heureux', + 'cheerful', + 'delighted', + 'ecstatic', + 'elated', + 'glad', + 'joy', + 'merry', + 'funny', + 'laughing', + 'lol', + 'rofl', + 'lmao', + 'lmfao', + 'hilarious', + 'ha', + 'haha', + 'chuckle', + 'comedy', + 'giggle', + 'hehe', + 'joyful', + 'laugh out loud', + 'rire', + 'tee hee', + 'jaja', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'good job', + 'nice', + 'well done', + 'bravo', + 'congratulations', + 'congrats', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'smiles', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'dentist', + 'idiot', + 'ignorant', + 'stupid' + ]), + Emoji( + name: 'grinning squinting face', + char: '\u{1F606}', + shortName: 'laughing', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceSmiling, + keywords: [ + 'face', + 'laugh', + 'mouth', + 'open', + 'satisfied', + 'smile', + 'uc6', + 'smiley', + 'happy', + 'silly', + 'laugh', + 'smile', + 'teeth', + 'dumb', + 'fun', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'hooray', + 'cheek', + 'cheeky', + 'excited', + 'feliz', + 'heureux', + 'cheerful', + 'delighted', + 'ecstatic', + 'elated', + 'glad', + 'joy', + 'merry', + 'funny', + 'laughing', + 'lol', + 'rofl', + 'lmao', + 'lmfao', + 'hilarious', + 'ha', + 'haha', + 'chuckle', + 'comedy', + 'giggle', + 'hehe', + 'joyful', + 'laugh out loud', + 'rire', + 'tee hee', + 'jaja', + 'smiles', + 'dentist', + 'idiot', + 'ignorant', + 'stupid', + '>:)', + '>;)', + '>:-)', + '>=)' + ]), + Emoji( + name: 'grinning face with sweat', + char: '\u{1F605}', + shortName: 'sweat_smile', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceSmiling, + keywords: [ + 'cold', + 'face', + 'open', + 'smile', + 'sweat', + 'uc6', + 'smiley', + 'happy', + 'laugh', + 'sweat', + 'smile', + 'tease', + 'drip', + 'guilty', + 'porn', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'hooray', + 'cheek', + 'cheeky', + 'excited', + 'feliz', + 'heureux', + 'cheerful', + 'delighted', + 'ecstatic', + 'elated', + 'glad', + 'joy', + 'merry', + 'laughing', + 'lol', + 'rofl', + 'lmao', + 'lmfao', + 'hilarious', + 'ha', + 'haha', + 'chuckle', + 'comedy', + 'giggle', + 'hehe', + 'joyful', + 'laugh out loud', + 'rire', + 'tee hee', + 'jaja', + 'smiles', + 'joke', + 'kidding', + ':)', + ':-)', + '=)', + ':D', + ':-D', + '=D' + ]), + Emoji( + name: 'face with tears of joy', + char: '\u{1F602}', + shortName: 'joy', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceSmiling, + keywords: [ + 'face', + 'joy', + 'laugh', + 'tear', + 'uc6', + 'smiley', + 'happy', + 'silly', + 'cry', + 'laugh', + 'sarcastic', + 'smile', + 'tease', + 'crazy', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'hooray', + 'cheek', + 'cheeky', + 'excited', + 'feliz', + 'heureux', + 'cheerful', + 'delighted', + 'ecstatic', + 'elated', + 'glad', + 'joy', + 'merry', + 'funny', + 'crying', + 'weeping', + 'weep', + 'sob', + 'sobbing', + 'tear', + 'tears', + 'bawling', + 'laughing', + 'lol', + 'rofl', + 'lmao', + 'lmfao', + 'hilarious', + 'ha', + 'haha', + 'chuckle', + 'comedy', + 'giggle', + 'hehe', + 'joyful', + 'laugh out loud', + 'rire', + 'tee hee', + 'jaja', + 'sarcasm', + 'smiles', + 'joke', + 'kidding', + 'weird', + 'awkward', + 'insane', + 'wild', + ":')", + ":'-)" + ]), + Emoji( + name: 'rolling on the floor laughing', + char: '\u{1F923}', + shortName: 'rofl', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceSmiling, + keywords: [ + 'face', + 'floor', + 'laugh', + 'rolling', + 'uc9', + 'smiley', + 'happy', + 'silly', + 'laugh', + 'tease', + 'crazy', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'hooray', + 'cheek', + 'cheeky', + 'excited', + 'feliz', + 'heureux', + 'cheerful', + 'delighted', + 'ecstatic', + 'elated', + 'glad', + 'joy', + 'merry', + 'funny', + 'laughing', + 'lol', + 'rofl', + 'lmao', + 'lmfao', + 'hilarious', + 'ha', + 'haha', + 'chuckle', + 'comedy', + 'giggle', + 'hehe', + 'joyful', + 'laugh out loud', + 'rire', + 'tee hee', + 'jaja', + 'joke', + 'kidding', + 'weird', + 'awkward', + 'insane', + 'wild' + ]), + Emoji( + name: 'smiling face', + char: '\u{263A}\u{FE0F}', + shortName: 'relaxed', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceAffection, + keywords: [ + 'face', + 'outlined', + 'relaxed', + 'smile', + 'uc1', + 'smiley', + 'happy', + 'beautiful', + 'smile', + 'blush', + 'pleased', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'hooray', + 'cheek', + 'cheeky', + 'excited', + 'feliz', + 'heureux', + 'cheerful', + 'delighted', + 'ecstatic', + 'elated', + 'glad', + 'joy', + 'merry', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'smiles', + 'blushing', + 'bella', + 'embarrassed', + 'creep', + 'please', + 'chill', + 'confident', + 'content' + ]), + Emoji( + name: 'smiling face with smiling eyes', + char: '\u{1F60A}', + shortName: 'blush', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceSmiling, + keywords: [ + 'blush', + 'eye', + 'face', + 'smile', + 'uc6', + 'smiley', + 'happy', + 'good', + 'beautiful', + 'smile', + 'blush', + 'pleased', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'hooray', + 'cheek', + 'cheeky', + 'excited', + 'feliz', + 'heureux', + 'cheerful', + 'delighted', + 'ecstatic', + 'elated', + 'glad', + 'joy', + 'merry', + 'good job', + 'nice', + 'well done', + 'bravo', + 'congratulations', + 'congrats', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'smiles', + 'blushing', + 'bella', + 'embarrassed', + 'creep', + 'please', + 'chill', + 'confident', + 'content' + ]), + Emoji( + name: 'smiling face with halo', + char: '\u{1F607}', + shortName: 'innocent', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceSmiling, + keywords: [ + 'angel', + 'face', + 'fairy tale', + 'fantasy', + 'halo', + 'innocent', + 'smile', + 'uc6', + 'smiley', + 'silly', + 'pray', + 'smile', + 'blush', + 'fantasy', + 'soul', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'funny', + 'prayer', + 'praying', + 'prayers', + 'grateful', + 'sorry', + 'heaven', + 'bless', + 'faith', + 'holy', + 'spirit', + 'hopeful', + 'blessed', + 'preach', + 'offering', + 'smiles', + 'blushing', + 'bella', + 'embarrassed', + 'creep', + 'O:-)', + '0:-3', + '0:3', + '0:-)', + '0:)', + '0;^)', + 'O:)', + 'O;-)', + 'O=)', + '0;-)', + 'O:-3', + 'O:3' + ]), + Emoji( + name: 'slightly smiling face', + char: '\u{1F642}', + shortName: 'slight_smile', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceSmiling, + keywords: [ + 'face', + 'smile', + 'uc7', + 'smiley', + 'happy', + 'awesome', + 'smile', + 'blush', + 'pleased', + 'fun', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'hooray', + 'cheek', + 'cheeky', + 'excited', + 'feliz', + 'heureux', + 'cheerful', + 'delighted', + 'ecstatic', + 'elated', + 'glad', + 'joy', + 'merry', + 'okay', + 'got it', + 'cool', + 'ok', + 'will do', + 'like', + 'bien', + 'yep', + 'yup', + 'smiles', + 'blushing', + 'bella', + 'embarrassed', + 'creep', + 'please', + 'chill', + 'confident', + 'content', + ':)', + ':-)', + '=]', + '=)', + ':]' + ]), + Emoji( + name: 'upside-down face', + char: '\u{1F643}', + shortName: 'upside_down', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceSmiling, + keywords: [ + 'face', + 'upside-down', + 'uc8', + 'smiley', + 'happy', + 'silly', + 'sarcastic', + 'smile', + 'pleased', + 'dumb', + 'what', + 'clever', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'hooray', + 'cheek', + 'cheeky', + 'excited', + 'feliz', + 'heureux', + 'cheerful', + 'delighted', + 'ecstatic', + 'elated', + 'glad', + 'joy', + 'merry', + 'funny', + 'sarcasm', + 'smiles', + 'please', + 'chill', + 'confident', + 'content', + 'idiot', + 'ignorant', + 'stupid', + 'witty' + ]), + Emoji( + name: 'winking face', + char: '\u{1F609}', + shortName: 'wink', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceSmiling, + keywords: [ + 'face', + 'wink', + 'uc6', + 'smiley', + 'happy', + 'silly', + 'sarcastic', + 'selfie', + 'smile', + 'tease', + 'clever', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'hooray', + 'cheek', + 'cheeky', + 'excited', + 'feliz', + 'heureux', + 'cheerful', + 'delighted', + 'ecstatic', + 'elated', + 'glad', + 'joy', + 'merry', + 'funny', + 'sarcasm', + 'smiles', + 'joke', + 'kidding', + 'witty', + ';)', + ';-)', + '*-)', + '*)', + ';-]', + ';]', + ';D', + ';^)' + ]), + Emoji( + name: 'relieved face', + char: '\u{1F60C}', + shortName: 'relieved', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceSleepy, + keywords: [ + 'face', + 'relieved', + 'uc6', + 'smiley', + 'happy', + 'smile', + 'pleased', + 'calm', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'hooray', + 'cheek', + 'cheeky', + 'excited', + 'feliz', + 'heureux', + 'cheerful', + 'delighted', + 'ecstatic', + 'elated', + 'glad', + 'joy', + 'merry', + 'smiles', + 'please', + 'chill', + 'confident', + 'content' + ]), + Emoji( + name: 'smiling face with tear', + char: '\u{1F972}', + shortName: 'smiling_face_with_tear', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceAffection, + keywords: [ + 'uc13', + 'smiley', + 'happy', + 'cry', + 'thank you', + 'beautiful', + 'smile', + 'blush', + 'pleased', + 'drip', + 'hope', + 'proud', + 'sentimental', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'hooray', + 'cheek', + 'cheeky', + 'excited', + 'feliz', + 'heureux', + 'cheerful', + 'delighted', + 'ecstatic', + 'elated', + 'glad', + 'joy', + 'merry', + 'crying', + 'weeping', + 'weep', + 'sob', + 'sobbing', + 'tear', + 'tears', + 'bawling', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'smiles', + 'blushing', + 'bella', + 'embarrassed', + 'creep', + 'please', + 'chill', + 'confident', + 'content', + 'swear', + 'promise', + 'nostalgic', + 'tender', + 'dreamy', + 'touched' + ]), + Emoji( + name: 'smiling face with heart-eyes', + char: '\u{1F60D}', + shortName: 'heart_eyes', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceAffection, + keywords: [ + 'eye', + 'face', + 'love', + 'smile', + 'uc6', + 'smiley', + 'happy', + 'love', + 'heart eyes', + 'beautiful', + 'smile', + 'hola', + 'facebook', + 'porn', + 'heart', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'hooray', + 'cheek', + 'cheeky', + 'excited', + 'feliz', + 'heureux', + 'cheerful', + 'delighted', + 'ecstatic', + 'elated', + 'glad', + 'joy', + 'merry', + 'i love you', + 'te amo', + "je t'aime", + 'anniversary', + 'lovin', + 'amour', + 'aimer', + 'amor', + 'valentines day', + 'enamour', + 'lovey', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'smiles', + 'hearts', + 'serce', + 'corazón', + 'coração', + 'coeur' + ]), + Emoji( + name: 'smiling face with hearts', + char: '\u{1F970}', + shortName: 'smiling_face_with_3_hearts', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceAffection, + keywords: [ + 'uc11', + 'smiley', + 'wedding', + 'happy', + 'love', + 'hug', + 'smile', + 'friend', + 'blush', + 'pleased', + 'heart', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'hooray', + 'cheek', + 'cheeky', + 'excited', + 'feliz', + 'heureux', + 'cheerful', + 'delighted', + 'ecstatic', + 'elated', + 'glad', + 'joy', + 'merry', + 'i love you', + 'te amo', + "je t'aime", + 'anniversary', + 'lovin', + 'amour', + 'aimer', + 'amor', + 'valentines day', + 'enamour', + 'lovey', + 'embrace', + 'hugs', + 'smiles', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'blushing', + 'bella', + 'embarrassed', + 'creep', + 'please', + 'chill', + 'confident', + 'content', + 'hearts', + 'serce', + 'corazón', + 'coração', + 'coeur' + ]), + Emoji( + name: 'face blowing a kiss', + char: '\u{1F618}', + shortName: 'kissing_heart', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceAffection, + keywords: [ + 'face', + 'kiss', + 'uc6', + 'smiley', + 'wedding', + 'love', + 'sexy', + 'beautiful', + 'disney', + 'kisses', + 'hit', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'i love you', + 'te amo', + "je t'aime", + 'anniversary', + 'lovin', + 'amour', + 'aimer', + 'amor', + 'valentines day', + 'enamour', + 'lovey', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'cartoon', + 'bisous', + 'beijos', + 'besos', + 'bise', + 'blowing kisses', + 'kissy', + 'punch', + 'pow', + 'bam', + ':*', + ':-*', + '=*', + ':^*' + ]), + Emoji( + name: 'kissing face', + char: '\u{1F617}', + shortName: 'kissing', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceAffection, + keywords: [ + 'face', + 'kiss', + 'uc6', + 'smiley', + 'sexy', + 'beautiful', + 'selfie', + 'kisses', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'bisous', + 'beijos', + 'besos', + 'bise', + 'blowing kisses', + 'kissy' + ]), + Emoji( + name: 'kissing face with smiling eyes', + char: '\u{1F619}', + shortName: 'kissing_smiling_eyes', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceAffection, + keywords: [ + 'eye', + 'face', + 'kiss', + 'smile', + 'uc6', + 'smiley', + 'love', + 'sexy', + 'kisses', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'i love you', + 'te amo', + "je t'aime", + 'anniversary', + 'lovin', + 'amour', + 'aimer', + 'amor', + 'valentines day', + 'enamour', + 'lovey', + 'bisous', + 'beijos', + 'besos', + 'bise', + 'blowing kisses', + 'kissy' + ]), + Emoji( + name: 'kissing face with closed eyes', + char: '\u{1F61A}', + shortName: 'kissing_closed_eyes', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceAffection, + keywords: [ + 'closed', + 'eye', + 'face', + 'kiss', + 'uc6', + 'smiley', + 'love', + 'sexy', + 'blush', + 'kisses', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'i love you', + 'te amo', + "je t'aime", + 'anniversary', + 'lovin', + 'amour', + 'aimer', + 'amor', + 'valentines day', + 'enamour', + 'lovey', + 'blushing', + 'bella', + 'embarrassed', + 'creep', + 'bisous', + 'beijos', + 'besos', + 'bise', + 'blowing kisses', + 'kissy' + ]), + Emoji( + name: 'face savoring food', + char: '\u{1F60B}', + shortName: 'yum', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceTongue, + keywords: [ + 'delicious', + 'face', + 'savouring', + 'smile', + 'um', + 'yum', + 'uc6', + 'smiley', + 'food', + 'happy', + 'silly', + 'sarcastic', + 'good', + 'smile', + 'pink', + 'lick', + 'tongue', + 'dinner', + 'picnic', + 'delicious', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'hooray', + 'cheek', + 'cheeky', + 'excited', + 'feliz', + 'heureux', + 'cheerful', + 'delighted', + 'ecstatic', + 'elated', + 'glad', + 'joy', + 'merry', + 'funny', + 'sarcasm', + 'good job', + 'nice', + 'well done', + 'bravo', + 'congratulations', + 'congrats', + 'smiles', + 'rose', + 'toung', + 'tounge', + 'lunch', + 'savour' + ]), + Emoji( + name: 'face with tongue', + char: '\u{1F61B}', + shortName: 'stuck_out_tongue', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceTongue, + keywords: [ + 'face', + 'tongue', + 'uc6', + 'smiley', + 'happy', + 'silly', + 'pink', + 'tease', + 'lick', + 'tongue', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'hooray', + 'cheek', + 'cheeky', + 'excited', + 'feliz', + 'heureux', + 'cheerful', + 'delighted', + 'ecstatic', + 'elated', + 'glad', + 'joy', + 'merry', + 'funny', + 'rose', + 'joke', + 'kidding', + 'toung', + 'tounge', + ':P', + ':-P', + '=P', + ':-Þ', + ':Þ', + ':-b', + ':b' + ]), + Emoji( + name: 'squinting face with tongue', + char: '\u{1F61D}', + shortName: 'stuck_out_tongue_closed_eyes', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceTongue, + keywords: [ + 'eye', + 'face', + 'horrible', + 'taste', + 'tongue', + 'uc6', + 'smiley', + 'happy', + 'silly', + 'laugh', + 'pink', + 'tease', + 'grimace', + 'lick', + 'tongue', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'hooray', + 'cheek', + 'cheeky', + 'excited', + 'feliz', + 'heureux', + 'cheerful', + 'delighted', + 'ecstatic', + 'elated', + 'glad', + 'joy', + 'merry', + 'funny', + 'laughing', + 'lol', + 'rofl', + 'lmao', + 'lmfao', + 'hilarious', + 'ha', + 'haha', + 'chuckle', + 'comedy', + 'giggle', + 'hehe', + 'joyful', + 'laugh out loud', + 'rire', + 'tee hee', + 'jaja', + 'rose', + 'joke', + 'kidding', + 'toung', + 'tounge' + ]), + Emoji( + name: 'winking face with tongue', + char: '\u{1F61C}', + shortName: 'stuck_out_tongue_winking_eye', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceTongue, + keywords: [ + 'eye', + 'face', + 'joke', + 'tongue', + 'wink', + 'uc6', + 'smiley', + 'happy', + 'silly', + 'pink', + 'tease', + 'pleased', + 'lick', + 'porn', + 'crazy', + 'tongue', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'hooray', + 'cheek', + 'cheeky', + 'excited', + 'feliz', + 'heureux', + 'cheerful', + 'delighted', + 'ecstatic', + 'elated', + 'glad', + 'joy', + 'merry', + 'funny', + 'rose', + 'joke', + 'kidding', + 'please', + 'chill', + 'confident', + 'content', + 'weird', + 'awkward', + 'insane', + 'wild', + 'toung', + 'tounge', + '>:P', + 'X-P' + ]), + Emoji( + name: 'zany face', + char: '\u{1F92A}', + shortName: 'zany_face', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceTongue, + keywords: [ + 'eye', + 'large', + 'small', + 'uc10', + 'smiley', + 'silly', + 'nutcase', + 'crazy', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'funny', + 'weird', + 'awkward', + 'insane', + 'wild' + ]), + Emoji( + name: 'face with raised eyebrow', + char: '\u{1F928}', + shortName: 'face_with_raised_eyebrow', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceNeutralSkeptical, + keywords: [ + 'uc10', + 'smiley', + 'doubt', + 'jealous', + 'colbert', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'unsure', + 'thinking', + 'wonder', + 'curious', + 'worry', + 'pensive', + 'remember', + 'skeptical' + ]), + Emoji( + name: 'face with monocle', + char: '\u{1F9D0}', + shortName: 'face_with_monocle', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceGlasses, + keywords: [ + 'uc10', + 'smiley', + 'nerd', + 'rich', + 'mystery', + 'proud', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'smart', + 'geek', + 'serious', + 'grand', + 'expensive', + 'fancy' + ]), + Emoji( + name: 'nerd face', + char: '\u{1F913}', + shortName: 'nerd', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceGlasses, + keywords: [ + 'face', + 'geek', + 'nerd', + 'uc8', + 'smiley', + 'glasses', + 'nerd', + 'google', + 'brain', + 'teeth', + 'dumb', + 'disguise', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'eyeglasses', + 'eye glasses', + 'smart', + 'geek', + 'serious', + 'mind', + 'memory', + 'thought', + 'conscience', + 'dentist', + 'idiot', + 'ignorant', + 'stupid' + ]), + Emoji( + name: 'smiling face with sunglasses', + char: '\u{1F60E}', + shortName: 'sunglasses', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceGlasses, + keywords: [ + 'bright', + 'cool', + 'eye', + 'eyewear', + 'face', + 'glasses', + 'smile', + 'sun', + 'sunglasses', + 'uc6', + 'smiley', + 'happy', + 'silly', + 'glasses', + 'emojione', + 'awesome', + 'beautiful', + 'boys night', + 'smile', + 'sunglasses', + 'hawaii', + 'california', + 'florida', + 'las vegas', + 'fun', + 'summer', + 'clever', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'hooray', + 'cheek', + 'cheeky', + 'excited', + 'feliz', + 'heureux', + 'cheerful', + 'delighted', + 'ecstatic', + 'elated', + 'glad', + 'joy', + 'merry', + 'funny', + 'eyeglasses', + 'eye glasses', + 'emoji one', + 'okay', + 'got it', + 'cool', + 'ok', + 'will do', + 'like', + 'bien', + 'yep', + 'yup', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'guys night', + 'smiles', + 'shades', + 'lunettes de soleil', + 'sun glasses', + 'aloha', + 'kawaii', + 'maui', + 'moana', + 'vegas', + 'weekend', + 'witty', + 'B-)', + 'B)', + '8)', + '8-)', + 'B-D', + '8-D' + ]), + Emoji( + name: 'star-struck', + char: '\u{1F929}', + shortName: 'star_struck', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceAffection, + keywords: [ + 'uc10', + 'smiley', + 'happy', + 'selfie', + 'fame', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'hooray', + 'cheek', + 'cheeky', + 'excited', + 'feliz', + 'heureux', + 'cheerful', + 'delighted', + 'ecstatic', + 'elated', + 'glad', + 'joy', + 'merry', + 'famous', + 'celebrity' + ]), + Emoji( + name: 'partying face', + char: '\u{1F973}', + shortName: 'partying_face', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceHat, + keywords: [ + 'uc11', + 'smiley', + 'holidays', + 'happy', + 'silly', + 'hat', + 'cheers', + 'happy birthday', + 'confetti', + 'celebrate', + 'fun', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'holiday', + 'hooray', + 'cheek', + 'cheeky', + 'excited', + 'feliz', + 'heureux', + 'cheerful', + 'delighted', + 'ecstatic', + 'elated', + 'glad', + 'joy', + 'merry', + 'funny', + 'hats', + 'cap', + 'caps', + 'gān bēi', + 'Na zdravi', + 'Proost', + 'Prost', + 'Sláinte', + 'Cin cin', + 'Kanpai', + 'Na zdrowie', + 'Saúde', + 'На здоровье', + 'Salud', + 'Skål', + 'Sei gesund', + 'santé', + 'Bon anniversaire', + 'joyeux anniversaire', + 'buon compleanno', + 'feliz cumpleaños', + 'alles Gute zum Geburtstag', + 'feliz Aniversário', + 'Gratulerer med dagen', + 'celebration', + 'event', + 'celebrating', + 'festa', + 'parties', + 'events', + 'new years', + 'new year', + 'fiesta', + 'fete', + 'newyear', + 'party', + 'festive', + 'festival', + 'yolo', + 'festejar' + ]), + Emoji( + name: 'smirking face', + char: '\u{1F60F}', + shortName: 'smirk', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceNeutralSkeptical, + keywords: [ + 'face', + 'smirk', + 'uc6', + 'smiley', + 'happy', + 'silly', + 'sexy', + 'sarcastic', + 'smile', + 'pleased', + 'clever', + 'proud', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'hooray', + 'cheek', + 'cheeky', + 'excited', + 'feliz', + 'heureux', + 'cheerful', + 'delighted', + 'ecstatic', + 'elated', + 'glad', + 'joy', + 'merry', + 'funny', + 'sarcasm', + 'smiles', + 'please', + 'chill', + 'confident', + 'content', + 'witty' + ]), + Emoji( + name: 'unamused face', + char: '\u{1F612}', + shortName: 'unamused', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceNeutralSkeptical, + keywords: [ + 'face', + 'unamused', + 'unhappy', + 'uc6', + 'smiley', + 'sad', + 'tired', + 'angry', + 'bored', + 'hate', + 'doubt', + 'grimace', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'triste', + 'depression', + 'negative', + 'sadness', + 'sleepy', + 'sleep', + 'dormi', + 'pillow', + 'blanket', + 'exhausted', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad', + 'boring', + 'agree', + 'whatever', + 'boredom', + 'i hate', + 'disgust', + 'stump', + 'shout', + 'dislike', + 'rude', + 'annoy', + 'grinch', + 'gross', + 'grumpy', + 'mean', + 'problem', + 'suck', + 'jerk', + 'asshole', + 'no', + 'unsure', + 'thinking', + 'wonder', + 'curious', + 'worry', + 'pensive', + 'remember', + 'skeptical' + ]), + Emoji( + name: 'disappointed face', + char: '\u{1F61E}', + shortName: 'disappointed', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceConcerned, + keywords: [ + 'disappointed', + 'face', + 'uc6', + 'smiley', + 'sad', + 'tired', + 'angry', + 'bored', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'triste', + 'depression', + 'negative', + 'sadness', + 'sleepy', + 'sleep', + 'dormi', + 'pillow', + 'blanket', + 'exhausted', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad', + 'boring', + 'agree', + 'whatever', + 'boredom', + '>:[', + ':-(', + ':(', + ':-[', + ':[', + '=(' + ]), + Emoji( + name: 'pensive face', + char: '\u{1F614}', + shortName: 'pensive', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceSleepy, + keywords: [ + 'dejected', + 'face', + 'pensive', + 'uc6', + 'smiley', + 'sad', + 'rip', + 'guilty', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'triste', + 'depression', + 'negative', + 'sadness', + 'rest in peace' + ]), + Emoji( + name: 'worried face', + char: '\u{1F61F}', + shortName: 'worried', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceConcerned, + keywords: [ + 'face', + 'worried', + 'uc6', + 'smiley', + 'sad', + 'angry', + 'doubt', + 'guilty', + 'jealous', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'triste', + 'depression', + 'negative', + 'sadness', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad', + 'unsure', + 'thinking', + 'wonder', + 'curious', + 'worry', + 'pensive', + 'remember', + 'skeptical' + ]), + Emoji( + name: 'confused face', + char: '\u{1F615}', + shortName: 'confused', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceConcerned, + keywords: [ + 'confused', + 'face', + 'uc6', + 'smiley', + 'nurse', + 'doubt', + 'what', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'unsure', + 'thinking', + 'wonder', + 'curious', + 'worry', + 'pensive', + 'remember', + 'skeptical', + '>:\\', + '>:/', + ':-/', + ':-.', + ':/', + ':\\', + '=/', + '=\\', + ':L', + '=L' + ]), + Emoji( + name: 'slightly frowning face', + char: '\u{1F641}', + shortName: 'slight_frown', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceConcerned, + keywords: [ + 'face', + 'frown', + 'uc7', + 'smiley', + 'sad', + 'angry', + 'hate', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'triste', + 'depression', + 'negative', + 'sadness', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad', + 'i hate', + 'disgust', + 'stump', + 'shout', + 'dislike', + 'rude', + 'annoy', + 'grinch', + 'gross', + 'grumpy', + 'mean', + 'problem', + 'suck', + 'jerk', + 'asshole', + 'no' + ]), + Emoji( + name: 'frowning face', + char: '\u{2639}\u{FE0F}', + shortName: 'frowning2', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceConcerned, + keywords: [ + 'face', + 'frown', + 'uc1', + 'smiley', + 'sad', + 'angry', + 'hate', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'triste', + 'depression', + 'negative', + 'sadness', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad', + 'i hate', + 'disgust', + 'stump', + 'shout', + 'dislike', + 'rude', + 'annoy', + 'grinch', + 'gross', + 'grumpy', + 'mean', + 'problem', + 'suck', + 'jerk', + 'asshole', + 'no' + ]), + Emoji( + name: 'persevering face', + char: '\u{1F623}', + shortName: 'persevere', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceConcerned, + keywords: [ + 'face', + 'persevere', + 'uc6', + 'smiley', + 'angry', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad', + '>.<' + ]), + Emoji( + name: 'confounded face', + char: '\u{1F616}', + shortName: 'confounded', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceConcerned, + keywords: [ + 'confounded', + 'face', + 'uc6', + 'smiley', + 'angry', + 'wow', + 'hate', + 'stinky', + 'ugly', + 'confused', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad', + 'surprised', + 'scared', + 'shocked', + 'whoa', + 'surprise', + 'scary', + 'nervous', + 'shaking', + 'afraid', + 'amaze', + 'amazing', + 'creepy', + 'cringe', + 'gasp', + 'anxious', + 'mind blown', + 'i hate', + 'disgust', + 'stump', + 'shout', + 'dislike', + 'rude', + 'annoy', + 'grinch', + 'gross', + 'grumpy', + 'mean', + 'problem', + 'suck', + 'jerk', + 'asshole', + 'no', + 'smell', + 'stink', + 'odor', + 'perplexed' + ]), + Emoji( + name: 'tired face', + char: '\u{1F62B}', + shortName: 'tired_face', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceConcerned, + keywords: [ + 'face', + 'tired', + 'uc6', + 'smiley', + 'sad', + 'tired', + 'angry', + 'sick', + 'wow', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'triste', + 'depression', + 'negative', + 'sadness', + 'sleepy', + 'sleep', + 'dormi', + 'pillow', + 'blanket', + 'exhausted', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad', + 'barf', + 'vomit', + 'throw up', + 'puke', + 'get well', + 'cough', + 'puking', + 'barfing', + 'malade', + 'spew', + 'surprised', + 'scared', + 'shocked', + 'whoa', + 'surprise', + 'scary', + 'nervous', + 'shaking', + 'afraid', + 'amaze', + 'amazing', + 'creepy', + 'cringe', + 'gasp', + 'anxious', + 'mind blown' + ]), + Emoji( + name: 'weary face', + char: '\u{1F629}', + shortName: 'weary', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceConcerned, + keywords: [ + 'face', + 'tired', + 'weary', + 'uc6', + 'smiley', + 'sad', + 'tired', + 'angry', + 'stressed', + 'wow', + 'shame', + 'lazy', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'triste', + 'depression', + 'negative', + 'sadness', + 'sleepy', + 'sleep', + 'dormi', + 'pillow', + 'blanket', + 'exhausted', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad', + 'surprised', + 'scared', + 'shocked', + 'whoa', + 'surprise', + 'scary', + 'nervous', + 'shaking', + 'afraid', + 'amaze', + 'amazing', + 'creepy', + 'cringe', + 'gasp', + 'anxious', + 'mind blown' + ]), + Emoji( + name: 'pleading face', + char: '\u{1F97A}', + shortName: 'pleading_face', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceConcerned, + keywords: [ + 'uc11', + 'smiley', + 'sad', + 'cry', + 'condolence', + 'omg', + 'heartbreak', + 'blush', + 'begging', + 'doubt', + 'guilty', + 'help', + 'shame', + 'hope', + 'liar', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'triste', + 'depression', + 'negative', + 'sadness', + 'crying', + 'weeping', + 'weep', + 'sob', + 'sobbing', + 'tear', + 'tears', + 'bawling', + 'compassion', + 'omfg', + 'oh my god', + 'broken heart', + 'heartbroken', + 'blushing', + 'bella', + 'embarrassed', + 'creep', + 'unsure', + 'thinking', + 'wonder', + 'curious', + 'worry', + 'pensive', + 'remember', + 'skeptical', + 'swear', + 'promise', + 'lies', + 'lying' + ]), + Emoji( + name: 'crying face', + char: '\u{1F622}', + shortName: 'cry', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceConcerned, + keywords: [ + 'cry', + 'face', + 'sad', + 'tear', + 'uc6', + 'smiley', + 'sad', + 'cry', + 'rip', + 'heartbreak', + 'drip', + 'guilty', + 'covid', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'triste', + 'depression', + 'negative', + 'sadness', + 'crying', + 'weeping', + 'weep', + 'sob', + 'sobbing', + 'tear', + 'tears', + 'bawling', + 'rest in peace', + 'broken heart', + 'heartbroken', + ":'(", + ":'-(", + ';(', + ';-(' + ]), + Emoji( + name: 'loudly crying face', + char: '\u{1F62D}', + shortName: 'sob', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceConcerned, + keywords: [ + 'cry', + 'face', + 'sad', + 'sob', + 'tear', + 'uc6', + 'smiley', + 'sad', + 'cry', + 'rip', + 'heartbreak', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'triste', + 'depression', + 'negative', + 'sadness', + 'crying', + 'weeping', + 'weep', + 'sob', + 'sobbing', + 'tear', + 'tears', + 'bawling', + 'rest in peace', + 'broken heart', + 'heartbroken' + ]), + Emoji( + name: 'face with steam from nose', + char: '\u{1F624}', + shortName: 'triumph', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceNegative, + keywords: [ + 'face', + 'triumph', + 'won', + 'uc6', + 'smiley', + 'angry', + 'smoking', + 'steam', + 'breathe', + 'proud', + 'festivus', + 'booger', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad', + 'smoke', + 'cigarette', + 'puff', + 'steaming', + 'piping', + 'sigh', + 'inhale' + ]), + Emoji( + name: 'angry face', + char: '\u{1F620}', + shortName: 'angry', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceNegative, + keywords: [ + 'angry', + 'face', + 'mad', + 'uc6', + 'smiley', + 'angry', + 'hate', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad', + 'i hate', + 'disgust', + 'stump', + 'shout', + 'dislike', + 'rude', + 'annoy', + 'grinch', + 'gross', + 'grumpy', + 'mean', + 'problem', + 'suck', + 'jerk', + 'asshole', + 'no', + '>:(', + '>:-(', + ':@' + ]), + Emoji( + name: 'pouting face', + char: '\u{1F621}', + shortName: 'rage', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceNegative, + keywords: [ + 'angry', + 'face', + 'mad', + 'pouting', + 'rage', + 'red', + 'uc6', + 'smiley', + 'angry', + 'hate', + 'bitch', + 'donald trump', + 'guilty', + 'las vegas', + 'killer', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad', + 'i hate', + 'disgust', + 'stump', + 'shout', + 'dislike', + 'rude', + 'annoy', + 'grinch', + 'gross', + 'grumpy', + 'mean', + 'problem', + 'suck', + 'jerk', + 'asshole', + 'no', + 'puta', + 'pute', + 'trump', + 'vegas', + 'savage', + 'scary clown' + ]), + Emoji( + name: 'face with symbols on mouth', + char: '\u{1F92C}', + shortName: 'face_with_symbols_over_mouth', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceNegative, + keywords: [ + 'uc10', + 'smiley', + 'angry', + 'hate', + 'donald trump', + 'swearing', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad', + 'i hate', + 'disgust', + 'stump', + 'shout', + 'dislike', + 'rude', + 'annoy', + 'grinch', + 'gross', + 'grumpy', + 'mean', + 'problem', + 'suck', + 'jerk', + 'asshole', + 'no', + 'trump', + 'cussing', + 'cursing' + ]), + Emoji( + name: 'exploding head', + char: '\u{1F92F}', + shortName: 'exploding_head', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceUnwell, + keywords: [ + 'shocked', + 'uc10', + 'smiley', + 'angry', + 'wow', + 'omg', + 'donald trump', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad', + 'surprised', + 'scared', + 'shocked', + 'whoa', + 'surprise', + 'scary', + 'nervous', + 'shaking', + 'afraid', + 'amaze', + 'amazing', + 'creepy', + 'cringe', + 'gasp', + 'anxious', + 'mind blown', + 'omfg', + 'oh my god', + 'trump' + ]), + Emoji( + name: 'flushed face', + char: '\u{1F633}', + shortName: 'flushed', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceConcerned, + keywords: [ + 'dazed', + 'face', + 'flushed', + 'uc6', + 'smiley', + 'omg', + 'blush', + 'guilty', + 'porn', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'omfg', + 'oh my god', + 'blushing', + 'bella', + 'embarrassed', + 'creep', + ':\$', + '=\$' + ]), + Emoji( + name: 'hot face', + char: '\u{1F975}', + shortName: 'hot_face', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceUnwell, + keywords: [ + 'uc11', + 'weather', + 'smiley', + 'stressed', + 'sweat', + 'hot', + 'hate', + 'summer', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'heat', + 'warm', + 'caliente', + 'chaud', + 'heiß', + 'i hate', + 'disgust', + 'stump', + 'shout', + 'dislike', + 'rude', + 'annoy', + 'grinch', + 'gross', + 'grumpy', + 'mean', + 'problem', + 'suck', + 'jerk', + 'asshole', + 'no', + 'weekend' + ]), + Emoji( + name: 'cold face', + char: '\u{1F976}', + shortName: 'cold_face', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceUnwell, + keywords: [ + 'uc11', + 'weather', + 'smiley', + 'winter', + 'snow', + 'cold', + 'grimace', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'freeze', + 'frozen', + 'frost', + 'ice cube', + 'chilly', + 'chilled', + 'brisk', + 'freezing', + 'frostbite', + 'icicles' + ]), + Emoji( + name: 'face screaming in fear', + char: '\u{1F631}', + shortName: 'scream', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceConcerned, + keywords: [ + 'face', + 'fear', + 'fearful', + 'munch', + 'scared', + 'scream', + 'uc6', + 'smiley', + 'halloween', + 'wow', + 'omg', + 'donald trump', + 'fame', + 'porn', + 'ugly', + 'what', + 'crazy', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'samhain', + 'surprised', + 'scared', + 'shocked', + 'whoa', + 'surprise', + 'scary', + 'nervous', + 'shaking', + 'afraid', + 'amaze', + 'amazing', + 'creepy', + 'cringe', + 'gasp', + 'anxious', + 'mind blown', + 'omfg', + 'oh my god', + 'trump', + 'famous', + 'celebrity', + 'weird', + 'awkward', + 'insane', + 'wild' + ]), + Emoji( + name: 'fearful face', + char: '\u{1F628}', + shortName: 'fearful', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceConcerned, + keywords: [ + 'face', + 'fear', + 'fearful', + 'scared', + 'uc6', + 'smiley', + 'halloween', + 'stressed', + 'wow', + 'guilty', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'samhain', + 'surprised', + 'scared', + 'shocked', + 'whoa', + 'surprise', + 'scary', + 'nervous', + 'shaking', + 'afraid', + 'amaze', + 'amazing', + 'creepy', + 'cringe', + 'gasp', + 'anxious', + 'mind blown', + 'D:' + ]), + Emoji( + name: 'anxious face with sweat', + char: '\u{1F630}', + shortName: 'cold_sweat', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceConcerned, + keywords: [ + 'blue', + 'cold', + 'face', + 'mouth', + 'open', + 'rushed', + 'sweat', + 'uc6', + 'smiley', + 'halloween', + 'angry', + 'stressed', + 'sweat', + 'drip', + 'porn', + 'covid', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'samhain', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad' + ]), + Emoji( + name: 'sad but relieved face', + char: '\u{1F625}', + shortName: 'disappointed_relieved', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceConcerned, + keywords: [ + 'disappointed', + 'face', + 'relieved', + 'whew', + 'uc6', + 'smiley', + 'sad', + 'cry', + 'stressed', + 'sweat', + 'calm', + 'drip', + 'guilty', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'triste', + 'depression', + 'negative', + 'sadness', + 'crying', + 'weeping', + 'weep', + 'sob', + 'sobbing', + 'tear', + 'tears', + 'bawling' + ]), + Emoji( + name: 'downcast face with sweat', + char: '\u{1F613}', + shortName: 'sweat', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceConcerned, + keywords: [ + 'cold', + 'face', + 'sweat', + 'uc6', + 'smiley', + 'sad', + 'stressed', + 'sweat', + 'drip', + 'guilty', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'triste', + 'depression', + 'negative', + 'sadness', + "':(", + "':-(", + "'=(" + ]), + Emoji( + name: 'hugging face', + char: '\u{1F917}', + shortName: 'hugging', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceHand, + keywords: [ + 'face', + 'hug', + 'hugging', + 'uc8', + 'smiley', + 'happy', + 'tired', + 'love', + 'hug', + 'thank you', + 'friend', + 'blush', + 'facebook', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'hooray', + 'cheek', + 'cheeky', + 'excited', + 'feliz', + 'heureux', + 'cheerful', + 'delighted', + 'ecstatic', + 'elated', + 'glad', + 'joy', + 'merry', + 'sleepy', + 'sleep', + 'dormi', + 'pillow', + 'blanket', + 'exhausted', + 'i love you', + 'te amo', + "je t'aime", + 'anniversary', + 'lovin', + 'amour', + 'aimer', + 'amor', + 'valentines day', + 'enamour', + 'lovey', + 'embrace', + 'hugs', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'blushing', + 'bella', + 'embarrassed', + 'creep' + ]), + Emoji( + name: 'thinking face', + char: '\u{1F914}', + shortName: 'thinking', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceHand, + keywords: [ + 'face', + 'thinking', + 'uc8', + 'smiley', + 'boys night', + 'dream', + 'brain', + 'doubt', + 'idea', + 'confused', + 'what', + 'mystery', + 'innovate', + 'question', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'guys night', + 'dreams', + 'mind', + 'memory', + 'thought', + 'conscience', + 'unsure', + 'thinking', + 'wonder', + 'curious', + 'worry', + 'pensive', + 'remember', + 'skeptical', + 'perplexed', + 'innovation', + 'inquire', + 'quiz', + 'puzzled' + ]), + Emoji( + name: 'face with hand over mouth', + char: '\u{1F92D}', + shortName: 'face_with_hand_over_mouth', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceHand, + keywords: [ + 'uc10', + 'smiley', + 'tired', + 'blush', + 'tease', + 'quiet', + 'what', + 'secret', + 'yawn', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'sleepy', + 'sleep', + 'dormi', + 'pillow', + 'blanket', + 'exhausted', + 'blushing', + 'bella', + 'embarrassed', + 'creep', + 'joke', + 'kidding', + 'shut up', + 'hushed', + 'silence', + 'silent', + 'shush', + 'shh', + 'shhhhh' + ]), + Emoji( + name: 'yawning face', + char: '\u{1F971}', + shortName: 'yawning_face', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceConcerned, + keywords: [ + 'uc12', + 'smiley', + 'tired', + 'goodnight', + 'bored', + 'calm', + 'quiet', + 'wait', + 'yawn', + 'lazy', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'sleepy', + 'sleep', + 'dormi', + 'pillow', + 'blanket', + 'exhausted', + 'boring', + 'agree', + 'whatever', + 'boredom', + 'shut up', + 'hushed', + 'silence', + 'silent', + 'shush', + 'shh', + 'hours' + ]), + Emoji( + name: 'shushing face', + char: '\u{1F92B}', + shortName: 'shushing_face', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceHand, + keywords: [ + 'quiet', + 'shush', + 'uc10', + 'smiley', + 'quiet', + 'secret', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'shut up', + 'hushed', + 'silence', + 'silent', + 'shush', + 'shh', + 'shhhhh' + ]), + Emoji( + name: 'lying face', + char: '\u{1F925}', + shortName: 'lying_face', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceNeutralSkeptical, + keywords: [ + 'face', + 'lie', + 'pinocchio', + 'uc9', + 'smiley', + 'donald trump', + 'guilty', + 'crazy', + 'liar', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'trump', + 'weird', + 'awkward', + 'insane', + 'wild', + 'lies', + 'lying' + ]), + Emoji( + name: 'face without mouth', + char: '\u{1F636}', + shortName: 'no_mouth', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceNeutralSkeptical, + keywords: [ + 'face', + 'mouth', + 'quiet', + 'silent', + 'uc6', + 'smiley', + 'neutral', + 'hate', + 'dumb', + 'quiet', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'i hate', + 'disgust', + 'stump', + 'shout', + 'dislike', + 'rude', + 'annoy', + 'grinch', + 'gross', + 'grumpy', + 'mean', + 'problem', + 'suck', + 'jerk', + 'asshole', + 'no', + 'idiot', + 'ignorant', + 'stupid', + 'shut up', + 'hushed', + 'silence', + 'silent', + 'shush', + 'shh', + ':-X', + ':X', + ':-#', + ':#', + '=X', + '=#' + ]), + Emoji( + name: 'neutral face', + char: '\u{1F610}', + shortName: 'neutral_face', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceNeutralSkeptical, + keywords: [ + 'deadpan', + 'face', + 'neutral', + 'uc6', + 'smiley', + 'shrug', + 'neutral', + 'bored', + 'calm', + 'doubt', + 'dumb', + 'quiet', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'boring', + 'agree', + 'whatever', + 'boredom', + 'unsure', + 'thinking', + 'wonder', + 'curious', + 'worry', + 'pensive', + 'remember', + 'skeptical', + 'idiot', + 'ignorant', + 'stupid', + 'shut up', + 'hushed', + 'silence', + 'silent', + 'shush', + 'shh' + ]), + Emoji( + name: 'expressionless face', + char: '\u{1F611}', + shortName: 'expressionless', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceNeutralSkeptical, + keywords: [ + 'expressionless', + 'face', + 'inexpressive', + 'unexpressive', + 'uc6', + 'smiley', + 'neutral', + 'bored', + 'calm', + 'doubt', + 'dumb', + 'quiet', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'boring', + 'agree', + 'whatever', + 'boredom', + 'unsure', + 'thinking', + 'wonder', + 'curious', + 'worry', + 'pensive', + 'remember', + 'skeptical', + 'idiot', + 'ignorant', + 'stupid', + 'shut up', + 'hushed', + 'silence', + 'silent', + 'shush', + 'shh', + '-_-', + '-__-', + '-___-' + ]), + Emoji( + name: 'grimacing face', + char: '\u{1F62C}', + shortName: 'grimacing', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceNeutralSkeptical, + keywords: [ + 'face', + 'grimace', + 'uc6', + 'smiley', + 'silly', + 'selfie', + 'teeth', + 'grimace', + 'help', + 'porn', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'funny', + 'dentist' + ]), + Emoji( + name: 'face with rolling eyes', + char: '\u{1F644}', + shortName: 'rolling_eyes', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceNeutralSkeptical, + keywords: [ + 'eyes', + 'face', + 'rolling', + 'uc8', + 'smiley', + 'rolling eyes', + 'sarcastic', + 'bored', + 'hate', + 'doubt', + 'eyeroll', + 'jealous', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'eye roll', + 'side eye', + 'sarcasm', + 'boring', + 'agree', + 'whatever', + 'boredom', + 'i hate', + 'disgust', + 'stump', + 'shout', + 'dislike', + 'rude', + 'annoy', + 'grinch', + 'gross', + 'grumpy', + 'mean', + 'problem', + 'suck', + 'jerk', + 'asshole', + 'no', + 'unsure', + 'thinking', + 'wonder', + 'curious', + 'worry', + 'pensive', + 'remember', + 'skeptical' + ]), + Emoji( + name: 'hushed face', + char: '\u{1F62F}', + shortName: 'hushed', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceConcerned, + keywords: [ + 'face', + 'hushed', + 'stunned', + 'surprised', + 'uc6', + 'smiley', + 'wow', + 'what', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'surprised', + 'scared', + 'shocked', + 'whoa', + 'surprise', + 'scary', + 'nervous', + 'shaking', + 'afraid', + 'amaze', + 'amazing', + 'creepy', + 'cringe', + 'gasp', + 'anxious', + 'mind blown' + ]), + Emoji( + name: 'frowning face with open mouth', + char: '\u{1F626}', + shortName: 'frowning', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceConcerned, + keywords: [ + 'face', + 'frown', + 'mouth', + 'open', + 'uc6', + 'smiley', + 'sad', + 'jealous', + 'what', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'triste', + 'depression', + 'negative', + 'sadness' + ]), + Emoji( + name: 'anguished face', + char: '\u{1F627}', + shortName: 'anguished', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceConcerned, + keywords: [ + 'anguished', + 'face', + 'uc6', + 'smiley', + 'sad', + 'stressed', + 'wow', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'triste', + 'depression', + 'negative', + 'sadness', + 'surprised', + 'scared', + 'shocked', + 'whoa', + 'surprise', + 'scary', + 'nervous', + 'shaking', + 'afraid', + 'amaze', + 'amazing', + 'creepy', + 'cringe', + 'gasp', + 'anxious', + 'mind blown' + ]), + Emoji( + name: 'face with open mouth', + char: '\u{1F62E}', + shortName: 'open_mouth', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceConcerned, + keywords: [ + 'face', + 'mouth', + 'open', + 'sympathy', + 'uc6', + 'smiley', + 'wow', + 'dumb', + 'what', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'surprised', + 'scared', + 'shocked', + 'whoa', + 'surprise', + 'scary', + 'nervous', + 'shaking', + 'afraid', + 'amaze', + 'amazing', + 'creepy', + 'cringe', + 'gasp', + 'anxious', + 'mind blown', + 'idiot', + 'ignorant', + 'stupid', + ':-O', + ':O', + 'O_O', + '>:O' + ]), + Emoji( + name: 'astonished face', + char: '\u{1F632}', + shortName: 'astonished', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceConcerned, + keywords: [ + 'astonished', + 'face', + 'shocked', + 'totally', + 'uc6', + 'smiley', + 'wow', + 'omg', + 'donald trump', + 'crazy', + 'mystery', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'surprised', + 'scared', + 'shocked', + 'whoa', + 'surprise', + 'scary', + 'nervous', + 'shaking', + 'afraid', + 'amaze', + 'amazing', + 'creepy', + 'cringe', + 'gasp', + 'anxious', + 'mind blown', + 'omfg', + 'oh my god', + 'trump', + 'weird', + 'awkward', + 'insane', + 'wild' + ]), + Emoji( + name: 'sleeping face', + char: '\u{1F634}', + shortName: 'sleeping', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceSleepy, + keywords: [ + 'face', + 'sleep', + 'zzz', + 'uc6', + 'smiley', + 'tired', + 'goodnight', + 'coffee', + 'dream', + 'calm', + 'lazy', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'sleepy', + 'sleep', + 'dormi', + 'pillow', + 'blanket', + 'exhausted', + 'starbucks', + 'dreams' + ]), + Emoji( + name: 'drooling face', + char: '\u{1F924}', + shortName: 'drooling_face', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceSleepy, + keywords: [ + 'drooling', + 'face', + 'uc9', + 'smiley', + 'beautiful', + 'dumb', + 'porn', + 'ugly', + 'what', + 'crazy', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'idiot', + 'ignorant', + 'stupid', + 'weird', + 'awkward', + 'insane', + 'wild' + ]), + Emoji( + name: 'sleepy face', + char: '\u{1F62A}', + shortName: 'sleepy', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceSleepy, + keywords: [ + 'face', + 'sleep', + 'uc6', + 'smiley', + 'sad', + 'sick', + 'costume', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'triste', + 'depression', + 'negative', + 'sadness', + 'barf', + 'vomit', + 'throw up', + 'puke', + 'get well', + 'cough', + 'puking', + 'barfing', + 'malade', + 'spew' + ]), + Emoji( + name: 'dizzy face', + char: '\u{1F635}', + shortName: 'dizzy_face', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceUnwell, + keywords: [ + 'dizzy', + 'face', + 'uc6', + 'smiley', + 'dead', + 'wow', + 'nutcase', + 'omg', + 'hate', + 'drunk', + 'dumb', + 'las vegas', + 'what', + 'crazy', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'death', + 'die', + 'dying', + 'fart', + 'goth', + 'grave', + 'headstone', + 'horror', + 'hurt', + 'kill', + 'murder', + 'tomb', + 'toot', + 'died', + 'surprised', + 'scared', + 'shocked', + 'whoa', + 'surprise', + 'scary', + 'nervous', + 'shaking', + 'afraid', + 'amaze', + 'amazing', + 'creepy', + 'cringe', + 'gasp', + 'anxious', + 'mind blown', + 'omfg', + 'oh my god', + 'i hate', + 'disgust', + 'stump', + 'shout', + 'dislike', + 'rude', + 'annoy', + 'grinch', + 'gross', + 'grumpy', + 'mean', + 'problem', + 'suck', + 'jerk', + 'asshole', + 'no', + 'flustered', + 'dizzy', + 'idiot', + 'ignorant', + 'stupid', + 'vegas', + 'weird', + 'awkward', + 'insane', + 'wild', + '#-)', + '#)', + '%-)', + '%)', + 'X)', + 'X-)' + ]), + Emoji( + name: 'zipper-mouth face', + char: '\u{1F910}', + shortName: 'zipper_mouth', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceNeutralSkeptical, + keywords: [ + 'face', + 'mouth', + 'zipper', + 'uc8', + 'smiley', + 'angry', + 'fight', + 'dumb', + 'quiet', + 'crazy', + 'secret', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad', + 'idiot', + 'ignorant', + 'stupid', + 'shut up', + 'hushed', + 'silence', + 'silent', + 'shush', + 'shh', + 'weird', + 'awkward', + 'insane', + 'wild', + 'shhhhh' + ]), + Emoji( + name: 'woozy face', + char: '\u{1F974}', + shortName: 'woozy_face', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceUnwell, + keywords: [ + 'uc11', + 'smiley', + 'silly', + 'drugs', + 'sick', + 'drunk', + 'dumb', + 'ugly', + 'crazy', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'funny', + 'drug', + 'narcotics', + 'barf', + 'vomit', + 'throw up', + 'puke', + 'get well', + 'cough', + 'puking', + 'barfing', + 'malade', + 'spew', + 'flustered', + 'dizzy', + 'idiot', + 'ignorant', + 'stupid', + 'weird', + 'awkward', + 'insane', + 'wild' + ]), + Emoji( + name: 'nauseated face', + char: '\u{1F922}', + shortName: 'nauseated_face', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceUnwell, + keywords: [ + 'face', + 'nauseated', + 'vomit', + 'uc9', + 'smiley', + 'bathroom', + 'sick', + 'hate', + 'drunk', + 'stinky', + 'donald trump', + 'poison', + 'full', + 'Nauseated', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'barf', + 'vomit', + 'throw up', + 'puke', + 'get well', + 'cough', + 'puking', + 'barfing', + 'malade', + 'spew', + 'i hate', + 'disgust', + 'stump', + 'shout', + 'dislike', + 'rude', + 'annoy', + 'grinch', + 'gross', + 'grumpy', + 'mean', + 'problem', + 'suck', + 'jerk', + 'asshole', + 'no', + 'flustered', + 'dizzy', + 'smell', + 'stink', + 'odor', + 'trump', + 'toxic', + 'toxins', + 'green face' + ]), + Emoji( + name: 'face vomiting', + char: '\u{1F92E}', + shortName: 'face_vomiting', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceUnwell, + keywords: [ + 'sick', + 'vomit', + 'uc10', + 'smiley', + 'bathroom', + 'sick', + 'hate', + 'donald trump', + 'Nauseated', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'barf', + 'vomit', + 'throw up', + 'puke', + 'get well', + 'cough', + 'puking', + 'barfing', + 'malade', + 'spew', + 'i hate', + 'disgust', + 'stump', + 'shout', + 'dislike', + 'rude', + 'annoy', + 'grinch', + 'gross', + 'grumpy', + 'mean', + 'problem', + 'suck', + 'jerk', + 'asshole', + 'no', + 'trump', + 'green face' + ]), + Emoji( + name: 'sneezing face', + char: '\u{1F927}', + shortName: 'sneezing_face', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceUnwell, + keywords: [ + 'face', + 'gesundheit', + 'sneeze', + 'uc9', + 'smiley', + 'sick', + 'nurse', + 'stinky', + 'booger', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'barf', + 'vomit', + 'throw up', + 'puke', + 'get well', + 'cough', + 'puking', + 'barfing', + 'malade', + 'spew', + 'smell', + 'stink', + 'odor' + ]), + Emoji( + name: 'face with medical mask', + char: '\u{1F637}', + shortName: 'mask', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceUnwell, + keywords: [ + 'cold', + 'doctor', + 'face', + 'mask', + 'medicine', + 'sick', + 'uc6', + 'smiley', + 'dead', + 'health', + 'sick', + 'teeth', + 'nurse', + 'clean', + 'poison', + 'mask', + 'virus', + 'covid', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'death', + 'die', + 'dying', + 'fart', + 'goth', + 'grave', + 'headstone', + 'horror', + 'hurt', + 'kill', + 'murder', + 'tomb', + 'toot', + 'died', + 'medicine', + 'doctor', + 'barf', + 'vomit', + 'throw up', + 'puke', + 'get well', + 'cough', + 'puking', + 'barfing', + 'malade', + 'spew', + 'dentist', + 'toxic', + 'toxins', + 'corona' + ]), + Emoji( + name: 'face with thermometer', + char: '\u{1F912}', + shortName: 'thermometer_face', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceUnwell, + keywords: [ + 'face', + 'ill', + 'sick', + 'thermometer', + 'uc8', + 'smiley', + 'health', + 'sick', + 'nurse', + 'virus', + 'covid', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'medicine', + 'doctor', + 'barf', + 'vomit', + 'throw up', + 'puke', + 'get well', + 'cough', + 'puking', + 'barfing', + 'malade', + 'spew', + 'corona' + ]), + Emoji( + name: 'face with head-bandage', + char: '\u{1F915}', + shortName: 'head_bandage', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceUnwell, + keywords: [ + 'bandage', + 'face', + 'hurt', + 'injury', + 'uc8', + 'smiley', + 'health', + 'sick', + 'nurse', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'medicine', + 'doctor', + 'barf', + 'vomit', + 'throw up', + 'puke', + 'get well', + 'cough', + 'puking', + 'barfing', + 'malade', + 'spew' + ]), + Emoji( + name: 'money-mouth face', + char: '\u{1F911}', + shortName: 'money_mouth', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceTongue, + keywords: [ + 'face', + 'money', + 'mouth', + 'uc8', + 'smiley', + 'money', + 'win', + 'boys night', + 'power', + 'stinky', + 'coins', + 'discount', + 'donald trump', + 'jealous', + 'las vegas', + 'rich', + 'greed', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'cash', + 'dollars', + 'dollar', + 'bucks', + 'currency', + 'funds', + 'payment', + 'money face', + 'reward', + 'thief', + 'bank', + 'benjamins', + 'argent', + 'dinero', + 'i soldi', + 'Geld', + 'winning', + 'killing it', + 'crushing it', + 'victory', + 'victorious', + 'success', + 'successful', + 'winner', + 'guys night', + 'smell', + 'stink', + 'odor', + 'sale', + 'bargain', + 'trump', + 'vegas', + 'grand', + 'expensive', + 'fancy', + 'selfish' + ]), + Emoji( + name: 'cowboy hat face', + char: '\u{1F920}', + shortName: 'cowboy', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceHat, + keywords: [ + 'cowboy', + 'cowgirl', + 'face', + 'hat', + 'uc9', + 'smiley', + 'america', + 'hat', + 'halloween', + 'boys night', + 'magic', + 'disney', + 'fame', + 'super hero', + 'texas', + 'costume', + 'independence day', + 'disguise', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'usa', + 'united states', + 'united states of america', + 'american', + 'hats', + 'cap', + 'caps', + 'samhain', + 'guys night', + 'spell', + 'genie', + 'magical', + 'cartoon', + 'famous', + 'celebrity', + 'superhero', + 'superman', + 'batman', + '4th of july' + ]), + Emoji( + name: 'disguised face', + char: '\u{1F978}', + shortName: 'disguised_face', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceHat, + keywords: [ + 'uc13', + 'smiley', + 'silly', + 'halloween', + 'eyes', + 'boys night', + 'celebrate', + 'crazy', + 'mystery', + 'costume', + 'clever', + 'disguise', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'funny', + 'samhain', + 'eye', + 'eyebrow', + 'guys night', + 'celebration', + 'event', + 'celebrating', + 'festa', + 'parties', + 'events', + 'new years', + 'new year', + 'fiesta', + 'fete', + 'newyear', + 'party', + 'festive', + 'festival', + 'yolo', + 'festejar', + 'weird', + 'awkward', + 'insane', + 'wild', + 'witty' + ]), + Emoji( + name: 'smiling face with horns', + char: '\u{1F608}', + shortName: 'smiling_imp', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceNegative, + keywords: [ + 'face', + 'fairy tale', + 'fantasy', + 'horns', + 'smile', + 'uc6', + 'smiley', + 'silly', + 'halloween', + 'angry', + 'monster', + 'boys night', + 'evil', + 'guilty', + 'jealous', + 'porn', + 'crazy', + 'killer', + 'disguise', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'funny', + 'samhain', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad', + 'monsters', + 'beast', + 'guys night', + 'imp', + 'demon', + 'devil', + 'naughty', + 'devilish', + 'diablo', + 'diable', + 'satan', + 'weird', + 'awkward', + 'insane', + 'wild', + 'savage', + 'scary clown' + ]), + Emoji( + name: 'angry face with horns', + char: '\u{1F47F}', + shortName: 'imp', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceNegative, + keywords: [ + 'demon', + 'devil', + 'face', + 'fairy tale', + 'fantasy', + 'imp', + 'uc6', + 'smiley', + 'halloween', + 'angry', + 'monster', + 'wth', + 'fight', + 'evil', + 'dumb', + 'vampire', + 'crazy', + 'killer', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'samhain', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad', + 'monsters', + 'beast', + 'what the hell', + 'imp', + 'demon', + 'devil', + 'naughty', + 'devilish', + 'diablo', + 'diable', + 'satan', + 'idiot', + 'ignorant', + 'stupid', + 'dracula', + 'weird', + 'awkward', + 'insane', + 'wild', + 'savage', + 'scary clown' + ]), + Emoji( + name: 'ogre', + char: '\u{1F479}', + shortName: 'japanese_ogre', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceCostume, + keywords: [ + 'uc6', + 'halloween', + 'japan', + 'angry', + 'monster', + 'wow', + 'evil', + 'super hero', + 'ugly', + 'crazy', + 'killer', + 'disguise', + 'samhain', + 'japanese', + 'ninja', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad', + 'monsters', + 'beast', + 'surprised', + 'scared', + 'shocked', + 'whoa', + 'surprise', + 'scary', + 'nervous', + 'shaking', + 'afraid', + 'amaze', + 'amazing', + 'creepy', + 'cringe', + 'gasp', + 'anxious', + 'mind blown', + 'imp', + 'demon', + 'devil', + 'naughty', + 'devilish', + 'diablo', + 'diable', + 'satan', + 'superhero', + 'superman', + 'batman', + 'weird', + 'awkward', + 'insane', + 'wild', + 'savage', + 'scary clown' + ]), + Emoji( + name: 'goblin', + char: '\u{1F47A}', + shortName: 'japanese_goblin', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceCostume, + keywords: [ + 'creature', + 'face', + 'fairy tale', + 'fantasy', + 'monster', + 'uc6', + 'halloween', + 'japan', + 'angry', + 'monster', + 'wow', + 'mustache', + 'evil', + 'super hero', + 'ugly', + 'crazy', + 'killer', + 'mask', + 'disguise', + 'samhain', + 'japanese', + 'ninja', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad', + 'monsters', + 'beast', + 'surprised', + 'scared', + 'shocked', + 'whoa', + 'surprise', + 'scary', + 'nervous', + 'shaking', + 'afraid', + 'amaze', + 'amazing', + 'creepy', + 'cringe', + 'gasp', + 'anxious', + 'mind blown', + 'imp', + 'demon', + 'devil', + 'naughty', + 'devilish', + 'diablo', + 'diable', + 'satan', + 'superhero', + 'superman', + 'batman', + 'weird', + 'awkward', + 'insane', + 'wild', + 'savage', + 'scary clown' + ]), + Emoji( + name: 'clown face', + char: '\u{1F921}', + shortName: 'clown', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceCostume, + keywords: [ + 'clown', + 'face', + 'uc9', + 'smiley', + 'silly', + 'halloween', + 'laugh', + 'circus', + 'magic', + 'donald trump', + 'mcdonalds', + 'super hero', + 'crazy', + 'killer', + 'costume', + 'disguise', + 'smileys', + 'mood', + 'emotion', + 'emotions', + 'emotional', + 'funny', + 'samhain', + 'laughing', + 'lol', + 'rofl', + 'lmao', + 'lmfao', + 'hilarious', + 'ha', + 'haha', + 'chuckle', + 'comedy', + 'giggle', + 'hehe', + 'joyful', + 'laugh out loud', + 'rire', + 'tee hee', + 'jaja', + 'circus tent', + 'clown', + 'clowns', + 'spell', + 'genie', + 'magical', + 'trump', + 'ronald mcdonald', + 'macdo', + 'superhero', + 'superman', + 'batman', + 'weird', + 'awkward', + 'insane', + 'wild', + 'savage', + 'scary clown' + ]), + Emoji( + name: 'pile of poo', + char: '\u{1F4A9}', + shortName: 'poop', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceCostume, + keywords: [ + 'comic', + 'dung', + 'face', + 'monster', + 'poo', + 'poop', + 'uc6', + 'silly', + 'bathroom', + 'dead', + 'sol', + 'diarrhea', + 'shit', + 'bitch', + 'stinky', + 'donald trump', + 'dumb', + 'ugly', + 'funny', + 'death', + 'die', + 'dying', + 'fart', + 'goth', + 'grave', + 'headstone', + 'horror', + 'hurt', + 'kill', + 'murder', + 'tomb', + 'toot', + 'died', + 'shit outta luck', + 'shit out of luck', + 'bad luck', + 'shits', + 'the shits', + 'poop', + 'turd', + 'feces', + 'pile', + 'merde', + 'butthole', + 'caca', + 'crap', + 'dirty', + 'pooo', + 'mess', + 'brown', + 'poopoo', + 'puta', + 'pute', + 'smell', + 'stink', + 'odor', + 'trump', + 'idiot', + 'ignorant', + 'stupid' + ]), + Emoji( + name: 'ghost', + char: '\u{1F47B}', + shortName: 'ghost', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceCostume, + keywords: [ + 'creature', + 'face', + 'fairy tale', + 'fantasy', + 'monster', + 'uc6', + 'holidays', + 'halloween', + 'dead', + 'monster', + 'wow', + 'disney', + 'pacman', + 'disguise', + 'holiday', + 'samhain', + 'death', + 'die', + 'dying', + 'fart', + 'goth', + 'grave', + 'headstone', + 'horror', + 'hurt', + 'kill', + 'murder', + 'tomb', + 'toot', + 'died', + 'monsters', + 'beast', + 'surprised', + 'scared', + 'shocked', + 'whoa', + 'surprise', + 'scary', + 'nervous', + 'shaking', + 'afraid', + 'amaze', + 'amazing', + 'creepy', + 'cringe', + 'gasp', + 'anxious', + 'mind blown', + 'cartoon', + 'pac man' + ]), + Emoji( + name: 'skull', + char: '\u{1F480}', + shortName: 'skull', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceNegative, + keywords: [ + 'death', + 'face', + 'fairy tale', + 'monster', + 'uc6', + 'halloween', + 'dead', + 'skull', + 'wow', + 'harry potter', + 'pirate', + 'poison', + 'super hero', + 'killer', + 'bones', + 'samhain', + 'death', + 'die', + 'dying', + 'fart', + 'goth', + 'grave', + 'headstone', + 'horror', + 'hurt', + 'kill', + 'murder', + 'tomb', + 'toot', + 'died', + 'skull and crossbones', + 'skeleton', + 'surprised', + 'scared', + 'shocked', + 'whoa', + 'surprise', + 'scary', + 'nervous', + 'shaking', + 'afraid', + 'amaze', + 'amazing', + 'creepy', + 'cringe', + 'gasp', + 'anxious', + 'mind blown', + 'toxic', + 'toxins', + 'superhero', + 'superman', + 'batman', + 'savage', + 'scary clown', + 'Os', + 'hueso' + ]), + Emoji( + name: 'skull and crossbones', + char: '\u{2620}\u{FE0F}', + shortName: 'skull_crossbones', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceNegative, + keywords: [ + 'crossbones', + 'death', + 'face', + 'monster', + 'skull', + 'uc1', + 'halloween', + 'dead', + 'skull', + 'wow', + 'deadpool', + 'pirate', + 'danger', + 'disney', + 'poison', + 'killer', + 'bones', + 'samhain', + 'death', + 'die', + 'dying', + 'fart', + 'goth', + 'grave', + 'headstone', + 'horror', + 'hurt', + 'kill', + 'murder', + 'tomb', + 'toot', + 'died', + 'skull and crossbones', + 'skeleton', + 'surprised', + 'scared', + 'shocked', + 'whoa', + 'surprise', + 'scary', + 'nervous', + 'shaking', + 'afraid', + 'amaze', + 'amazing', + 'creepy', + 'cringe', + 'gasp', + 'anxious', + 'mind blown', + 'warn', + 'attention', + 'caution', + 'alert', + 'error', + 'panic', + 'restricted', + "don't", + 'dont', + 'dangerous', + 'cartoon', + 'toxic', + 'toxins', + 'savage', + 'scary clown', + 'Os', + 'hueso' + ]), + Emoji( + name: 'alien', + char: '\u{1F47D}', + shortName: 'alien', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceCostume, + keywords: [ + 'creature', + 'extraterrestrial', + 'face', + 'fairy tale', + 'fantasy', + 'monster', + 'ufo', + 'uc6', + 'halloween', + 'space', + 'monster', + 'alien', + 'scientology', + 'star wars', + 'disguise', + 'samhain', + 'outer space', + 'galaxy', + 'universe', + 'nasa', + 'spaceship', + 'monsters', + 'beast', + 'ufo', + 'scientologist' + ]), + Emoji( + name: 'alien monster', + char: '\u{1F47E}', + shortName: 'space_invader', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceCostume, + keywords: [ + 'alien', + 'creature', + 'extraterrestrial', + 'face', + 'fairy tale', + 'fantasy', + 'monster', + 'ufo', + 'uc6', + 'halloween', + 'space', + 'monster', + 'alien', + 'star wars', + 'vintage', + 'pacman', + 'samhain', + 'outer space', + 'galaxy', + 'universe', + 'nasa', + 'spaceship', + 'monsters', + 'beast', + 'ufo', + 'pac man' + ]), + Emoji( + name: 'robot', + char: '\u{1F916}', + shortName: 'robot', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.faceCostume, + keywords: [ + 'face', + 'monster', + 'robot', + 'uc8', + 'halloween', + 'monster', + 'disney', + 'drone', + 'samhain', + 'monsters', + 'beast', + 'cartoon' + ]), + Emoji( + name: 'jack-o-lantern', + char: '\u{1F383}', + shortName: 'jack_o_lantern', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.event, + keywords: [ + 'celebration', + 'halloween', + 'jack', + 'lantern', + 'uc6', + 'holidays', + 'halloween', + 'pumpkin', + 'minecraft', + 'holiday', + 'samhain', + 'jack o lantern', + 'zucca', + 'citrouille' + ]), + Emoji( + name: 'grinning cat', + char: '\u{1F63A}', + shortName: 'smiley_cat', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.catFace, + keywords: [ + 'cat', + 'face', + 'mouth', + 'open', + 'smile', + 'uc6', + 'animal', + 'happy', + 'silly', + 'cat', + 'animals', + 'animal kingdom', + 'hooray', + 'cheek', + 'cheeky', + 'excited', + 'feliz', + 'heureux', + 'cheerful', + 'delighted', + 'ecstatic', + 'elated', + 'glad', + 'joy', + 'merry', + 'funny', + 'kitty', + 'kitten', + 'cats', + 'kittens', + 'kitties', + 'feline', + 'felines', + 'cat face', + 'gato', + 'meow' + ]), + Emoji( + name: 'grinning cat with smiling eyes', + char: '\u{1F638}', + shortName: 'smile_cat', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.catFace, + keywords: [ + 'cat', + 'eye', + 'face', + 'grin', + 'smile', + 'uc6', + 'animal', + 'happy', + 'silly', + 'cat', + 'porn', + 'animals', + 'animal kingdom', + 'hooray', + 'cheek', + 'cheeky', + 'excited', + 'feliz', + 'heureux', + 'cheerful', + 'delighted', + 'ecstatic', + 'elated', + 'glad', + 'joy', + 'merry', + 'funny', + 'kitty', + 'kitten', + 'cats', + 'kittens', + 'kitties', + 'feline', + 'felines', + 'cat face', + 'gato', + 'meow' + ]), + Emoji( + name: 'cat with tears of joy', + char: '\u{1F639}', + shortName: 'joy_cat', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.catFace, + keywords: [ + 'cat', + 'face', + 'joy', + 'tear', + 'uc6', + 'animal', + 'happy', + 'silly', + 'cry', + 'laugh', + 'cat', + 'sarcastic', + 'tease', + 'animals', + 'animal kingdom', + 'hooray', + 'cheek', + 'cheeky', + 'excited', + 'feliz', + 'heureux', + 'cheerful', + 'delighted', + 'ecstatic', + 'elated', + 'glad', + 'joy', + 'merry', + 'funny', + 'crying', + 'weeping', + 'weep', + 'sob', + 'sobbing', + 'tear', + 'tears', + 'bawling', + 'laughing', + 'lol', + 'rofl', + 'lmao', + 'lmfao', + 'hilarious', + 'ha', + 'haha', + 'chuckle', + 'comedy', + 'giggle', + 'hehe', + 'joyful', + 'laugh out loud', + 'rire', + 'tee hee', + 'jaja', + 'kitty', + 'kitten', + 'cats', + 'kittens', + 'kitties', + 'feline', + 'felines', + 'cat face', + 'gato', + 'meow', + 'sarcasm', + 'joke', + 'kidding' + ]), + Emoji( + name: 'smiling cat with heart-eyes', + char: '\u{1F63B}', + shortName: 'heart_eyes_cat', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.catFace, + keywords: [ + 'cat', + 'eye', + 'face', + 'love', + 'smile', + 'uc6', + 'animal', + 'happy', + 'love', + 'cat', + 'heart eyes', + 'beautiful', + 'pussy', + 'porn', + 'animals', + 'animal kingdom', + 'hooray', + 'cheek', + 'cheeky', + 'excited', + 'feliz', + 'heureux', + 'cheerful', + 'delighted', + 'ecstatic', + 'elated', + 'glad', + 'joy', + 'merry', + 'i love you', + 'te amo', + "je t'aime", + 'anniversary', + 'lovin', + 'amour', + 'aimer', + 'amor', + 'valentines day', + 'enamour', + 'lovey', + 'kitty', + 'kitten', + 'cats', + 'kittens', + 'kitties', + 'feline', + 'felines', + 'cat face', + 'gato', + 'meow', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'condom' + ]), + Emoji( + name: 'cat with wry smile', + char: '\u{1F63C}', + shortName: 'smirk_cat', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.catFace, + keywords: [ + 'cat', + 'face', + 'ironic', + 'smile', + 'wry', + 'uc6', + 'animal', + 'cat', + 'animals', + 'animal kingdom', + 'kitty', + 'kitten', + 'cats', + 'kittens', + 'kitties', + 'feline', + 'felines', + 'cat face', + 'gato', + 'meow' + ]), + Emoji( + name: 'kissing cat', + char: '\u{1F63D}', + shortName: 'kissing_cat', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.catFace, + keywords: [ + 'cat', + 'eye', + 'face', + 'kiss', + 'uc6', + 'animal', + 'love', + 'cat', + 'kisses', + 'animals', + 'animal kingdom', + 'i love you', + 'te amo', + "je t'aime", + 'anniversary', + 'lovin', + 'amour', + 'aimer', + 'amor', + 'valentines day', + 'enamour', + 'lovey', + 'kitty', + 'kitten', + 'cats', + 'kittens', + 'kitties', + 'feline', + 'felines', + 'cat face', + 'gato', + 'meow', + 'bisous', + 'beijos', + 'besos', + 'bise', + 'blowing kisses', + 'kissy' + ]), + Emoji( + name: 'weary cat', + char: '\u{1F640}', + shortName: 'scream_cat', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.catFace, + keywords: [ + 'cat', + 'face', + 'oh', + 'surprised', + 'weary', + 'uc6', + 'animal', + 'cat', + 'animals', + 'animal kingdom', + 'kitty', + 'kitten', + 'cats', + 'kittens', + 'kitties', + 'feline', + 'felines', + 'cat face', + 'gato', + 'meow' + ]), + Emoji( + name: 'crying cat', + char: '\u{1F63F}', + shortName: 'crying_cat_face', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.catFace, + keywords: [ + 'cat', + 'cry', + 'face', + 'sad', + 'tear', + 'uc6', + 'animal', + 'cry', + 'cat', + 'animals', + 'animal kingdom', + 'crying', + 'weeping', + 'weep', + 'sob', + 'sobbing', + 'tear', + 'tears', + 'bawling', + 'kitty', + 'kitten', + 'cats', + 'kittens', + 'kitties', + 'feline', + 'felines', + 'cat face', + 'gato', + 'meow' + ]), + Emoji( + name: 'pouting cat', + char: '\u{1F63E}', + shortName: 'pouting_cat', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.catFace, + keywords: [ + 'cat', + 'face', + 'pouting', + 'uc6', + 'animal', + 'cat', + 'animals', + 'animal kingdom', + 'kitty', + 'kitten', + 'cats', + 'kittens', + 'kitties', + 'feline', + 'felines', + 'cat face', + 'gato', + 'meow' + ]), + Emoji( + name: 'palms up together', + char: '\u{1F932}', + shortName: 'palms_up_together', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.hands, + keywords: [ + 'uc10', + 'diversity', + 'body', + 'hands', + 'pray', + 'soul', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'prayer', + 'praying', + 'prayers', + 'grateful', + 'sorry', + 'heaven', + 'bless', + 'faith', + 'holy', + 'spirit', + 'hopeful', + 'blessed', + 'preach', + 'offering' + ]), + Emoji( + name: 'palms up together: light skin tone', + char: '\u{1F932}\u{1F3FB}', + shortName: 'palms_up_together_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.hands, + keywords: [ + 'light skin tone', + 'prayer', + 'uc10', + 'diversity', + 'body', + 'hands', + 'pray', + 'soul', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'prayer', + 'praying', + 'prayers', + 'grateful', + 'sorry', + 'heaven', + 'bless', + 'faith', + 'holy', + 'spirit', + 'hopeful', + 'blessed', + 'preach', + 'offering' + ], + modifiable: true), + Emoji( + name: 'palms up together: medium-light skin tone', + char: '\u{1F932}\u{1F3FC}', + shortName: 'palms_up_together_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.hands, + keywords: [ + 'medium-light skin tone', + 'prayer', + 'uc10', + 'diversity', + 'body', + 'hands', + 'pray', + 'soul', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'prayer', + 'praying', + 'prayers', + 'grateful', + 'sorry', + 'heaven', + 'bless', + 'faith', + 'holy', + 'spirit', + 'hopeful', + 'blessed', + 'preach', + 'offering' + ], + modifiable: true), + Emoji( + name: 'palms up together: medium skin tone', + char: '\u{1F932}\u{1F3FD}', + shortName: 'palms_up_together_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.hands, + keywords: [ + 'medium skin tone', + 'prayer', + 'uc10', + 'diversity', + 'body', + 'hands', + 'pray', + 'soul', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'prayer', + 'praying', + 'prayers', + 'grateful', + 'sorry', + 'heaven', + 'bless', + 'faith', + 'holy', + 'spirit', + 'hopeful', + 'blessed', + 'preach', + 'offering' + ], + modifiable: true), + Emoji( + name: 'palms up together: medium-dark skin tone', + char: '\u{1F932}\u{1F3FE}', + shortName: 'palms_up_together_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.hands, + keywords: [ + 'medium-dark skin tone', + 'prayer', + 'uc10', + 'diversity', + 'body', + 'hands', + 'pray', + 'soul', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'prayer', + 'praying', + 'prayers', + 'grateful', + 'sorry', + 'heaven', + 'bless', + 'faith', + 'holy', + 'spirit', + 'hopeful', + 'blessed', + 'preach', + 'offering' + ], + modifiable: true), + Emoji( + name: 'palms up together: dark skin tone', + char: '\u{1F932}\u{1F3FF}', + shortName: 'palms_up_together_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.hands, + keywords: [ + 'dark skin tone', + 'prayer', + 'uc10', + 'diversity', + 'body', + 'hands', + 'pray', + 'soul', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'prayer', + 'praying', + 'prayers', + 'grateful', + 'sorry', + 'heaven', + 'bless', + 'faith', + 'holy', + 'spirit', + 'hopeful', + 'blessed', + 'preach', + 'offering' + ], + modifiable: true), + Emoji( + name: 'open hands', + char: '\u{1F450}', + shortName: 'open_hands', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.hands, + keywords: [ + 'hand', + 'open', + 'uc6', + 'diversity', + 'body', + 'hands', + 'hi', + 'thank you', + 'condolence', + 'private', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'compassion', + 'прив', + 'privé', + 'privado', + 'reserved' + ]), + Emoji( + name: 'open hands: light skin tone', + char: '\u{1F450}\u{1F3FB}', + shortName: 'open_hands_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.hands, + keywords: [ + 'hand', + 'light skin tone', + 'open', + 'uc8', + 'diversity', + 'body', + 'hands', + 'hi', + 'thank you', + 'condolence', + 'private', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'compassion', + 'прив', + 'privé', + 'privado', + 'reserved' + ], + modifiable: true), + Emoji( + name: 'open hands: medium-light skin tone', + char: '\u{1F450}\u{1F3FC}', + shortName: 'open_hands_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.hands, + keywords: [ + 'hand', + 'medium-light skin tone', + 'open', + 'uc8', + 'diversity', + 'body', + 'hands', + 'hi', + 'thank you', + 'condolence', + 'private', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'compassion', + 'прив', + 'privé', + 'privado', + 'reserved' + ], + modifiable: true), + Emoji( + name: 'open hands: medium skin tone', + char: '\u{1F450}\u{1F3FD}', + shortName: 'open_hands_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.hands, + keywords: [ + 'hand', + 'medium skin tone', + 'open', + 'uc8', + 'diversity', + 'body', + 'hands', + 'hi', + 'thank you', + 'condolence', + 'private', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'compassion', + 'прив', + 'privé', + 'privado', + 'reserved' + ], + modifiable: true), + Emoji( + name: 'open hands: medium-dark skin tone', + char: '\u{1F450}\u{1F3FE}', + shortName: 'open_hands_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.hands, + keywords: [ + 'hand', + 'medium-dark skin tone', + 'open', + 'uc8', + 'diversity', + 'body', + 'hands', + 'hi', + 'thank you', + 'condolence', + 'private', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'compassion', + 'прив', + 'privé', + 'privado', + 'reserved' + ], + modifiable: true), + Emoji( + name: 'open hands: dark skin tone', + char: '\u{1F450}\u{1F3FF}', + shortName: 'open_hands_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.hands, + keywords: [ + 'dark skin tone', + 'hand', + 'open', + 'uc8', + 'diversity', + 'body', + 'hands', + 'hi', + 'thank you', + 'condolence', + 'private', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'compassion', + 'прив', + 'privé', + 'privado', + 'reserved' + ], + modifiable: true), + Emoji( + name: 'raising hands', + char: '\u{1F64C}', + shortName: 'raised_hands', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.hands, + keywords: [ + 'celebration', + 'gesture', + 'hand', + 'hooray', + 'raised', + 'uc6', + 'diversity', + 'happy', + 'body', + 'hands', + 'award', + 'hi', + 'thank you', + 'perfect', + 'pray', + 'good', + 'girls night', + 'easter', + 'fame', + 'festivus', + 'language', + 'protest', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'hooray', + 'cheek', + 'cheeky', + 'excited', + 'feliz', + 'heureux', + 'cheerful', + 'delighted', + 'ecstatic', + 'elated', + 'glad', + 'joy', + 'merry', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'awards', + 'prize', + 'prizes', + 'trophy', + 'trophies', + 'spot', + 'best', + 'champion', + 'hero', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'perfecto', + 'perfection', + 'superb', + 'flawless', + 'excellent', + 'supreme', + 'super', + 'great', + 'prayer', + 'praying', + 'prayers', + 'grateful', + 'sorry', + 'heaven', + 'bless', + 'faith', + 'holy', + 'spirit', + 'hopeful', + 'blessed', + 'preach', + 'offering', + 'good job', + 'nice', + 'well done', + 'bravo', + 'congratulations', + 'congrats', + 'ladies night', + 'girls only', + 'girlfriend', + 'famous', + 'celebrity', + 'blm', + 'demonstration' + ]), + Emoji( + name: 'raising hands: light skin tone', + char: '\u{1F64C}\u{1F3FB}', + shortName: 'raised_hands_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.hands, + keywords: [ + 'celebration', + 'gesture', + 'hand', + 'hooray', + 'light skin tone', + 'raised', + 'uc8', + 'diversity', + 'happy', + 'body', + 'hands', + 'award', + 'hi', + 'thank you', + 'perfect', + 'pray', + 'good', + 'girls night', + 'easter', + 'fame', + 'festivus', + 'language', + 'protest', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'hooray', + 'cheek', + 'cheeky', + 'excited', + 'feliz', + 'heureux', + 'cheerful', + 'delighted', + 'ecstatic', + 'elated', + 'glad', + 'joy', + 'merry', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'awards', + 'prize', + 'prizes', + 'trophy', + 'trophies', + 'spot', + 'best', + 'champion', + 'hero', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'perfecto', + 'perfection', + 'superb', + 'flawless', + 'excellent', + 'supreme', + 'super', + 'great', + 'prayer', + 'praying', + 'prayers', + 'grateful', + 'sorry', + 'heaven', + 'bless', + 'faith', + 'holy', + 'spirit', + 'hopeful', + 'blessed', + 'preach', + 'offering', + 'good job', + 'nice', + 'well done', + 'bravo', + 'congratulations', + 'congrats', + 'ladies night', + 'girls only', + 'girlfriend', + 'famous', + 'celebrity', + 'blm', + 'demonstration' + ], + modifiable: true), + Emoji( + name: 'raising hands: medium-light skin tone', + char: '\u{1F64C}\u{1F3FC}', + shortName: 'raised_hands_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.hands, + keywords: [ + 'celebration', + 'gesture', + 'hand', + 'hooray', + 'medium-light skin tone', + 'raised', + 'uc8', + 'diversity', + 'happy', + 'body', + 'hands', + 'award', + 'hi', + 'thank you', + 'perfect', + 'pray', + 'good', + 'girls night', + 'easter', + 'fame', + 'festivus', + 'language', + 'protest', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'hooray', + 'cheek', + 'cheeky', + 'excited', + 'feliz', + 'heureux', + 'cheerful', + 'delighted', + 'ecstatic', + 'elated', + 'glad', + 'joy', + 'merry', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'awards', + 'prize', + 'prizes', + 'trophy', + 'trophies', + 'spot', + 'best', + 'champion', + 'hero', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'perfecto', + 'perfection', + 'superb', + 'flawless', + 'excellent', + 'supreme', + 'super', + 'great', + 'prayer', + 'praying', + 'prayers', + 'grateful', + 'sorry', + 'heaven', + 'bless', + 'faith', + 'holy', + 'spirit', + 'hopeful', + 'blessed', + 'preach', + 'offering', + 'good job', + 'nice', + 'well done', + 'bravo', + 'congratulations', + 'congrats', + 'ladies night', + 'girls only', + 'girlfriend', + 'famous', + 'celebrity', + 'blm', + 'demonstration' + ], + modifiable: true), + Emoji( + name: 'raising hands: medium skin tone', + char: '\u{1F64C}\u{1F3FD}', + shortName: 'raised_hands_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.hands, + keywords: [ + 'celebration', + 'gesture', + 'hand', + 'hooray', + 'medium skin tone', + 'raised', + 'uc8', + 'diversity', + 'happy', + 'body', + 'hands', + 'award', + 'hi', + 'thank you', + 'perfect', + 'pray', + 'good', + 'girls night', + 'easter', + 'fame', + 'festivus', + 'language', + 'protest', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'hooray', + 'cheek', + 'cheeky', + 'excited', + 'feliz', + 'heureux', + 'cheerful', + 'delighted', + 'ecstatic', + 'elated', + 'glad', + 'joy', + 'merry', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'awards', + 'prize', + 'prizes', + 'trophy', + 'trophies', + 'spot', + 'best', + 'champion', + 'hero', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'perfecto', + 'perfection', + 'superb', + 'flawless', + 'excellent', + 'supreme', + 'super', + 'great', + 'prayer', + 'praying', + 'prayers', + 'grateful', + 'sorry', + 'heaven', + 'bless', + 'faith', + 'holy', + 'spirit', + 'hopeful', + 'blessed', + 'preach', + 'offering', + 'good job', + 'nice', + 'well done', + 'bravo', + 'congratulations', + 'congrats', + 'ladies night', + 'girls only', + 'girlfriend', + 'famous', + 'celebrity', + 'blm', + 'demonstration' + ], + modifiable: true), + Emoji( + name: 'raising hands: medium-dark skin tone', + char: '\u{1F64C}\u{1F3FE}', + shortName: 'raised_hands_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.hands, + keywords: [ + 'celebration', + 'gesture', + 'hand', + 'hooray', + 'medium-dark skin tone', + 'raised', + 'uc8', + 'diversity', + 'happy', + 'body', + 'hands', + 'award', + 'hi', + 'thank you', + 'perfect', + 'pray', + 'good', + 'girls night', + 'easter', + 'fame', + 'festivus', + 'language', + 'protest', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'hooray', + 'cheek', + 'cheeky', + 'excited', + 'feliz', + 'heureux', + 'cheerful', + 'delighted', + 'ecstatic', + 'elated', + 'glad', + 'joy', + 'merry', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'awards', + 'prize', + 'prizes', + 'trophy', + 'trophies', + 'spot', + 'best', + 'champion', + 'hero', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'perfecto', + 'perfection', + 'superb', + 'flawless', + 'excellent', + 'supreme', + 'super', + 'great', + 'prayer', + 'praying', + 'prayers', + 'grateful', + 'sorry', + 'heaven', + 'bless', + 'faith', + 'holy', + 'spirit', + 'hopeful', + 'blessed', + 'preach', + 'offering', + 'good job', + 'nice', + 'well done', + 'bravo', + 'congratulations', + 'congrats', + 'ladies night', + 'girls only', + 'girlfriend', + 'famous', + 'celebrity', + 'blm', + 'demonstration' + ], + modifiable: true), + Emoji( + name: 'raising hands: dark skin tone', + char: '\u{1F64C}\u{1F3FF}', + shortName: 'raised_hands_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.hands, + keywords: [ + 'celebration', + 'dark skin tone', + 'gesture', + 'hand', + 'hooray', + 'raised', + 'uc8', + 'diversity', + 'happy', + 'body', + 'hands', + 'award', + 'hi', + 'thank you', + 'perfect', + 'pray', + 'good', + 'girls night', + 'easter', + 'fame', + 'festivus', + 'language', + 'protest', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'hooray', + 'cheek', + 'cheeky', + 'excited', + 'feliz', + 'heureux', + 'cheerful', + 'delighted', + 'ecstatic', + 'elated', + 'glad', + 'joy', + 'merry', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'awards', + 'prize', + 'prizes', + 'trophy', + 'trophies', + 'spot', + 'best', + 'champion', + 'hero', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'perfecto', + 'perfection', + 'superb', + 'flawless', + 'excellent', + 'supreme', + 'super', + 'great', + 'prayer', + 'praying', + 'prayers', + 'grateful', + 'sorry', + 'heaven', + 'bless', + 'faith', + 'holy', + 'spirit', + 'hopeful', + 'blessed', + 'preach', + 'offering', + 'good job', + 'nice', + 'well done', + 'bravo', + 'congratulations', + 'congrats', + 'ladies night', + 'girls only', + 'girlfriend', + 'famous', + 'celebrity', + 'blm', + 'demonstration' + ], + modifiable: true), + Emoji( + name: 'clapping hands', + char: '\u{1F44F}', + shortName: 'clap', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.hands, + keywords: [ + 'clap', + 'hand', + 'uc6', + 'diversity', + 'happy', + 'body', + 'hands', + 'thank you', + 'win', + 'awesome', + 'good', + 'beautiful', + 'clap', + 'pussy', + 'celebrate', + 'pleased', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'hooray', + 'cheek', + 'cheeky', + 'excited', + 'feliz', + 'heureux', + 'cheerful', + 'delighted', + 'ecstatic', + 'elated', + 'glad', + 'joy', + 'merry', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'winning', + 'killing it', + 'crushing it', + 'victory', + 'victorious', + 'success', + 'successful', + 'winner', + 'okay', + 'got it', + 'cool', + 'ok', + 'will do', + 'like', + 'bien', + 'yep', + 'yup', + 'good job', + 'nice', + 'well done', + 'bravo', + 'congratulations', + 'congrats', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'clapping', + 'claps', + 'clapping hands', + 'applause', + 'condom', + 'celebration', + 'event', + 'celebrating', + 'festa', + 'parties', + 'events', + 'new years', + 'new year', + 'fiesta', + 'fete', + 'newyear', + 'party', + 'festive', + 'festival', + 'yolo', + 'festejar', + 'please', + 'chill', + 'confident', + 'content' + ]), + Emoji( + name: 'clapping hands: light skin tone', + char: '\u{1F44F}\u{1F3FB}', + shortName: 'clap_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.hands, + keywords: [ + 'clap', + 'hand', + 'light skin tone', + 'uc8', + 'diversity', + 'happy', + 'body', + 'hands', + 'thank you', + 'win', + 'awesome', + 'good', + 'beautiful', + 'clap', + 'pussy', + 'celebrate', + 'pleased', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'hooray', + 'cheek', + 'cheeky', + 'excited', + 'feliz', + 'heureux', + 'cheerful', + 'delighted', + 'ecstatic', + 'elated', + 'glad', + 'joy', + 'merry', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'winning', + 'killing it', + 'crushing it', + 'victory', + 'victorious', + 'success', + 'successful', + 'winner', + 'okay', + 'got it', + 'cool', + 'ok', + 'will do', + 'like', + 'bien', + 'yep', + 'yup', + 'good job', + 'nice', + 'well done', + 'bravo', + 'congratulations', + 'congrats', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'clapping', + 'claps', + 'clapping hands', + 'applause', + 'condom', + 'celebration', + 'event', + 'celebrating', + 'festa', + 'parties', + 'events', + 'new years', + 'new year', + 'fiesta', + 'fete', + 'newyear', + 'party', + 'festive', + 'festival', + 'yolo', + 'festejar', + 'please', + 'chill', + 'confident', + 'content' + ], + modifiable: true), + Emoji( + name: 'clapping hands: medium-light skin tone', + char: '\u{1F44F}\u{1F3FC}', + shortName: 'clap_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.hands, + keywords: [ + 'clap', + 'hand', + 'medium-light skin tone', + 'uc8', + 'diversity', + 'happy', + 'body', + 'hands', + 'thank you', + 'win', + 'awesome', + 'good', + 'beautiful', + 'clap', + 'pussy', + 'celebrate', + 'pleased', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'hooray', + 'cheek', + 'cheeky', + 'excited', + 'feliz', + 'heureux', + 'cheerful', + 'delighted', + 'ecstatic', + 'elated', + 'glad', + 'joy', + 'merry', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'winning', + 'killing it', + 'crushing it', + 'victory', + 'victorious', + 'success', + 'successful', + 'winner', + 'okay', + 'got it', + 'cool', + 'ok', + 'will do', + 'like', + 'bien', + 'yep', + 'yup', + 'good job', + 'nice', + 'well done', + 'bravo', + 'congratulations', + 'congrats', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'clapping', + 'claps', + 'clapping hands', + 'applause', + 'condom', + 'celebration', + 'event', + 'celebrating', + 'festa', + 'parties', + 'events', + 'new years', + 'new year', + 'fiesta', + 'fete', + 'newyear', + 'party', + 'festive', + 'festival', + 'yolo', + 'festejar', + 'please', + 'chill', + 'confident', + 'content' + ], + modifiable: true), + Emoji( + name: 'clapping hands: medium skin tone', + char: '\u{1F44F}\u{1F3FD}', + shortName: 'clap_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.hands, + keywords: [ + 'clap', + 'hand', + 'medium skin tone', + 'uc8', + 'diversity', + 'happy', + 'body', + 'hands', + 'thank you', + 'win', + 'awesome', + 'good', + 'beautiful', + 'clap', + 'pussy', + 'celebrate', + 'pleased', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'hooray', + 'cheek', + 'cheeky', + 'excited', + 'feliz', + 'heureux', + 'cheerful', + 'delighted', + 'ecstatic', + 'elated', + 'glad', + 'joy', + 'merry', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'winning', + 'killing it', + 'crushing it', + 'victory', + 'victorious', + 'success', + 'successful', + 'winner', + 'okay', + 'got it', + 'cool', + 'ok', + 'will do', + 'like', + 'bien', + 'yep', + 'yup', + 'good job', + 'nice', + 'well done', + 'bravo', + 'congratulations', + 'congrats', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'clapping', + 'claps', + 'clapping hands', + 'applause', + 'condom', + 'celebration', + 'event', + 'celebrating', + 'festa', + 'parties', + 'events', + 'new years', + 'new year', + 'fiesta', + 'fete', + 'newyear', + 'party', + 'festive', + 'festival', + 'yolo', + 'festejar', + 'please', + 'chill', + 'confident', + 'content' + ], + modifiable: true), + Emoji( + name: 'clapping hands: medium-dark skin tone', + char: '\u{1F44F}\u{1F3FE}', + shortName: 'clap_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.hands, + keywords: [ + 'clap', + 'hand', + 'medium-dark skin tone', + 'uc8', + 'diversity', + 'happy', + 'body', + 'hands', + 'thank you', + 'win', + 'awesome', + 'good', + 'beautiful', + 'clap', + 'pussy', + 'celebrate', + 'pleased', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'hooray', + 'cheek', + 'cheeky', + 'excited', + 'feliz', + 'heureux', + 'cheerful', + 'delighted', + 'ecstatic', + 'elated', + 'glad', + 'joy', + 'merry', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'winning', + 'killing it', + 'crushing it', + 'victory', + 'victorious', + 'success', + 'successful', + 'winner', + 'okay', + 'got it', + 'cool', + 'ok', + 'will do', + 'like', + 'bien', + 'yep', + 'yup', + 'good job', + 'nice', + 'well done', + 'bravo', + 'congratulations', + 'congrats', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'clapping', + 'claps', + 'clapping hands', + 'applause', + 'condom', + 'celebration', + 'event', + 'celebrating', + 'festa', + 'parties', + 'events', + 'new years', + 'new year', + 'fiesta', + 'fete', + 'newyear', + 'party', + 'festive', + 'festival', + 'yolo', + 'festejar', + 'please', + 'chill', + 'confident', + 'content' + ], + modifiable: true), + Emoji( + name: 'clapping hands: dark skin tone', + char: '\u{1F44F}\u{1F3FF}', + shortName: 'clap_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.hands, + keywords: [ + 'clap', + 'dark skin tone', + 'hand', + 'uc8', + 'diversity', + 'happy', + 'body', + 'hands', + 'thank you', + 'win', + 'awesome', + 'good', + 'beautiful', + 'clap', + 'pussy', + 'celebrate', + 'pleased', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'hooray', + 'cheek', + 'cheeky', + 'excited', + 'feliz', + 'heureux', + 'cheerful', + 'delighted', + 'ecstatic', + 'elated', + 'glad', + 'joy', + 'merry', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'winning', + 'killing it', + 'crushing it', + 'victory', + 'victorious', + 'success', + 'successful', + 'winner', + 'okay', + 'got it', + 'cool', + 'ok', + 'will do', + 'like', + 'bien', + 'yep', + 'yup', + 'good job', + 'nice', + 'well done', + 'bravo', + 'congratulations', + 'congrats', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'clapping', + 'claps', + 'clapping hands', + 'applause', + 'condom', + 'celebration', + 'event', + 'celebrating', + 'festa', + 'parties', + 'events', + 'new years', + 'new year', + 'fiesta', + 'fete', + 'newyear', + 'party', + 'festive', + 'festival', + 'yolo', + 'festejar', + 'please', + 'chill', + 'confident', + 'content' + ], + modifiable: true), + Emoji( + name: 'handshake', + char: '\u{1F91D}', + shortName: 'handshake', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.hands, + keywords: [ + 'agreement', + 'hand', + 'handshake', + 'meeting', + 'shake', + 'uc9', + 'diversity', + 'body', + 'hands', + 'hi', + 'business', + 'proud', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle' + ]), + Emoji( + name: 'thumbs up', + char: '\u{1F44D}', + shortName: 'thumbsup', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersClosed, + keywords: [ + '+1', + 'hand', + 'thumb', + 'up', + 'uc6', + 'diversity', + 'happy', + 'body', + 'hands', + 'award', + 'hi', + 'luck', + 'thank you', + 'perfect', + 'awesome', + 'good', + 'beautiful', + 'correct', + 'fun', + 'proud', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'hooray', + 'cheek', + 'cheeky', + 'excited', + 'feliz', + 'heureux', + 'cheerful', + 'delighted', + 'ecstatic', + 'elated', + 'glad', + 'joy', + 'merry', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'awards', + 'prize', + 'prizes', + 'trophy', + 'trophies', + 'spot', + 'best', + 'champion', + 'hero', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'good luck', + 'lucky', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'perfecto', + 'perfection', + 'superb', + 'flawless', + 'excellent', + 'supreme', + 'super', + 'great', + 'okay', + 'got it', + 'cool', + 'ok', + 'will do', + 'like', + 'bien', + 'yep', + 'yup', + 'good job', + 'nice', + 'well done', + 'bravo', + 'congratulations', + 'congrats', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'passing grade', + '(y)' + ]), + Emoji( + name: 'thumbs up: light skin tone', + char: '\u{1F44D}\u{1F3FB}', + shortName: 'thumbsup_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersClosed, + keywords: [ + '+1', + 'hand', + 'light skin tone', + 'thumb', + 'up', + 'uc8', + 'diversity', + 'happy', + 'body', + 'hands', + 'award', + 'hi', + 'luck', + 'thank you', + 'perfect', + 'awesome', + 'good', + 'beautiful', + 'correct', + 'fun', + 'proud', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'hooray', + 'cheek', + 'cheeky', + 'excited', + 'feliz', + 'heureux', + 'cheerful', + 'delighted', + 'ecstatic', + 'elated', + 'glad', + 'joy', + 'merry', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'awards', + 'prize', + 'prizes', + 'trophy', + 'trophies', + 'spot', + 'best', + 'champion', + 'hero', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'good luck', + 'lucky', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'perfecto', + 'perfection', + 'superb', + 'flawless', + 'excellent', + 'supreme', + 'super', + 'great', + 'okay', + 'got it', + 'cool', + 'ok', + 'will do', + 'like', + 'bien', + 'yep', + 'yup', + 'good job', + 'nice', + 'well done', + 'bravo', + 'congratulations', + 'congrats', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'passing grade' + ], + modifiable: true), + Emoji( + name: 'thumbs up: medium-light skin tone', + char: '\u{1F44D}\u{1F3FC}', + shortName: 'thumbsup_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersClosed, + keywords: [ + '+1', + 'hand', + 'medium-light skin tone', + 'thumb', + 'up', + 'uc8', + 'diversity', + 'happy', + 'body', + 'hands', + 'award', + 'hi', + 'luck', + 'thank you', + 'perfect', + 'awesome', + 'good', + 'beautiful', + 'correct', + 'fun', + 'proud', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'hooray', + 'cheek', + 'cheeky', + 'excited', + 'feliz', + 'heureux', + 'cheerful', + 'delighted', + 'ecstatic', + 'elated', + 'glad', + 'joy', + 'merry', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'awards', + 'prize', + 'prizes', + 'trophy', + 'trophies', + 'spot', + 'best', + 'champion', + 'hero', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'good luck', + 'lucky', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'perfecto', + 'perfection', + 'superb', + 'flawless', + 'excellent', + 'supreme', + 'super', + 'great', + 'okay', + 'got it', + 'cool', + 'ok', + 'will do', + 'like', + 'bien', + 'yep', + 'yup', + 'good job', + 'nice', + 'well done', + 'bravo', + 'congratulations', + 'congrats', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'passing grade' + ], + modifiable: true), + Emoji( + name: 'thumbs up: medium skin tone', + char: '\u{1F44D}\u{1F3FD}', + shortName: 'thumbsup_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersClosed, + keywords: [ + '+1', + 'hand', + 'medium skin tone', + 'thumb', + 'up', + 'uc8', + 'diversity', + 'happy', + 'body', + 'hands', + 'award', + 'hi', + 'luck', + 'thank you', + 'perfect', + 'awesome', + 'good', + 'beautiful', + 'correct', + 'fun', + 'proud', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'hooray', + 'cheek', + 'cheeky', + 'excited', + 'feliz', + 'heureux', + 'cheerful', + 'delighted', + 'ecstatic', + 'elated', + 'glad', + 'joy', + 'merry', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'awards', + 'prize', + 'prizes', + 'trophy', + 'trophies', + 'spot', + 'best', + 'champion', + 'hero', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'good luck', + 'lucky', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'perfecto', + 'perfection', + 'superb', + 'flawless', + 'excellent', + 'supreme', + 'super', + 'great', + 'okay', + 'got it', + 'cool', + 'ok', + 'will do', + 'like', + 'bien', + 'yep', + 'yup', + 'good job', + 'nice', + 'well done', + 'bravo', + 'congratulations', + 'congrats', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'passing grade' + ], + modifiable: true), + Emoji( + name: 'thumbs up: medium-dark skin tone', + char: '\u{1F44D}\u{1F3FE}', + shortName: 'thumbsup_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersClosed, + keywords: [ + '+1', + 'hand', + 'medium-dark skin tone', + 'thumb', + 'up', + 'uc8', + 'diversity', + 'happy', + 'body', + 'hands', + 'award', + 'hi', + 'luck', + 'thank you', + 'perfect', + 'awesome', + 'good', + 'beautiful', + 'correct', + 'fun', + 'proud', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'hooray', + 'cheek', + 'cheeky', + 'excited', + 'feliz', + 'heureux', + 'cheerful', + 'delighted', + 'ecstatic', + 'elated', + 'glad', + 'joy', + 'merry', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'awards', + 'prize', + 'prizes', + 'trophy', + 'trophies', + 'spot', + 'best', + 'champion', + 'hero', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'good luck', + 'lucky', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'perfecto', + 'perfection', + 'superb', + 'flawless', + 'excellent', + 'supreme', + 'super', + 'great', + 'okay', + 'got it', + 'cool', + 'ok', + 'will do', + 'like', + 'bien', + 'yep', + 'yup', + 'good job', + 'nice', + 'well done', + 'bravo', + 'congratulations', + 'congrats', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'passing grade' + ], + modifiable: true), + Emoji( + name: 'thumbs up: dark skin tone', + char: '\u{1F44D}\u{1F3FF}', + shortName: 'thumbsup_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersClosed, + keywords: [ + '+1', + 'dark skin tone', + 'hand', + 'thumb', + 'up', + 'uc8', + 'diversity', + 'happy', + 'body', + 'hands', + 'award', + 'hi', + 'luck', + 'thank you', + 'perfect', + 'awesome', + 'good', + 'beautiful', + 'correct', + 'fun', + 'proud', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'hooray', + 'cheek', + 'cheeky', + 'excited', + 'feliz', + 'heureux', + 'cheerful', + 'delighted', + 'ecstatic', + 'elated', + 'glad', + 'joy', + 'merry', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'awards', + 'prize', + 'prizes', + 'trophy', + 'trophies', + 'spot', + 'best', + 'champion', + 'hero', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'good luck', + 'lucky', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'perfecto', + 'perfection', + 'superb', + 'flawless', + 'excellent', + 'supreme', + 'super', + 'great', + 'okay', + 'got it', + 'cool', + 'ok', + 'will do', + 'like', + 'bien', + 'yep', + 'yup', + 'good job', + 'nice', + 'well done', + 'bravo', + 'congratulations', + 'congrats', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'passing grade' + ], + modifiable: true), + Emoji( + name: 'thumbs down', + char: '\u{1F44E}', + shortName: 'thumbsdown', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersClosed, + keywords: [ + '-1', + 'down', + 'hand', + 'thumb', + 'uc6', + 'diversity', + 'sad', + 'body', + 'hands', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'triste', + 'depression', + 'negative', + 'sadness', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers' + ]), + Emoji( + name: 'thumbs down: light skin tone', + char: '\u{1F44E}\u{1F3FB}', + shortName: 'thumbsdown_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersClosed, + keywords: [ + '-1', + 'down', + 'hand', + 'light skin tone', + 'thumb', + 'uc8', + 'diversity', + 'sad', + 'body', + 'hands', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'triste', + 'depression', + 'negative', + 'sadness', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers' + ], + modifiable: true), + Emoji( + name: 'thumbs down: medium-light skin tone', + char: '\u{1F44E}\u{1F3FC}', + shortName: 'thumbsdown_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersClosed, + keywords: [ + '-1', + 'down', + 'hand', + 'medium-light skin tone', + 'thumb', + 'uc8', + 'diversity', + 'sad', + 'body', + 'hands', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'triste', + 'depression', + 'negative', + 'sadness', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers' + ], + modifiable: true), + Emoji( + name: 'thumbs down: medium skin tone', + char: '\u{1F44E}\u{1F3FD}', + shortName: 'thumbsdown_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersClosed, + keywords: [ + '-1', + 'down', + 'hand', + 'medium skin tone', + 'thumb', + 'uc8', + 'diversity', + 'sad', + 'body', + 'hands', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'triste', + 'depression', + 'negative', + 'sadness', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers' + ], + modifiable: true), + Emoji( + name: 'thumbs down: medium-dark skin tone', + char: '\u{1F44E}\u{1F3FE}', + shortName: 'thumbsdown_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersClosed, + keywords: [ + '-1', + 'down', + 'hand', + 'medium-dark skin tone', + 'thumb', + 'uc8', + 'diversity', + 'sad', + 'body', + 'hands', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'triste', + 'depression', + 'negative', + 'sadness', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers' + ], + modifiable: true), + Emoji( + name: 'thumbs down: dark skin tone', + char: '\u{1F44E}\u{1F3FF}', + shortName: 'thumbsdown_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersClosed, + keywords: [ + '-1', + 'dark skin tone', + 'down', + 'hand', + 'thumb', + 'uc8', + 'diversity', + 'sad', + 'body', + 'hands', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'triste', + 'depression', + 'negative', + 'sadness', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers' + ], + modifiable: true), + Emoji( + name: 'oncoming fist', + char: '\u{1F44A}', + shortName: 'punch', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersClosed, + keywords: [ + 'clenched', + 'fist', + 'hand', + 'punch', + 'uc6', + 'diversity', + 'body', + 'hands', + 'hi', + 'fist bump', + 'win', + 'awesome', + 'boys night', + 'friend', + 'fight', + 'hit', + 'proud', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'fist', + 'winning', + 'killing it', + 'crushing it', + 'victory', + 'victorious', + 'success', + 'successful', + 'winner', + 'okay', + 'got it', + 'cool', + 'ok', + 'will do', + 'like', + 'bien', + 'yep', + 'yup', + 'guys night', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'punch', + 'pow', + 'bam' + ]), + Emoji( + name: 'oncoming fist: light skin tone', + char: '\u{1F44A}\u{1F3FB}', + shortName: 'punch_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersClosed, + keywords: [ + 'clenched', + 'fist', + 'hand', + 'light skin tone', + 'punch', + 'uc8', + 'diversity', + 'body', + 'hands', + 'hi', + 'fist bump', + 'win', + 'awesome', + 'boys night', + 'friend', + 'fight', + 'hit', + 'proud', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'fist', + 'winning', + 'killing it', + 'crushing it', + 'victory', + 'victorious', + 'success', + 'successful', + 'winner', + 'okay', + 'got it', + 'cool', + 'ok', + 'will do', + 'like', + 'bien', + 'yep', + 'yup', + 'guys night', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'punch', + 'pow', + 'bam' + ], + modifiable: true), + Emoji( + name: 'oncoming fist: medium-light skin tone', + char: '\u{1F44A}\u{1F3FC}', + shortName: 'punch_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersClosed, + keywords: [ + 'clenched', + 'fist', + 'hand', + 'medium-light skin tone', + 'punch', + 'uc8', + 'diversity', + 'body', + 'hands', + 'hi', + 'fist bump', + 'win', + 'awesome', + 'boys night', + 'friend', + 'fight', + 'hit', + 'proud', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'fist', + 'winning', + 'killing it', + 'crushing it', + 'victory', + 'victorious', + 'success', + 'successful', + 'winner', + 'okay', + 'got it', + 'cool', + 'ok', + 'will do', + 'like', + 'bien', + 'yep', + 'yup', + 'guys night', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'punch', + 'pow', + 'bam' + ], + modifiable: true), + Emoji( + name: 'oncoming fist: medium skin tone', + char: '\u{1F44A}\u{1F3FD}', + shortName: 'punch_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersClosed, + keywords: [ + 'clenched', + 'fist', + 'hand', + 'medium skin tone', + 'punch', + 'uc8', + 'diversity', + 'body', + 'hands', + 'hi', + 'fist bump', + 'win', + 'awesome', + 'boys night', + 'friend', + 'fight', + 'hit', + 'proud', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'fist', + 'winning', + 'killing it', + 'crushing it', + 'victory', + 'victorious', + 'success', + 'successful', + 'winner', + 'okay', + 'got it', + 'cool', + 'ok', + 'will do', + 'like', + 'bien', + 'yep', + 'yup', + 'guys night', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'punch', + 'pow', + 'bam' + ], + modifiable: true), + Emoji( + name: 'oncoming fist: medium-dark skin tone', + char: '\u{1F44A}\u{1F3FE}', + shortName: 'punch_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersClosed, + keywords: [ + 'clenched', + 'fist', + 'hand', + 'medium-dark skin tone', + 'punch', + 'uc8', + 'diversity', + 'body', + 'hands', + 'hi', + 'fist bump', + 'win', + 'awesome', + 'boys night', + 'friend', + 'fight', + 'hit', + 'proud', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'fist', + 'winning', + 'killing it', + 'crushing it', + 'victory', + 'victorious', + 'success', + 'successful', + 'winner', + 'okay', + 'got it', + 'cool', + 'ok', + 'will do', + 'like', + 'bien', + 'yep', + 'yup', + 'guys night', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'punch', + 'pow', + 'bam' + ], + modifiable: true), + Emoji( + name: 'oncoming fist: dark skin tone', + char: '\u{1F44A}\u{1F3FF}', + shortName: 'punch_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersClosed, + keywords: [ + 'clenched', + 'dark skin tone', + 'fist', + 'hand', + 'punch', + 'uc8', + 'diversity', + 'body', + 'hands', + 'hi', + 'fist bump', + 'win', + 'awesome', + 'boys night', + 'friend', + 'fight', + 'hit', + 'proud', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'fist', + 'winning', + 'killing it', + 'crushing it', + 'victory', + 'victorious', + 'success', + 'successful', + 'winner', + 'okay', + 'got it', + 'cool', + 'ok', + 'will do', + 'like', + 'bien', + 'yep', + 'yup', + 'guys night', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'punch', + 'pow', + 'bam' + ], + modifiable: true), + Emoji( + name: 'raised fist', + char: '\u{270A}', + shortName: 'fist', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersClosed, + keywords: [ + 'clenched', + 'fist', + 'hand', + 'punch', + 'uc6', + 'diversity', + 'body', + 'hands', + 'hi', + 'fist bump', + 'win', + 'condolence', + 'proud', + 'language', + 'protest', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'fist', + 'winning', + 'killing it', + 'crushing it', + 'victory', + 'victorious', + 'success', + 'successful', + 'winner', + 'compassion', + 'blm', + 'demonstration' + ]), + Emoji( + name: 'raised fist: light skin tone', + char: '\u{270A}\u{1F3FB}', + shortName: 'fist_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersClosed, + keywords: [ + 'clenched', + 'fist', + 'hand', + 'light skin tone', + 'punch', + 'uc8', + 'diversity', + 'body', + 'hands', + 'hi', + 'fist bump', + 'win', + 'condolence', + 'proud', + 'language', + 'protest', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'fist', + 'winning', + 'killing it', + 'crushing it', + 'victory', + 'victorious', + 'success', + 'successful', + 'winner', + 'compassion', + 'blm', + 'demonstration' + ], + modifiable: true), + Emoji( + name: 'raised fist: medium-light skin tone', + char: '\u{270A}\u{1F3FC}', + shortName: 'fist_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersClosed, + keywords: [ + 'clenched', + 'fist', + 'hand', + 'medium-light skin tone', + 'punch', + 'uc8', + 'diversity', + 'body', + 'hands', + 'hi', + 'fist bump', + 'win', + 'condolence', + 'proud', + 'language', + 'protest', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'fist', + 'winning', + 'killing it', + 'crushing it', + 'victory', + 'victorious', + 'success', + 'successful', + 'winner', + 'compassion', + 'blm', + 'demonstration' + ], + modifiable: true), + Emoji( + name: 'raised fist: medium skin tone', + char: '\u{270A}\u{1F3FD}', + shortName: 'fist_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersClosed, + keywords: [ + 'clenched', + 'fist', + 'hand', + 'medium skin tone', + 'punch', + 'uc8', + 'diversity', + 'body', + 'hands', + 'hi', + 'fist bump', + 'win', + 'condolence', + 'proud', + 'language', + 'protest', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'fist', + 'winning', + 'killing it', + 'crushing it', + 'victory', + 'victorious', + 'success', + 'successful', + 'winner', + 'compassion', + 'blm', + 'demonstration' + ], + modifiable: true), + Emoji( + name: 'raised fist: medium-dark skin tone', + char: '\u{270A}\u{1F3FE}', + shortName: 'fist_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersClosed, + keywords: [ + 'clenched', + 'fist', + 'hand', + 'medium-dark skin tone', + 'punch', + 'uc8', + 'diversity', + 'body', + 'hands', + 'hi', + 'fist bump', + 'win', + 'condolence', + 'proud', + 'language', + 'protest', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'fist', + 'winning', + 'killing it', + 'crushing it', + 'victory', + 'victorious', + 'success', + 'successful', + 'winner', + 'compassion', + 'blm', + 'demonstration' + ], + modifiable: true), + Emoji( + name: 'raised fist: dark skin tone', + char: '\u{270A}\u{1F3FF}', + shortName: 'fist_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersClosed, + keywords: [ + 'clenched', + 'dark skin tone', + 'fist', + 'hand', + 'punch', + 'uc8', + 'diversity', + 'body', + 'hands', + 'hi', + 'fist bump', + 'win', + 'condolence', + 'proud', + 'language', + 'protest', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'fist', + 'winning', + 'killing it', + 'crushing it', + 'victory', + 'victorious', + 'success', + 'successful', + 'winner', + 'compassion', + 'blm', + 'demonstration' + ], + modifiable: true), + Emoji( + name: 'left-facing fist', + char: '\u{1F91B}', + shortName: 'left_facing_fist', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersClosed, + keywords: [ + 'fist', + 'leftwards', + 'uc9', + 'diversity', + 'body', + 'hands', + 'hi', + 'fist bump', + 'win', + 'friend', + 'hit', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'fist', + 'winning', + 'killing it', + 'crushing it', + 'victory', + 'victorious', + 'success', + 'successful', + 'winner', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'punch', + 'pow', + 'bam' + ]), + Emoji( + name: 'left-facing fist: light skin tone', + char: '\u{1F91B}\u{1F3FB}', + shortName: 'left_facing_fist_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersClosed, + keywords: [ + 'fist', + 'leftwards', + 'light skin tone', + 'uc9', + 'diversity', + 'body', + 'hands', + 'hi', + 'fist bump', + 'win', + 'friend', + 'hit', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'fist', + 'winning', + 'killing it', + 'crushing it', + 'victory', + 'victorious', + 'success', + 'successful', + 'winner', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'punch', + 'pow', + 'bam' + ], + modifiable: true), + Emoji( + name: 'left-facing fist: medium-light skin tone', + char: '\u{1F91B}\u{1F3FC}', + shortName: 'left_facing_fist_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersClosed, + keywords: [ + 'fist', + 'leftwards', + 'medium-light skin tone', + 'uc9', + 'diversity', + 'body', + 'hands', + 'hi', + 'fist bump', + 'win', + 'friend', + 'hit', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'fist', + 'winning', + 'killing it', + 'crushing it', + 'victory', + 'victorious', + 'success', + 'successful', + 'winner', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'punch', + 'pow', + 'bam' + ], + modifiable: true), + Emoji( + name: 'left-facing fist: medium skin tone', + char: '\u{1F91B}\u{1F3FD}', + shortName: 'left_facing_fist_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersClosed, + keywords: [ + 'fist', + 'leftwards', + 'medium skin tone', + 'uc9', + 'diversity', + 'body', + 'hands', + 'hi', + 'fist bump', + 'win', + 'friend', + 'hit', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'fist', + 'winning', + 'killing it', + 'crushing it', + 'victory', + 'victorious', + 'success', + 'successful', + 'winner', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'punch', + 'pow', + 'bam' + ], + modifiable: true), + Emoji( + name: 'left-facing fist: medium-dark skin tone', + char: '\u{1F91B}\u{1F3FE}', + shortName: 'left_facing_fist_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersClosed, + keywords: [ + 'fist', + 'leftwards', + 'medium-dark skin tone', + 'uc9', + 'diversity', + 'body', + 'hands', + 'hi', + 'fist bump', + 'win', + 'friend', + 'hit', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'fist', + 'winning', + 'killing it', + 'crushing it', + 'victory', + 'victorious', + 'success', + 'successful', + 'winner', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'punch', + 'pow', + 'bam' + ], + modifiable: true), + Emoji( + name: 'left-facing fist: dark skin tone', + char: '\u{1F91B}\u{1F3FF}', + shortName: 'left_facing_fist_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersClosed, + keywords: [ + 'dark skin tone', + 'fist', + 'leftwards', + 'uc9', + 'diversity', + 'body', + 'hands', + 'hi', + 'fist bump', + 'win', + 'friend', + 'hit', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'fist', + 'winning', + 'killing it', + 'crushing it', + 'victory', + 'victorious', + 'success', + 'successful', + 'winner', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'punch', + 'pow', + 'bam' + ], + modifiable: true), + Emoji( + name: 'right-facing fist', + char: '\u{1F91C}', + shortName: 'right_facing_fist', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersClosed, + keywords: [ + 'fist', + 'rightwards', + 'uc9', + 'diversity', + 'body', + 'hands', + 'hi', + 'fist bump', + 'friend', + 'hit', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'fist', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'punch', + 'pow', + 'bam' + ]), + Emoji( + name: 'right-facing fist: light skin tone', + char: '\u{1F91C}\u{1F3FB}', + shortName: 'right_facing_fist_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersClosed, + keywords: [ + 'fist', + 'light skin tone', + 'rightwards', + 'uc9', + 'diversity', + 'body', + 'hands', + 'hi', + 'fist bump', + 'friend', + 'hit', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'fist', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'punch', + 'pow', + 'bam' + ], + modifiable: true), + Emoji( + name: 'right-facing fist: medium-light skin tone', + char: '\u{1F91C}\u{1F3FC}', + shortName: 'right_facing_fist_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersClosed, + keywords: [ + 'fist', + 'medium-light skin tone', + 'rightwards', + 'uc9', + 'diversity', + 'body', + 'hands', + 'hi', + 'fist bump', + 'friend', + 'hit', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'fist', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'punch', + 'pow', + 'bam' + ], + modifiable: true), + Emoji( + name: 'right-facing fist: medium skin tone', + char: '\u{1F91C}\u{1F3FD}', + shortName: 'right_facing_fist_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersClosed, + keywords: [ + 'fist', + 'medium skin tone', + 'rightwards', + 'uc9', + 'diversity', + 'body', + 'hands', + 'hi', + 'fist bump', + 'friend', + 'hit', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'fist', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'punch', + 'pow', + 'bam' + ], + modifiable: true), + Emoji( + name: 'right-facing fist: medium-dark skin tone', + char: '\u{1F91C}\u{1F3FE}', + shortName: 'right_facing_fist_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersClosed, + keywords: [ + 'fist', + 'medium-dark skin tone', + 'rightwards', + 'uc9', + 'diversity', + 'body', + 'hands', + 'hi', + 'fist bump', + 'friend', + 'hit', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'fist', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'punch', + 'pow', + 'bam' + ], + modifiable: true), + Emoji( + name: 'right-facing fist: dark skin tone', + char: '\u{1F91C}\u{1F3FF}', + shortName: 'right_facing_fist_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersClosed, + keywords: [ + 'dark skin tone', + 'fist', + 'rightwards', + 'uc9', + 'diversity', + 'body', + 'hands', + 'hi', + 'fist bump', + 'friend', + 'hit', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'fist', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'punch', + 'pow', + 'bam' + ], + modifiable: true), + Emoji( + name: 'crossed fingers', + char: '\u{1F91E}', + shortName: 'fingers_crossed', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersPartial, + keywords: [ + 'cross', + 'finger', + 'hand', + 'luck', + 'uc9', + 'diversity', + 'body', + 'hands', + 'donald trump', + 'irish', + 'hope', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'trump', + 'saint patricks day', + 'st patricks day', + 'leprechaun', + 'swear', + 'promise' + ]), + Emoji( + name: 'crossed fingers: light skin tone', + char: '\u{1F91E}\u{1F3FB}', + shortName: 'fingers_crossed_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersPartial, + keywords: [ + 'cross', + 'finger', + 'hand', + 'light skin tone', + 'luck', + 'uc9', + 'diversity', + 'body', + 'hands', + 'donald trump', + 'irish', + 'hope', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'trump', + 'saint patricks day', + 'st patricks day', + 'leprechaun', + 'swear', + 'promise' + ], + modifiable: true), + Emoji( + name: 'crossed fingers: medium-light skin tone', + char: '\u{1F91E}\u{1F3FC}', + shortName: 'fingers_crossed_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersPartial, + keywords: [ + 'cross', + 'finger', + 'hand', + 'luck', + 'medium-light skin tone', + 'uc9', + 'diversity', + 'body', + 'hands', + 'donald trump', + 'irish', + 'hope', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'trump', + 'saint patricks day', + 'st patricks day', + 'leprechaun', + 'swear', + 'promise' + ], + modifiable: true), + Emoji( + name: 'crossed fingers: medium skin tone', + char: '\u{1F91E}\u{1F3FD}', + shortName: 'fingers_crossed_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersPartial, + keywords: [ + 'cross', + 'finger', + 'hand', + 'luck', + 'medium skin tone', + 'uc9', + 'diversity', + 'body', + 'hands', + 'donald trump', + 'irish', + 'hope', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'trump', + 'saint patricks day', + 'st patricks day', + 'leprechaun', + 'swear', + 'promise' + ], + modifiable: true), + Emoji( + name: 'crossed fingers: medium-dark skin tone', + char: '\u{1F91E}\u{1F3FE}', + shortName: 'fingers_crossed_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersPartial, + keywords: [ + 'cross', + 'finger', + 'hand', + 'luck', + 'medium-dark skin tone', + 'uc9', + 'diversity', + 'body', + 'hands', + 'donald trump', + 'irish', + 'hope', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'trump', + 'saint patricks day', + 'st patricks day', + 'leprechaun', + 'swear', + 'promise' + ], + modifiable: true), + Emoji( + name: 'crossed fingers: dark skin tone', + char: '\u{1F91E}\u{1F3FF}', + shortName: 'fingers_crossed_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersPartial, + keywords: [ + 'cross', + 'dark skin tone', + 'finger', + 'hand', + 'luck', + 'uc9', + 'diversity', + 'body', + 'hands', + 'donald trump', + 'irish', + 'hope', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'trump', + 'saint patricks day', + 'st patricks day', + 'leprechaun', + 'swear', + 'promise' + ], + modifiable: true), + Emoji( + name: 'victory hand', + char: '\u{270C}\u{FE0F}', + shortName: 'v', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersPartial, + keywords: [ + 'hand', + 'v', + 'victory', + 'uc1', + 'diversity', + 'peace', + 'body', + 'hands', + 'hi', + 'thank you', + 'girls night', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'peace out', + 'peace sign', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'ladies night', + 'girls only', + 'girlfriend' + ]), + Emoji( + name: 'victory hand: light skin tone', + char: '\u{270C}\u{1F3FB}', + shortName: 'v_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersPartial, + keywords: [ + 'hand', + 'light skin tone', + 'v', + 'victory', + 'uc8', + 'diversity', + 'peace', + 'body', + 'hands', + 'hi', + 'thank you', + 'girls night', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'peace out', + 'peace sign', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'ladies night', + 'girls only', + 'girlfriend' + ], + modifiable: true), + Emoji( + name: 'victory hand: medium-light skin tone', + char: '\u{270C}\u{1F3FC}', + shortName: 'v_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersPartial, + keywords: [ + 'hand', + 'medium-light skin tone', + 'v', + 'victory', + 'uc8', + 'diversity', + 'peace', + 'body', + 'hands', + 'hi', + 'thank you', + 'girls night', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'peace out', + 'peace sign', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'ladies night', + 'girls only', + 'girlfriend' + ], + modifiable: true), + Emoji( + name: 'victory hand: medium skin tone', + char: '\u{270C}\u{1F3FD}', + shortName: 'v_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersPartial, + keywords: [ + 'hand', + 'medium skin tone', + 'v', + 'victory', + 'uc8', + 'diversity', + 'peace', + 'body', + 'hands', + 'hi', + 'thank you', + 'girls night', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'peace out', + 'peace sign', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'ladies night', + 'girls only', + 'girlfriend' + ], + modifiable: true), + Emoji( + name: 'victory hand: medium-dark skin tone', + char: '\u{270C}\u{1F3FE}', + shortName: 'v_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersPartial, + keywords: [ + 'hand', + 'medium-dark skin tone', + 'v', + 'victory', + 'uc8', + 'diversity', + 'peace', + 'body', + 'hands', + 'hi', + 'thank you', + 'girls night', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'peace out', + 'peace sign', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'ladies night', + 'girls only', + 'girlfriend' + ], + modifiable: true), + Emoji( + name: 'victory hand: dark skin tone', + char: '\u{270C}\u{1F3FF}', + shortName: 'v_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersPartial, + keywords: [ + 'dark skin tone', + 'hand', + 'v', + 'victory', + 'uc8', + 'diversity', + 'peace', + 'body', + 'hands', + 'hi', + 'thank you', + 'girls night', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'peace out', + 'peace sign', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'ladies night', + 'girls only', + 'girlfriend' + ], + modifiable: true), + Emoji( + name: 'love-you gesture', + char: '\u{1F91F}', + shortName: 'love_you_gesture', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersPartial, + keywords: [ + 'ILY', + 'hand', + 'uc10', + 'diversity', + 'body', + 'hands', + 'love', + 'beautiful', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'i love you', + 'te amo', + "je t'aime", + 'anniversary', + 'lovin', + 'amour', + 'aimer', + 'amor', + 'valentines day', + 'enamour', + 'lovey', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely' + ]), + Emoji( + name: 'love-you gesture: light skin tone', + char: '\u{1F91F}\u{1F3FB}', + shortName: 'love_you_gesture_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersPartial, + keywords: [ + 'ILY', + 'hand', + 'light skin tone', + 'uc10', + 'diversity', + 'body', + 'hands', + 'love', + 'beautiful', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'i love you', + 'te amo', + "je t'aime", + 'anniversary', + 'lovin', + 'amour', + 'aimer', + 'amor', + 'valentines day', + 'enamour', + 'lovey', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely' + ], + modifiable: true), + Emoji( + name: 'love-you gesture: medium-light skin tone', + char: '\u{1F91F}\u{1F3FC}', + shortName: 'love_you_gesture_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersPartial, + keywords: [ + 'ILY', + 'hand', + 'medium-light skin tone', + 'uc10', + 'diversity', + 'body', + 'hands', + 'love', + 'beautiful', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'i love you', + 'te amo', + "je t'aime", + 'anniversary', + 'lovin', + 'amour', + 'aimer', + 'amor', + 'valentines day', + 'enamour', + 'lovey', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely' + ], + modifiable: true), + Emoji( + name: 'love-you gesture: medium skin tone', + char: '\u{1F91F}\u{1F3FD}', + shortName: 'love_you_gesture_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersPartial, + keywords: [ + 'ILY', + 'hand', + 'medium skin tone', + 'uc10', + 'diversity', + 'body', + 'hands', + 'love', + 'beautiful', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'i love you', + 'te amo', + "je t'aime", + 'anniversary', + 'lovin', + 'amour', + 'aimer', + 'amor', + 'valentines day', + 'enamour', + 'lovey', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely' + ], + modifiable: true), + Emoji( + name: 'love-you gesture: medium-dark skin tone', + char: '\u{1F91F}\u{1F3FE}', + shortName: 'love_you_gesture_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersPartial, + keywords: [ + 'ILY', + 'hand', + 'medium-dark skin tone', + 'uc10', + 'diversity', + 'body', + 'hands', + 'love', + 'beautiful', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'i love you', + 'te amo', + "je t'aime", + 'anniversary', + 'lovin', + 'amour', + 'aimer', + 'amor', + 'valentines day', + 'enamour', + 'lovey', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely' + ], + modifiable: true), + Emoji( + name: 'love-you gesture: dark skin tone', + char: '\u{1F91F}\u{1F3FF}', + shortName: 'love_you_gesture_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersPartial, + keywords: [ + 'ILY', + 'dark skin tone', + 'hand', + 'uc10', + 'diversity', + 'body', + 'hands', + 'love', + 'beautiful', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'i love you', + 'te amo', + "je t'aime", + 'anniversary', + 'lovin', + 'amour', + 'aimer', + 'amor', + 'valentines day', + 'enamour', + 'lovey', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely' + ], + modifiable: true), + Emoji( + name: 'sign of the horns', + char: '\u{1F918}', + shortName: 'metal', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersPartial, + keywords: [ + 'finger', + 'hand', + 'horns', + 'rock-on', + 'uc8', + 'diversity', + 'body', + 'hands', + 'hi', + 'boys night', + 'rock and roll', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'guys night' + ]), + Emoji( + name: 'sign of the horns: light skin tone', + char: '\u{1F918}\u{1F3FB}', + shortName: 'metal_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersPartial, + keywords: [ + 'finger', + 'hand', + 'horns', + 'light skin tone', + 'rock-on', + 'uc8', + 'diversity', + 'body', + 'hands', + 'hi', + 'boys night', + 'rock and roll', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'guys night' + ], + modifiable: true), + Emoji( + name: 'sign of the horns: medium-light skin tone', + char: '\u{1F918}\u{1F3FC}', + shortName: 'metal_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersPartial, + keywords: [ + 'finger', + 'hand', + 'horns', + 'medium-light skin tone', + 'rock-on', + 'uc8', + 'diversity', + 'body', + 'hands', + 'hi', + 'boys night', + 'rock and roll', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'guys night' + ], + modifiable: true), + Emoji( + name: 'sign of the horns: medium skin tone', + char: '\u{1F918}\u{1F3FD}', + shortName: 'metal_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersPartial, + keywords: [ + 'finger', + 'hand', + 'horns', + 'medium skin tone', + 'rock-on', + 'uc8', + 'diversity', + 'body', + 'hands', + 'hi', + 'boys night', + 'rock and roll', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'guys night' + ], + modifiable: true), + Emoji( + name: 'sign of the horns: medium-dark skin tone', + char: '\u{1F918}\u{1F3FE}', + shortName: 'metal_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersPartial, + keywords: [ + 'finger', + 'hand', + 'horns', + 'medium-dark skin tone', + 'rock-on', + 'uc8', + 'diversity', + 'body', + 'hands', + 'hi', + 'boys night', + 'rock and roll', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'guys night' + ], + modifiable: true), + Emoji( + name: 'sign of the horns: dark skin tone', + char: '\u{1F918}\u{1F3FF}', + shortName: 'metal_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersPartial, + keywords: [ + 'dark skin tone', + 'finger', + 'hand', + 'horns', + 'rock-on', + 'uc8', + 'diversity', + 'body', + 'hands', + 'hi', + 'boys night', + 'rock and roll', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'guys night' + ], + modifiable: true), + Emoji( + name: 'OK hand', + char: '\u{1F44C}', + shortName: 'ok_hand', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersPartial, + keywords: [ + 'OK', + 'hand', + 'uc6', + 'diversity', + 'happy', + 'butt', + 'body', + 'hands', + 'hi', + 'sex', + 'thank you', + 'perfect', + 'awesome', + 'good', + 'beautiful', + 'google', + 'correct', + 'porn', + 'fun', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'hooray', + 'cheek', + 'cheeky', + 'excited', + 'feliz', + 'heureux', + 'cheerful', + 'delighted', + 'ecstatic', + 'elated', + 'glad', + 'joy', + 'merry', + 'ass', + 'booty', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'fuck', + 'fucking', + 'horny', + 'humping', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'perfecto', + 'perfection', + 'superb', + 'flawless', + 'excellent', + 'supreme', + 'super', + 'great', + 'okay', + 'got it', + 'cool', + 'ok', + 'will do', + 'like', + 'bien', + 'yep', + 'yup', + 'good job', + 'nice', + 'well done', + 'bravo', + 'congratulations', + 'congrats', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'passing grade' + ]), + Emoji( + name: 'OK hand: light skin tone', + char: '\u{1F44C}\u{1F3FB}', + shortName: 'ok_hand_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersPartial, + keywords: [ + 'OK', + 'hand', + 'light skin tone', + 'uc8', + 'diversity', + 'happy', + 'butt', + 'body', + 'hands', + 'hi', + 'sex', + 'thank you', + 'perfect', + 'awesome', + 'good', + 'beautiful', + 'google', + 'correct', + 'porn', + 'fun', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'hooray', + 'cheek', + 'cheeky', + 'excited', + 'feliz', + 'heureux', + 'cheerful', + 'delighted', + 'ecstatic', + 'elated', + 'glad', + 'joy', + 'merry', + 'ass', + 'booty', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'fuck', + 'fucking', + 'horny', + 'humping', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'perfecto', + 'perfection', + 'superb', + 'flawless', + 'excellent', + 'supreme', + 'super', + 'great', + 'okay', + 'got it', + 'cool', + 'ok', + 'will do', + 'like', + 'bien', + 'yep', + 'yup', + 'good job', + 'nice', + 'well done', + 'bravo', + 'congratulations', + 'congrats', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'passing grade' + ], + modifiable: true), + Emoji( + name: 'OK hand: medium-light skin tone', + char: '\u{1F44C}\u{1F3FC}', + shortName: 'ok_hand_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersPartial, + keywords: [ + 'OK', + 'hand', + 'medium-light skin tone', + 'uc8', + 'diversity', + 'happy', + 'butt', + 'body', + 'hands', + 'hi', + 'sex', + 'thank you', + 'perfect', + 'awesome', + 'good', + 'beautiful', + 'google', + 'correct', + 'porn', + 'fun', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'hooray', + 'cheek', + 'cheeky', + 'excited', + 'feliz', + 'heureux', + 'cheerful', + 'delighted', + 'ecstatic', + 'elated', + 'glad', + 'joy', + 'merry', + 'ass', + 'booty', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'fuck', + 'fucking', + 'horny', + 'humping', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'perfecto', + 'perfection', + 'superb', + 'flawless', + 'excellent', + 'supreme', + 'super', + 'great', + 'okay', + 'got it', + 'cool', + 'ok', + 'will do', + 'like', + 'bien', + 'yep', + 'yup', + 'good job', + 'nice', + 'well done', + 'bravo', + 'congratulations', + 'congrats', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'passing grade' + ], + modifiable: true), + Emoji( + name: 'OK hand: medium skin tone', + char: '\u{1F44C}\u{1F3FD}', + shortName: 'ok_hand_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersPartial, + keywords: [ + 'OK', + 'hand', + 'medium skin tone', + 'uc8', + 'diversity', + 'happy', + 'butt', + 'body', + 'hands', + 'hi', + 'sex', + 'thank you', + 'perfect', + 'awesome', + 'good', + 'beautiful', + 'google', + 'correct', + 'porn', + 'fun', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'hooray', + 'cheek', + 'cheeky', + 'excited', + 'feliz', + 'heureux', + 'cheerful', + 'delighted', + 'ecstatic', + 'elated', + 'glad', + 'joy', + 'merry', + 'ass', + 'booty', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'fuck', + 'fucking', + 'horny', + 'humping', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'perfecto', + 'perfection', + 'superb', + 'flawless', + 'excellent', + 'supreme', + 'super', + 'great', + 'okay', + 'got it', + 'cool', + 'ok', + 'will do', + 'like', + 'bien', + 'yep', + 'yup', + 'good job', + 'nice', + 'well done', + 'bravo', + 'congratulations', + 'congrats', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'passing grade' + ], + modifiable: true), + Emoji( + name: 'OK hand: medium-dark skin tone', + char: '\u{1F44C}\u{1F3FE}', + shortName: 'ok_hand_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersPartial, + keywords: [ + 'OK', + 'hand', + 'medium-dark skin tone', + 'uc8', + 'diversity', + 'happy', + 'butt', + 'body', + 'hands', + 'hi', + 'sex', + 'thank you', + 'perfect', + 'awesome', + 'good', + 'beautiful', + 'google', + 'correct', + 'porn', + 'fun', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'hooray', + 'cheek', + 'cheeky', + 'excited', + 'feliz', + 'heureux', + 'cheerful', + 'delighted', + 'ecstatic', + 'elated', + 'glad', + 'joy', + 'merry', + 'ass', + 'booty', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'fuck', + 'fucking', + 'horny', + 'humping', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'perfecto', + 'perfection', + 'superb', + 'flawless', + 'excellent', + 'supreme', + 'super', + 'great', + 'okay', + 'got it', + 'cool', + 'ok', + 'will do', + 'like', + 'bien', + 'yep', + 'yup', + 'good job', + 'nice', + 'well done', + 'bravo', + 'congratulations', + 'congrats', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'passing grade' + ], + modifiable: true), + Emoji( + name: 'OK hand: dark skin tone', + char: '\u{1F44C}\u{1F3FF}', + shortName: 'ok_hand_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersPartial, + keywords: [ + 'OK', + 'dark skin tone', + 'hand', + 'uc8', + 'diversity', + 'happy', + 'butt', + 'body', + 'hands', + 'hi', + 'sex', + 'thank you', + 'perfect', + 'awesome', + 'good', + 'beautiful', + 'google', + 'correct', + 'porn', + 'fun', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'hooray', + 'cheek', + 'cheeky', + 'excited', + 'feliz', + 'heureux', + 'cheerful', + 'delighted', + 'ecstatic', + 'elated', + 'glad', + 'joy', + 'merry', + 'ass', + 'booty', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'fuck', + 'fucking', + 'horny', + 'humping', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'perfecto', + 'perfection', + 'superb', + 'flawless', + 'excellent', + 'supreme', + 'super', + 'great', + 'okay', + 'got it', + 'cool', + 'ok', + 'will do', + 'like', + 'bien', + 'yep', + 'yup', + 'good job', + 'nice', + 'well done', + 'bravo', + 'congratulations', + 'congrats', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'passing grade' + ], + modifiable: true), + Emoji( + name: 'pinching hand', + char: '\u{1F90F}', + shortName: 'pinching_hand', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersPartial, + keywords: [ + 'uc12', + 'diversity', + 'penis', + 'body', + 'hands', + 'donald trump', + 'half', + 'quiet', + 'tiny', + 'greed', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'dick', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'trump', + 'shut up', + 'hushed', + 'silence', + 'silent', + 'shush', + 'shh', + 'petite bite', + 'small dick', + 'small', + 'selfish' + ]), + Emoji( + name: 'pinching hand: light skin tone', + char: '\u{1F90F}\u{1F3FB}', + shortName: 'pinching_hand_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersPartial, + keywords: [ + 'uc12', + 'diversity', + 'penis', + 'body', + 'hands', + 'donald trump', + 'half', + 'quiet', + 'tiny', + 'greed', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'dick', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'trump', + 'shut up', + 'hushed', + 'silence', + 'silent', + 'shush', + 'shh', + 'petite bite', + 'small dick', + 'small', + 'selfish' + ], + modifiable: true), + Emoji( + name: 'pinching hand: medium-light skin tone', + char: '\u{1F90F}\u{1F3FC}', + shortName: 'pinching_hand_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersPartial, + keywords: [ + 'uc12', + 'diversity', + 'penis', + 'body', + 'hands', + 'donald trump', + 'half', + 'quiet', + 'tiny', + 'greed', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'dick', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'trump', + 'shut up', + 'hushed', + 'silence', + 'silent', + 'shush', + 'shh', + 'petite bite', + 'small dick', + 'small', + 'selfish' + ], + modifiable: true), + Emoji( + name: 'pinching hand: medium skin tone', + char: '\u{1F90F}\u{1F3FD}', + shortName: 'pinching_hand_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersPartial, + keywords: [ + 'uc12', + 'diversity', + 'penis', + 'body', + 'hands', + 'donald trump', + 'half', + 'quiet', + 'tiny', + 'greed', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'dick', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'trump', + 'shut up', + 'hushed', + 'silence', + 'silent', + 'shush', + 'shh', + 'petite bite', + 'small dick', + 'small', + 'selfish' + ], + modifiable: true), + Emoji( + name: 'pinching hand: medium-dark skin tone', + char: '\u{1F90F}\u{1F3FE}', + shortName: 'pinching_hand_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersPartial, + keywords: [ + 'uc12', + 'diversity', + 'penis', + 'body', + 'hands', + 'donald trump', + 'half', + 'quiet', + 'tiny', + 'greed', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'dick', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'trump', + 'shut up', + 'hushed', + 'silence', + 'silent', + 'shush', + 'shh', + 'petite bite', + 'small dick', + 'small', + 'selfish' + ], + modifiable: true), + Emoji( + name: 'pinching hand: dark skin tone', + char: '\u{1F90F}\u{1F3FF}', + shortName: 'pinching_hand_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersPartial, + keywords: [ + 'uc12', + 'diversity', + 'penis', + 'body', + 'hands', + 'donald trump', + 'half', + 'quiet', + 'tiny', + 'greed', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'dick', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'trump', + 'shut up', + 'hushed', + 'silence', + 'silent', + 'shush', + 'shh', + 'petite bite', + 'small dick', + 'small', + 'selfish' + ], + modifiable: true), + Emoji( + name: 'pinched fingers', + char: '\u{1F90C}', + shortName: 'pinched_fingers', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersPartial, + keywords: [ + 'uc13', + 'diversity', + 'italian', + 'body', + 'hands', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'italy', + 'italie', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers' + ]), + Emoji( + name: 'pinched fingers: medium-light skin tone', + char: '\u{1F90C}\u{1F3FC}', + shortName: 'pinched_fingers_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersPartial, + keywords: [ + 'uc13', + 'diversity', + 'italian', + 'body', + 'hands', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'italy', + 'italie', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers' + ], + modifiable: true), + Emoji( + name: 'pinched fingers: light skin tone', + char: '\u{1F90C}\u{1F3FB}', + shortName: 'pinched_fingers_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersPartial, + keywords: [ + 'uc13', + 'diversity', + 'italian', + 'body', + 'hands', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'italy', + 'italie', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers' + ], + modifiable: true), + Emoji( + name: 'pinched fingers: medium skin tone', + char: '\u{1F90C}\u{1F3FD}', + shortName: 'pinched_fingers_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersPartial, + keywords: [ + 'uc13', + 'diversity', + 'italian', + 'body', + 'hands', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'italy', + 'italie', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers' + ], + modifiable: true), + Emoji( + name: 'pinched fingers: medium-dark skin tone', + char: '\u{1F90C}\u{1F3FE}', + shortName: 'pinched_fingers_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersPartial, + keywords: [ + 'uc13', + 'diversity', + 'italian', + 'body', + 'hands', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'italy', + 'italie', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers' + ], + modifiable: true), + Emoji( + name: 'pinched fingers: dark skin tone', + char: '\u{1F90C}\u{1F3FF}', + shortName: 'pinched_fingers_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersPartial, + keywords: [ + 'uc13', + 'diversity', + 'italian', + 'body', + 'hands', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'italy', + 'italie', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers' + ], + modifiable: true), + Emoji( + name: 'backhand index pointing left', + char: '\u{1F448}', + shortName: 'point_left', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handSingleFinger, + keywords: [ + 'backhand', + 'finger', + 'hand', + 'index', + 'point', + 'uc6', + 'diversity', + 'body', + 'hands', + 'hi', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle' + ]), + Emoji( + name: 'backhand index pointing left: light skin tone', + char: '\u{1F448}\u{1F3FB}', + shortName: 'point_left_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handSingleFinger, + keywords: [ + 'backhand', + 'finger', + 'hand', + 'index', + 'light skin tone', + 'point', + 'uc8', + 'diversity', + 'body', + 'hands', + 'hi', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle' + ], + modifiable: true), + Emoji( + name: 'backhand index pointing left: medium-light skin tone', + char: '\u{1F448}\u{1F3FC}', + shortName: 'point_left_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handSingleFinger, + keywords: [ + 'backhand', + 'finger', + 'hand', + 'index', + 'medium-light skin tone', + 'point', + 'uc8', + 'diversity', + 'body', + 'hands', + 'hi', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle' + ], + modifiable: true), + Emoji( + name: 'backhand index pointing left: medium skin tone', + char: '\u{1F448}\u{1F3FD}', + shortName: 'point_left_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handSingleFinger, + keywords: [ + 'backhand', + 'finger', + 'hand', + 'index', + 'medium skin tone', + 'point', + 'uc8', + 'diversity', + 'body', + 'hands', + 'hi', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle' + ], + modifiable: true), + Emoji( + name: 'backhand index pointing left: medium-dark skin tone', + char: '\u{1F448}\u{1F3FE}', + shortName: 'point_left_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handSingleFinger, + keywords: [ + 'backhand', + 'finger', + 'hand', + 'index', + 'medium-dark skin tone', + 'point', + 'uc8', + 'diversity', + 'body', + 'hands', + 'hi', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle' + ], + modifiable: true), + Emoji( + name: 'backhand index pointing left: dark skin tone', + char: '\u{1F448}\u{1F3FF}', + shortName: 'point_left_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handSingleFinger, + keywords: [ + 'backhand', + 'dark skin tone', + 'finger', + 'hand', + 'index', + 'point', + 'uc8', + 'diversity', + 'body', + 'hands', + 'hi', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle' + ], + modifiable: true), + Emoji( + name: 'backhand index pointing right', + char: '\u{1F449}', + shortName: 'point_right', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handSingleFinger, + keywords: [ + 'backhand', + 'finger', + 'hand', + 'index', + 'point', + 'uc6', + 'diversity', + 'body', + 'hands', + 'hi', + 'sex', + 'download', + 'porn', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'fuck', + 'fucking', + 'horny', + 'humping' + ]), + Emoji( + name: 'backhand index pointing right: light skin tone', + char: '\u{1F449}\u{1F3FB}', + shortName: 'point_right_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handSingleFinger, + keywords: [ + 'backhand', + 'finger', + 'hand', + 'index', + 'light skin tone', + 'point', + 'uc8', + 'diversity', + 'body', + 'hands', + 'hi', + 'sex', + 'download', + 'porn', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'fuck', + 'fucking', + 'horny', + 'humping' + ], + modifiable: true), + Emoji( + name: 'backhand index pointing right: medium-light skin tone', + char: '\u{1F449}\u{1F3FC}', + shortName: 'point_right_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handSingleFinger, + keywords: [ + 'backhand', + 'finger', + 'hand', + 'index', + 'medium-light skin tone', + 'point', + 'uc8', + 'diversity', + 'body', + 'hands', + 'hi', + 'sex', + 'download', + 'porn', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'fuck', + 'fucking', + 'horny', + 'humping' + ], + modifiable: true), + Emoji( + name: 'backhand index pointing right: medium skin tone', + char: '\u{1F449}\u{1F3FD}', + shortName: 'point_right_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handSingleFinger, + keywords: [ + 'backhand', + 'finger', + 'hand', + 'index', + 'medium skin tone', + 'point', + 'uc8', + 'diversity', + 'body', + 'hands', + 'hi', + 'sex', + 'download', + 'porn', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'fuck', + 'fucking', + 'horny', + 'humping' + ], + modifiable: true), + Emoji( + name: 'backhand index pointing right: medium-dark skin tone', + char: '\u{1F449}\u{1F3FE}', + shortName: 'point_right_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handSingleFinger, + keywords: [ + 'backhand', + 'finger', + 'hand', + 'index', + 'medium-dark skin tone', + 'point', + 'uc8', + 'diversity', + 'body', + 'hands', + 'hi', + 'sex', + 'download', + 'porn', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'fuck', + 'fucking', + 'horny', + 'humping' + ], + modifiable: true), + Emoji( + name: 'backhand index pointing right: dark skin tone', + char: '\u{1F449}\u{1F3FF}', + shortName: 'point_right_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handSingleFinger, + keywords: [ + 'backhand', + 'dark skin tone', + 'finger', + 'hand', + 'index', + 'point', + 'uc8', + 'diversity', + 'body', + 'hands', + 'hi', + 'sex', + 'download', + 'porn', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'fuck', + 'fucking', + 'horny', + 'humping' + ], + modifiable: true), + Emoji( + name: 'backhand index pointing up', + char: '\u{1F446}', + shortName: 'point_up_2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handSingleFinger, + keywords: [ + 'backhand', + 'finger', + 'hand', + 'index', + 'point', + 'up', + 'uc6', + 'diversity', + 'body', + 'hands', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers' + ]), + Emoji( + name: 'backhand index pointing up: light skin tone', + char: '\u{1F446}\u{1F3FB}', + shortName: 'point_up_2_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handSingleFinger, + keywords: [ + 'backhand', + 'finger', + 'hand', + 'index', + 'light skin tone', + 'point', + 'up', + 'uc8', + 'diversity', + 'body', + 'hands', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers' + ], + modifiable: true), + Emoji( + name: 'backhand index pointing up: medium-light skin tone', + char: '\u{1F446}\u{1F3FC}', + shortName: 'point_up_2_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handSingleFinger, + keywords: [ + 'backhand', + 'finger', + 'hand', + 'index', + 'medium-light skin tone', + 'point', + 'up', + 'uc8', + 'diversity', + 'body', + 'hands', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers' + ], + modifiable: true), + Emoji( + name: 'backhand index pointing up: medium skin tone', + char: '\u{1F446}\u{1F3FD}', + shortName: 'point_up_2_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handSingleFinger, + keywords: [ + 'backhand', + 'finger', + 'hand', + 'index', + 'medium skin tone', + 'point', + 'up', + 'uc8', + 'diversity', + 'body', + 'hands', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers' + ], + modifiable: true), + Emoji( + name: 'backhand index pointing up: medium-dark skin tone', + char: '\u{1F446}\u{1F3FE}', + shortName: 'point_up_2_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handSingleFinger, + keywords: [ + 'backhand', + 'finger', + 'hand', + 'index', + 'medium-dark skin tone', + 'point', + 'up', + 'uc8', + 'diversity', + 'body', + 'hands', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers' + ], + modifiable: true), + Emoji( + name: 'backhand index pointing up: dark skin tone', + char: '\u{1F446}\u{1F3FF}', + shortName: 'point_up_2_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handSingleFinger, + keywords: [ + 'backhand', + 'dark skin tone', + 'finger', + 'hand', + 'index', + 'point', + 'up', + 'uc8', + 'diversity', + 'body', + 'hands', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers' + ], + modifiable: true), + Emoji( + name: 'backhand index pointing down', + char: '\u{1F447}', + shortName: 'point_down', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handSingleFinger, + keywords: [ + 'backhand', + 'down', + 'finger', + 'hand', + 'index', + 'point', + 'uc6', + 'diversity', + 'body', + 'hands', + 'click', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers' + ]), + Emoji( + name: 'backhand index pointing down: light skin tone', + char: '\u{1F447}\u{1F3FB}', + shortName: 'point_down_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handSingleFinger, + keywords: [ + 'backhand', + 'down', + 'finger', + 'hand', + 'index', + 'light skin tone', + 'point', + 'uc8', + 'diversity', + 'body', + 'hands', + 'click', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers' + ], + modifiable: true), + Emoji( + name: 'backhand index pointing down: medium-light skin tone', + char: '\u{1F447}\u{1F3FC}', + shortName: 'point_down_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handSingleFinger, + keywords: [ + 'backhand', + 'down', + 'finger', + 'hand', + 'index', + 'medium-light skin tone', + 'point', + 'uc8', + 'diversity', + 'body', + 'hands', + 'click', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers' + ], + modifiable: true), + Emoji( + name: 'backhand index pointing down: medium skin tone', + char: '\u{1F447}\u{1F3FD}', + shortName: 'point_down_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handSingleFinger, + keywords: [ + 'backhand', + 'down', + 'finger', + 'hand', + 'index', + 'medium skin tone', + 'point', + 'uc8', + 'diversity', + 'body', + 'hands', + 'click', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers' + ], + modifiable: true), + Emoji( + name: 'backhand index pointing down: medium-dark skin tone', + char: '\u{1F447}\u{1F3FE}', + shortName: 'point_down_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handSingleFinger, + keywords: [ + 'backhand', + 'down', + 'finger', + 'hand', + 'index', + 'medium-dark skin tone', + 'point', + 'uc8', + 'diversity', + 'body', + 'hands', + 'click', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers' + ], + modifiable: true), + Emoji( + name: 'backhand index pointing down: dark skin tone', + char: '\u{1F447}\u{1F3FF}', + shortName: 'point_down_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handSingleFinger, + keywords: [ + 'backhand', + 'dark skin tone', + 'down', + 'finger', + 'hand', + 'index', + 'point', + 'uc8', + 'diversity', + 'body', + 'hands', + 'click', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers' + ], + modifiable: true), + Emoji( + name: 'index pointing up', + char: '\u{261D}\u{FE0F}', + shortName: 'point_up', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handSingleFinger, + keywords: [ + 'finger', + 'hand', + 'index', + 'point', + 'up', + 'uc1', + 'diversity', + 'body', + 'hands', + 'emojione', + 'porn', + 'important', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'emoji one' + ]), + Emoji( + name: 'index pointing up: light skin tone', + char: '\u{261D}\u{1F3FB}', + shortName: 'point_up_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handSingleFinger, + keywords: [ + 'finger', + 'hand', + 'index', + 'light skin tone', + 'point', + 'up', + 'uc8', + 'diversity', + 'body', + 'hands', + 'emojione', + 'porn', + 'important', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'emoji one' + ], + modifiable: true), + Emoji( + name: 'index pointing up: medium-light skin tone', + char: '\u{261D}\u{1F3FC}', + shortName: 'point_up_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handSingleFinger, + keywords: [ + 'finger', + 'hand', + 'index', + 'medium-light skin tone', + 'point', + 'up', + 'uc8', + 'diversity', + 'body', + 'hands', + 'emojione', + 'porn', + 'important', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'emoji one' + ], + modifiable: true), + Emoji( + name: 'index pointing up: medium skin tone', + char: '\u{261D}\u{1F3FD}', + shortName: 'point_up_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handSingleFinger, + keywords: [ + 'finger', + 'hand', + 'index', + 'medium skin tone', + 'point', + 'up', + 'uc8', + 'diversity', + 'body', + 'hands', + 'emojione', + 'porn', + 'important', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'emoji one' + ], + modifiable: true), + Emoji( + name: 'index pointing up: medium-dark skin tone', + char: '\u{261D}\u{1F3FE}', + shortName: 'point_up_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handSingleFinger, + keywords: [ + 'finger', + 'hand', + 'index', + 'medium-dark skin tone', + 'point', + 'up', + 'uc8', + 'diversity', + 'body', + 'hands', + 'emojione', + 'porn', + 'important', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'emoji one' + ], + modifiable: true), + Emoji( + name: 'index pointing up: dark skin tone', + char: '\u{261D}\u{1F3FF}', + shortName: 'point_up_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handSingleFinger, + keywords: [ + 'dark skin tone', + 'finger', + 'hand', + 'index', + 'point', + 'up', + 'uc8', + 'diversity', + 'body', + 'hands', + 'emojione', + 'porn', + 'important', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'emoji one' + ], + modifiable: true), + Emoji( + name: 'raised hand', + char: '\u{270B}', + shortName: 'raised_hand', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersOpen, + keywords: [ + 'hand', + 'uc6', + 'diversity', + 'body', + 'hands', + 'hi', + 'girls night', + 'high five', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'ladies night', + 'girls only', + 'girlfriend' + ]), + Emoji( + name: 'raised hand: light skin tone', + char: '\u{270B}\u{1F3FB}', + shortName: 'raised_hand_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersOpen, + keywords: [ + 'hand', + 'light skin tone', + 'uc8', + 'diversity', + 'body', + 'hands', + 'hi', + 'girls night', + 'high five', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'ladies night', + 'girls only', + 'girlfriend' + ], + modifiable: true), + Emoji( + name: 'raised hand: medium-light skin tone', + char: '\u{270B}\u{1F3FC}', + shortName: 'raised_hand_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersOpen, + keywords: [ + 'hand', + 'medium-light skin tone', + 'uc8', + 'diversity', + 'body', + 'hands', + 'hi', + 'girls night', + 'high five', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'ladies night', + 'girls only', + 'girlfriend' + ], + modifiable: true), + Emoji( + name: 'raised hand: medium skin tone', + char: '\u{270B}\u{1F3FD}', + shortName: 'raised_hand_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersOpen, + keywords: [ + 'hand', + 'medium skin tone', + 'uc8', + 'diversity', + 'body', + 'hands', + 'hi', + 'girls night', + 'high five', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'ladies night', + 'girls only', + 'girlfriend' + ], + modifiable: true), + Emoji( + name: 'raised hand: medium-dark skin tone', + char: '\u{270B}\u{1F3FE}', + shortName: 'raised_hand_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersOpen, + keywords: [ + 'hand', + 'medium-dark skin tone', + 'uc8', + 'diversity', + 'body', + 'hands', + 'hi', + 'girls night', + 'high five', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'ladies night', + 'girls only', + 'girlfriend' + ], + modifiable: true), + Emoji( + name: 'raised hand: dark skin tone', + char: '\u{270B}\u{1F3FF}', + shortName: 'raised_hand_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersOpen, + keywords: [ + 'dark skin tone', + 'hand', + 'uc8', + 'diversity', + 'body', + 'hands', + 'hi', + 'girls night', + 'high five', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'ladies night', + 'girls only', + 'girlfriend' + ], + modifiable: true), + Emoji( + name: 'raised back of hand', + char: '\u{1F91A}', + shortName: 'raised_back_of_hand', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersOpen, + keywords: [ + 'backhand', + 'raised', + 'uc9', + 'diversity', + 'body', + 'hands', + 'award', + 'hi', + 'hate', + 'private', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'awards', + 'prize', + 'prizes', + 'trophy', + 'trophies', + 'spot', + 'best', + 'champion', + 'hero', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'i hate', + 'disgust', + 'stump', + 'shout', + 'dislike', + 'rude', + 'annoy', + 'grinch', + 'gross', + 'grumpy', + 'mean', + 'problem', + 'suck', + 'jerk', + 'asshole', + 'no', + 'прив', + 'privé', + 'privado', + 'reserved' + ]), + Emoji( + name: 'raised back of hand: light skin tone', + char: '\u{1F91A}\u{1F3FB}', + shortName: 'raised_back_of_hand_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersOpen, + keywords: [ + 'backhand', + 'light skin tone', + 'raised', + 'uc9', + 'diversity', + 'body', + 'hands', + 'award', + 'hi', + 'hate', + 'private', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'awards', + 'prize', + 'prizes', + 'trophy', + 'trophies', + 'spot', + 'best', + 'champion', + 'hero', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'i hate', + 'disgust', + 'stump', + 'shout', + 'dislike', + 'rude', + 'annoy', + 'grinch', + 'gross', + 'grumpy', + 'mean', + 'problem', + 'suck', + 'jerk', + 'asshole', + 'no', + 'прив', + 'privé', + 'privado', + 'reserved' + ], + modifiable: true), + Emoji( + name: 'raised back of hand: medium-light skin tone', + char: '\u{1F91A}\u{1F3FC}', + shortName: 'raised_back_of_hand_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersOpen, + keywords: [ + 'backhand', + 'medium-light skin tone', + 'raised', + 'uc9', + 'diversity', + 'body', + 'hands', + 'award', + 'hi', + 'hate', + 'private', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'awards', + 'prize', + 'prizes', + 'trophy', + 'trophies', + 'spot', + 'best', + 'champion', + 'hero', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'i hate', + 'disgust', + 'stump', + 'shout', + 'dislike', + 'rude', + 'annoy', + 'grinch', + 'gross', + 'grumpy', + 'mean', + 'problem', + 'suck', + 'jerk', + 'asshole', + 'no', + 'прив', + 'privé', + 'privado', + 'reserved' + ], + modifiable: true), + Emoji( + name: 'raised back of hand: medium skin tone', + char: '\u{1F91A}\u{1F3FD}', + shortName: 'raised_back_of_hand_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersOpen, + keywords: [ + 'backhand', + 'medium skin tone', + 'raised', + 'uc9', + 'diversity', + 'body', + 'hands', + 'award', + 'hi', + 'hate', + 'private', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'awards', + 'prize', + 'prizes', + 'trophy', + 'trophies', + 'spot', + 'best', + 'champion', + 'hero', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'i hate', + 'disgust', + 'stump', + 'shout', + 'dislike', + 'rude', + 'annoy', + 'grinch', + 'gross', + 'grumpy', + 'mean', + 'problem', + 'suck', + 'jerk', + 'asshole', + 'no', + 'прив', + 'privé', + 'privado', + 'reserved' + ], + modifiable: true), + Emoji( + name: 'raised back of hand: medium-dark skin tone', + char: '\u{1F91A}\u{1F3FE}', + shortName: 'raised_back_of_hand_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersOpen, + keywords: [ + 'backhand', + 'medium-dark skin tone', + 'raised', + 'uc9', + 'diversity', + 'body', + 'hands', + 'award', + 'hi', + 'hate', + 'private', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'awards', + 'prize', + 'prizes', + 'trophy', + 'trophies', + 'spot', + 'best', + 'champion', + 'hero', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'i hate', + 'disgust', + 'stump', + 'shout', + 'dislike', + 'rude', + 'annoy', + 'grinch', + 'gross', + 'grumpy', + 'mean', + 'problem', + 'suck', + 'jerk', + 'asshole', + 'no', + 'прив', + 'privé', + 'privado', + 'reserved' + ], + modifiable: true), + Emoji( + name: 'raised back of hand: dark skin tone', + char: '\u{1F91A}\u{1F3FF}', + shortName: 'raised_back_of_hand_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersOpen, + keywords: [ + 'backhand', + 'dark skin tone', + 'raised', + 'uc9', + 'diversity', + 'body', + 'hands', + 'award', + 'hi', + 'hate', + 'private', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'awards', + 'prize', + 'prizes', + 'trophy', + 'trophies', + 'spot', + 'best', + 'champion', + 'hero', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'i hate', + 'disgust', + 'stump', + 'shout', + 'dislike', + 'rude', + 'annoy', + 'grinch', + 'gross', + 'grumpy', + 'mean', + 'problem', + 'suck', + 'jerk', + 'asshole', + 'no', + 'прив', + 'privé', + 'privado', + 'reserved' + ], + modifiable: true), + Emoji( + name: 'hand with fingers splayed', + char: '\u{1F590}', + shortName: 'hand_splayed', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersOpen, + keywords: [ + 'finger', + 'hand', + 'splayed', + 'uc7', + 'diversity', + 'body', + 'hands', + 'hi', + 'gay pride', + 'high five', + 'proud', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle' + ]), + Emoji( + name: 'hand with fingers splayed: light skin tone', + char: '\u{1F590}\u{1F3FB}', + shortName: 'hand_splayed_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersOpen, + keywords: [ + 'finger', + 'hand', + 'light skin tone', + 'splayed', + 'uc8', + 'diversity', + 'body', + 'hands', + 'hi', + 'gay pride', + 'high five', + 'proud', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle' + ], + modifiable: true), + Emoji( + name: 'hand with fingers splayed: medium-light skin tone', + char: '\u{1F590}\u{1F3FC}', + shortName: 'hand_splayed_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersOpen, + keywords: [ + 'finger', + 'hand', + 'medium-light skin tone', + 'splayed', + 'uc8', + 'diversity', + 'body', + 'hands', + 'hi', + 'gay pride', + 'high five', + 'proud', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle' + ], + modifiable: true), + Emoji( + name: 'hand with fingers splayed: medium skin tone', + char: '\u{1F590}\u{1F3FD}', + shortName: 'hand_splayed_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersOpen, + keywords: [ + 'finger', + 'hand', + 'medium skin tone', + 'splayed', + 'uc8', + 'diversity', + 'body', + 'hands', + 'hi', + 'gay pride', + 'high five', + 'proud', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle' + ], + modifiable: true), + Emoji( + name: 'hand with fingers splayed: medium-dark skin tone', + char: '\u{1F590}\u{1F3FE}', + shortName: 'hand_splayed_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersOpen, + keywords: [ + 'finger', + 'hand', + 'medium-dark skin tone', + 'splayed', + 'uc8', + 'diversity', + 'body', + 'hands', + 'hi', + 'gay pride', + 'high five', + 'proud', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle' + ], + modifiable: true), + Emoji( + name: 'hand with fingers splayed: dark skin tone', + char: '\u{1F590}\u{1F3FF}', + shortName: 'hand_splayed_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersOpen, + keywords: [ + 'dark skin tone', + 'finger', + 'hand', + 'splayed', + 'uc8', + 'diversity', + 'body', + 'hands', + 'hi', + 'gay pride', + 'high five', + 'proud', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle' + ], + modifiable: true), + Emoji( + name: 'vulcan salute', + char: '\u{1F596}', + shortName: 'vulcan', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersOpen, + keywords: [ + 'finger', + 'hand', + 'spock', + 'vulcan', + 'uc7', + 'diversity', + 'body', + 'hands', + 'hi', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle' + ]), + Emoji( + name: 'vulcan salute: light skin tone', + char: '\u{1F596}\u{1F3FB}', + shortName: 'vulcan_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersOpen, + keywords: [ + 'finger', + 'hand', + 'light skin tone', + 'spock', + 'vulcan', + 'uc8', + 'diversity', + 'body', + 'hands', + 'hi', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle' + ], + modifiable: true), + Emoji( + name: 'vulcan salute: medium-light skin tone', + char: '\u{1F596}\u{1F3FC}', + shortName: 'vulcan_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersOpen, + keywords: [ + 'finger', + 'hand', + 'medium-light skin tone', + 'spock', + 'vulcan', + 'uc8', + 'diversity', + 'body', + 'hands', + 'hi', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle' + ], + modifiable: true), + Emoji( + name: 'vulcan salute: medium skin tone', + char: '\u{1F596}\u{1F3FD}', + shortName: 'vulcan_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersOpen, + keywords: [ + 'finger', + 'hand', + 'medium skin tone', + 'spock', + 'vulcan', + 'uc8', + 'diversity', + 'body', + 'hands', + 'hi', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle' + ], + modifiable: true), + Emoji( + name: 'vulcan salute: medium-dark skin tone', + char: '\u{1F596}\u{1F3FE}', + shortName: 'vulcan_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersOpen, + keywords: [ + 'finger', + 'hand', + 'medium-dark skin tone', + 'spock', + 'vulcan', + 'uc8', + 'diversity', + 'body', + 'hands', + 'hi', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle' + ], + modifiable: true), + Emoji( + name: 'vulcan salute: dark skin tone', + char: '\u{1F596}\u{1F3FF}', + shortName: 'vulcan_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersOpen, + keywords: [ + 'dark skin tone', + 'finger', + 'hand', + 'spock', + 'vulcan', + 'uc8', + 'diversity', + 'body', + 'hands', + 'hi', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle' + ], + modifiable: true), + Emoji( + name: 'waving hand', + char: '\u{1F44B}', + shortName: 'wave', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersOpen, + keywords: [ + 'hand', + 'wave', + 'waving', + 'uc6', + 'diversity', + 'body', + 'hands', + 'hi', + 'hola', + 'friend', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo' + ]), + Emoji( + name: 'waving hand: light skin tone', + char: '\u{1F44B}\u{1F3FB}', + shortName: 'wave_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersOpen, + keywords: [ + 'hand', + 'light skin tone', + 'wave', + 'waving', + 'uc8', + 'diversity', + 'body', + 'hands', + 'hi', + 'hola', + 'friend', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo' + ], + modifiable: true), + Emoji( + name: 'waving hand: medium-light skin tone', + char: '\u{1F44B}\u{1F3FC}', + shortName: 'wave_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersOpen, + keywords: [ + 'hand', + 'medium-light skin tone', + 'wave', + 'waving', + 'uc8', + 'diversity', + 'body', + 'hands', + 'hi', + 'hola', + 'friend', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo' + ], + modifiable: true), + Emoji( + name: 'waving hand: medium skin tone', + char: '\u{1F44B}\u{1F3FD}', + shortName: 'wave_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersOpen, + keywords: [ + 'hand', + 'medium skin tone', + 'wave', + 'waving', + 'uc8', + 'diversity', + 'body', + 'hands', + 'hi', + 'hola', + 'friend', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo' + ], + modifiable: true), + Emoji( + name: 'waving hand: medium-dark skin tone', + char: '\u{1F44B}\u{1F3FE}', + shortName: 'wave_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersOpen, + keywords: [ + 'hand', + 'medium-dark skin tone', + 'wave', + 'waving', + 'uc8', + 'diversity', + 'body', + 'hands', + 'hi', + 'hola', + 'friend', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo' + ], + modifiable: true), + Emoji( + name: 'waving hand: dark skin tone', + char: '\u{1F44B}\u{1F3FF}', + shortName: 'wave_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersOpen, + keywords: [ + 'dark skin tone', + 'hand', + 'wave', + 'waving', + 'uc8', + 'diversity', + 'body', + 'hands', + 'hi', + 'hola', + 'friend', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo' + ], + modifiable: true), + Emoji( + name: 'call me hand', + char: '\u{1F919}', + shortName: 'call_me', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersPartial, + keywords: [ + 'call', + 'hand', + 'uc9', + 'diversity', + 'body', + 'hands', + 'hi', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle' + ]), + Emoji( + name: 'call me hand: light skin tone', + char: '\u{1F919}\u{1F3FB}', + shortName: 'call_me_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersPartial, + keywords: [ + 'call', + 'hand', + 'light skin tone', + 'uc9', + 'diversity', + 'body', + 'hands', + 'hi', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle' + ], + modifiable: true), + Emoji( + name: 'call me hand: medium-light skin tone', + char: '\u{1F919}\u{1F3FC}', + shortName: 'call_me_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersPartial, + keywords: [ + 'call', + 'hand', + 'medium-light skin tone', + 'uc9', + 'diversity', + 'body', + 'hands', + 'hi', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle' + ], + modifiable: true), + Emoji( + name: 'call me hand: medium skin tone', + char: '\u{1F919}\u{1F3FD}', + shortName: 'call_me_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersPartial, + keywords: [ + 'call', + 'hand', + 'medium skin tone', + 'uc9', + 'diversity', + 'body', + 'hands', + 'hi', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle' + ], + modifiable: true), + Emoji( + name: 'call me hand: medium-dark skin tone', + char: '\u{1F919}\u{1F3FE}', + shortName: 'call_me_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersPartial, + keywords: [ + 'call', + 'hand', + 'medium-dark skin tone', + 'uc9', + 'diversity', + 'body', + 'hands', + 'hi', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle' + ], + modifiable: true), + Emoji( + name: 'call me hand: dark skin tone', + char: '\u{1F919}\u{1F3FF}', + shortName: 'call_me_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handFingersPartial, + keywords: [ + 'call', + 'dark skin tone', + 'hand', + 'uc9', + 'diversity', + 'body', + 'hands', + 'hi', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle' + ], + modifiable: true), + Emoji( + name: 'flexed biceps', + char: '\u{1F4AA}', + shortName: 'muscle', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.bodyParts, + keywords: [ + 'biceps', + 'comic', + 'flex', + 'muscle', + 'uc6', + 'sport', + 'diversity', + 'body', + 'hands', + 'flex', + 'weight lifting', + 'win', + 'feminist', + 'boys night', + 'power', + 'handsome', + 'festivus', + 'protest', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'strong', + 'weight lifter', + 'winning', + 'killing it', + 'crushing it', + 'victory', + 'victorious', + 'success', + 'successful', + 'winner', + 'feminism', + 'strong woman', + 'guys night', + 'stud', + 'blm', + 'demonstration' + ]), + Emoji( + name: 'flexed biceps: light skin tone', + char: '\u{1F4AA}\u{1F3FB}', + shortName: 'muscle_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.bodyParts, + keywords: [ + 'biceps', + 'comic', + 'flex', + 'light skin tone', + 'muscle', + 'uc8', + 'sport', + 'diversity', + 'body', + 'hands', + 'flex', + 'weight lifting', + 'win', + 'feminist', + 'boys night', + 'power', + 'handsome', + 'festivus', + 'protest', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'strong', + 'weight lifter', + 'winning', + 'killing it', + 'crushing it', + 'victory', + 'victorious', + 'success', + 'successful', + 'winner', + 'feminism', + 'strong woman', + 'guys night', + 'stud', + 'blm', + 'demonstration' + ], + modifiable: true), + Emoji( + name: 'flexed biceps: medium-light skin tone', + char: '\u{1F4AA}\u{1F3FC}', + shortName: 'muscle_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.bodyParts, + keywords: [ + 'biceps', + 'comic', + 'flex', + 'medium-light skin tone', + 'muscle', + 'uc8', + 'sport', + 'diversity', + 'body', + 'hands', + 'flex', + 'weight lifting', + 'win', + 'feminist', + 'boys night', + 'power', + 'handsome', + 'festivus', + 'protest', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'strong', + 'weight lifter', + 'winning', + 'killing it', + 'crushing it', + 'victory', + 'victorious', + 'success', + 'successful', + 'winner', + 'feminism', + 'strong woman', + 'guys night', + 'stud', + 'blm', + 'demonstration' + ], + modifiable: true), + Emoji( + name: 'flexed biceps: medium skin tone', + char: '\u{1F4AA}\u{1F3FD}', + shortName: 'muscle_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.bodyParts, + keywords: [ + 'biceps', + 'comic', + 'flex', + 'medium skin tone', + 'muscle', + 'uc8', + 'sport', + 'diversity', + 'body', + 'hands', + 'flex', + 'weight lifting', + 'win', + 'feminist', + 'boys night', + 'power', + 'handsome', + 'festivus', + 'protest', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'strong', + 'weight lifter', + 'winning', + 'killing it', + 'crushing it', + 'victory', + 'victorious', + 'success', + 'successful', + 'winner', + 'feminism', + 'strong woman', + 'guys night', + 'stud', + 'blm', + 'demonstration' + ], + modifiable: true), + Emoji( + name: 'flexed biceps: medium-dark skin tone', + char: '\u{1F4AA}\u{1F3FE}', + shortName: 'muscle_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.bodyParts, + keywords: [ + 'biceps', + 'comic', + 'flex', + 'medium-dark skin tone', + 'muscle', + 'uc8', + 'sport', + 'diversity', + 'body', + 'hands', + 'flex', + 'weight lifting', + 'win', + 'feminist', + 'boys night', + 'power', + 'handsome', + 'festivus', + 'protest', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'strong', + 'weight lifter', + 'winning', + 'killing it', + 'crushing it', + 'victory', + 'victorious', + 'success', + 'successful', + 'winner', + 'feminism', + 'strong woman', + 'guys night', + 'stud', + 'blm', + 'demonstration' + ], + modifiable: true), + Emoji( + name: 'flexed biceps: dark skin tone', + char: '\u{1F4AA}\u{1F3FF}', + shortName: 'muscle_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.bodyParts, + keywords: [ + 'biceps', + 'comic', + 'dark skin tone', + 'flex', + 'muscle', + 'uc8', + 'sport', + 'diversity', + 'body', + 'hands', + 'flex', + 'weight lifting', + 'win', + 'feminist', + 'boys night', + 'power', + 'handsome', + 'festivus', + 'protest', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'strong', + 'weight lifter', + 'winning', + 'killing it', + 'crushing it', + 'victory', + 'victorious', + 'success', + 'successful', + 'winner', + 'feminism', + 'strong woman', + 'guys night', + 'stud', + 'blm', + 'demonstration' + ], + modifiable: true), + Emoji( + name: 'mechanical arm', + char: '\u{1F9BE}', + shortName: 'mechanical_arm', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.bodyParts, + keywords: [ + 'uc12', + 'body', + 'science', + 'handicap', + 'prosthetic', + 'fake arm', + 'accessibility', + 'body part', + 'anatomy', + 'lab', + 'disabled', + 'disability', + 'prosthetics', + 'robotic arm' + ]), + Emoji( + name: 'middle finger', + char: '\u{1F595}', + shortName: 'middle_finger', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handSingleFinger, + keywords: [ + 'finger', + 'hand', + 'uc7', + 'diversity', + 'penis', + 'body', + 'hands', + 'angry', + 'middle finger', + 'sex', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'dick', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad', + 'flipping off', + 'fu', + 'the finger', + 'fuck you', + 'fuck off', + 'fuck', + 'fucking', + 'horny', + 'humping' + ]), + Emoji( + name: 'middle finger: light skin tone', + char: '\u{1F595}\u{1F3FB}', + shortName: 'middle_finger_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handSingleFinger, + keywords: [ + 'finger', + 'hand', + 'light skin tone', + 'uc8', + 'diversity', + 'penis', + 'body', + 'hands', + 'angry', + 'middle finger', + 'sex', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'dick', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad', + 'flipping off', + 'fu', + 'the finger', + 'fuck you', + 'fuck off', + 'fuck', + 'fucking', + 'horny', + 'humping' + ], + modifiable: true), + Emoji( + name: 'middle finger: medium-light skin tone', + char: '\u{1F595}\u{1F3FC}', + shortName: 'middle_finger_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handSingleFinger, + keywords: [ + 'finger', + 'hand', + 'medium-light skin tone', + 'uc8', + 'diversity', + 'penis', + 'body', + 'hands', + 'angry', + 'middle finger', + 'sex', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'dick', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad', + 'flipping off', + 'fu', + 'the finger', + 'fuck you', + 'fuck off', + 'fuck', + 'fucking', + 'horny', + 'humping' + ], + modifiable: true), + Emoji( + name: 'middle finger: medium skin tone', + char: '\u{1F595}\u{1F3FD}', + shortName: 'middle_finger_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handSingleFinger, + keywords: [ + 'finger', + 'hand', + 'medium skin tone', + 'uc8', + 'diversity', + 'penis', + 'body', + 'hands', + 'angry', + 'middle finger', + 'sex', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'dick', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad', + 'flipping off', + 'fu', + 'the finger', + 'fuck you', + 'fuck off', + 'fuck', + 'fucking', + 'horny', + 'humping' + ], + modifiable: true), + Emoji( + name: 'middle finger: medium-dark skin tone', + char: '\u{1F595}\u{1F3FE}', + shortName: 'middle_finger_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handSingleFinger, + keywords: [ + 'finger', + 'hand', + 'medium-dark skin tone', + 'uc8', + 'diversity', + 'penis', + 'body', + 'hands', + 'angry', + 'middle finger', + 'sex', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'dick', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad', + 'flipping off', + 'fu', + 'the finger', + 'fuck you', + 'fuck off', + 'fuck', + 'fucking', + 'horny', + 'humping' + ], + modifiable: true), + Emoji( + name: 'middle finger: dark skin tone', + char: '\u{1F595}\u{1F3FF}', + shortName: 'middle_finger_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handSingleFinger, + keywords: [ + 'dark skin tone', + 'finger', + 'hand', + 'uc8', + 'diversity', + 'penis', + 'body', + 'hands', + 'angry', + 'middle finger', + 'sex', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'dick', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad', + 'flipping off', + 'fu', + 'the finger', + 'fuck you', + 'fuck off', + 'fuck', + 'fucking', + 'horny', + 'humping' + ], + modifiable: true), + Emoji( + name: 'writing hand', + char: '\u{270D}\u{FE0F}', + shortName: 'writing_hand', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handProp, + keywords: [ + 'hand', + 'write', + 'uc1', + 'diversity', + 'body', + 'hands', + 'write', + 'color', + 'correct', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'writing', + 'colour', + 'coloring', + 'colouring', + 'drawing', + 'marker', + 'sketch', + 'passing grade' + ]), + Emoji( + name: 'writing hand: light skin tone', + char: '\u{270D}\u{1F3FB}', + shortName: 'writing_hand_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handProp, + keywords: [ + 'hand', + 'light skin tone', + 'write', + 'uc8', + 'diversity', + 'body', + 'hands', + 'write', + 'color', + 'correct', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'writing', + 'colour', + 'coloring', + 'colouring', + 'drawing', + 'marker', + 'sketch', + 'passing grade' + ], + modifiable: true), + Emoji( + name: 'writing hand: medium-light skin tone', + char: '\u{270D}\u{1F3FC}', + shortName: 'writing_hand_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handProp, + keywords: [ + 'hand', + 'medium-light skin tone', + 'write', + 'uc8', + 'diversity', + 'body', + 'hands', + 'write', + 'color', + 'correct', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'writing', + 'colour', + 'coloring', + 'colouring', + 'drawing', + 'marker', + 'sketch', + 'passing grade' + ], + modifiable: true), + Emoji( + name: 'writing hand: medium skin tone', + char: '\u{270D}\u{1F3FD}', + shortName: 'writing_hand_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handProp, + keywords: [ + 'hand', + 'medium skin tone', + 'write', + 'uc8', + 'diversity', + 'body', + 'hands', + 'write', + 'color', + 'correct', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'writing', + 'colour', + 'coloring', + 'colouring', + 'drawing', + 'marker', + 'sketch', + 'passing grade' + ], + modifiable: true), + Emoji( + name: 'writing hand: medium-dark skin tone', + char: '\u{270D}\u{1F3FE}', + shortName: 'writing_hand_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handProp, + keywords: [ + 'hand', + 'medium-dark skin tone', + 'write', + 'uc8', + 'diversity', + 'body', + 'hands', + 'write', + 'color', + 'correct', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'writing', + 'colour', + 'coloring', + 'colouring', + 'drawing', + 'marker', + 'sketch', + 'passing grade' + ], + modifiable: true), + Emoji( + name: 'writing hand: dark skin tone', + char: '\u{270D}\u{1F3FF}', + shortName: 'writing_hand_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handProp, + keywords: [ + 'dark skin tone', + 'hand', + 'write', + 'uc8', + 'diversity', + 'body', + 'hands', + 'write', + 'color', + 'correct', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'writing', + 'colour', + 'coloring', + 'colouring', + 'drawing', + 'marker', + 'sketch', + 'passing grade' + ], + modifiable: true), + Emoji( + name: 'folded hands', + char: '\u{1F64F}', + shortName: 'pray', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.hands, + keywords: [ + 'ask', + 'bow', + 'folded', + 'gesture', + 'hand', + 'please', + 'pray', + 'thanks', + 'uc6', + 'diversity', + 'body', + 'hands', + 'hi', + 'luck', + 'thank you', + 'pray', + 'scientology', + 'jesus', + 'pleased', + 'yoga', + 'easter', + 'begging', + 'help', + 'hope', + 'soul', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'good luck', + 'lucky', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'prayer', + 'praying', + 'prayers', + 'grateful', + 'sorry', + 'heaven', + 'bless', + 'faith', + 'holy', + 'spirit', + 'hopeful', + 'blessed', + 'preach', + 'offering', + 'scientologist', + 'please', + 'chill', + 'confident', + 'content', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'swear', + 'promise' + ]), + Emoji( + name: 'folded hands: light skin tone', + char: '\u{1F64F}\u{1F3FB}', + shortName: 'pray_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.hands, + keywords: [ + 'ask', + 'bow', + 'folded', + 'gesture', + 'hand', + 'light skin tone', + 'please', + 'pray', + 'thanks', + 'uc8', + 'diversity', + 'body', + 'hands', + 'hi', + 'luck', + 'thank you', + 'pray', + 'scientology', + 'jesus', + 'pleased', + 'yoga', + 'easter', + 'begging', + 'help', + 'hope', + 'soul', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'good luck', + 'lucky', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'prayer', + 'praying', + 'prayers', + 'grateful', + 'sorry', + 'heaven', + 'bless', + 'faith', + 'holy', + 'spirit', + 'hopeful', + 'blessed', + 'preach', + 'offering', + 'scientologist', + 'please', + 'chill', + 'confident', + 'content', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'swear', + 'promise' + ], + modifiable: true), + Emoji( + name: 'folded hands: medium-light skin tone', + char: '\u{1F64F}\u{1F3FC}', + shortName: 'pray_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.hands, + keywords: [ + 'ask', + 'bow', + 'folded', + 'gesture', + 'hand', + 'medium-light skin tone', + 'please', + 'pray', + 'thanks', + 'uc8', + 'diversity', + 'body', + 'hands', + 'hi', + 'luck', + 'thank you', + 'pray', + 'scientology', + 'jesus', + 'pleased', + 'yoga', + 'easter', + 'begging', + 'help', + 'hope', + 'soul', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'good luck', + 'lucky', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'prayer', + 'praying', + 'prayers', + 'grateful', + 'sorry', + 'heaven', + 'bless', + 'faith', + 'holy', + 'spirit', + 'hopeful', + 'blessed', + 'preach', + 'offering', + 'scientologist', + 'please', + 'chill', + 'confident', + 'content', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'swear', + 'promise' + ], + modifiable: true), + Emoji( + name: 'folded hands: medium skin tone', + char: '\u{1F64F}\u{1F3FD}', + shortName: 'pray_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.hands, + keywords: [ + 'ask', + 'bow', + 'folded', + 'gesture', + 'hand', + 'medium skin tone', + 'please', + 'pray', + 'thanks', + 'uc8', + 'diversity', + 'body', + 'hands', + 'hi', + 'luck', + 'thank you', + 'pray', + 'scientology', + 'jesus', + 'pleased', + 'yoga', + 'easter', + 'begging', + 'help', + 'hope', + 'soul', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'good luck', + 'lucky', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'prayer', + 'praying', + 'prayers', + 'grateful', + 'sorry', + 'heaven', + 'bless', + 'faith', + 'holy', + 'spirit', + 'hopeful', + 'blessed', + 'preach', + 'offering', + 'scientologist', + 'please', + 'chill', + 'confident', + 'content', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'swear', + 'promise' + ], + modifiable: true), + Emoji( + name: 'folded hands: medium-dark skin tone', + char: '\u{1F64F}\u{1F3FE}', + shortName: 'pray_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.hands, + keywords: [ + 'ask', + 'bow', + 'folded', + 'gesture', + 'hand', + 'medium-dark skin tone', + 'please', + 'pray', + 'thanks', + 'uc8', + 'diversity', + 'body', + 'hands', + 'hi', + 'luck', + 'thank you', + 'pray', + 'scientology', + 'jesus', + 'pleased', + 'yoga', + 'easter', + 'begging', + 'help', + 'hope', + 'soul', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'good luck', + 'lucky', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'prayer', + 'praying', + 'prayers', + 'grateful', + 'sorry', + 'heaven', + 'bless', + 'faith', + 'holy', + 'spirit', + 'hopeful', + 'blessed', + 'preach', + 'offering', + 'scientologist', + 'please', + 'chill', + 'confident', + 'content', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'swear', + 'promise' + ], + modifiable: true), + Emoji( + name: 'folded hands: dark skin tone', + char: '\u{1F64F}\u{1F3FF}', + shortName: 'pray_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.hands, + keywords: [ + 'ask', + 'bow', + 'dark skin tone', + 'folded', + 'gesture', + 'hand', + 'please', + 'pray', + 'thanks', + 'uc8', + 'diversity', + 'body', + 'hands', + 'hi', + 'luck', + 'thank you', + 'pray', + 'scientology', + 'jesus', + 'pleased', + 'yoga', + 'easter', + 'begging', + 'help', + 'hope', + 'soul', + 'language', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'good luck', + 'lucky', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'prayer', + 'praying', + 'prayers', + 'grateful', + 'sorry', + 'heaven', + 'bless', + 'faith', + 'holy', + 'spirit', + 'hopeful', + 'blessed', + 'preach', + 'offering', + 'scientologist', + 'please', + 'chill', + 'confident', + 'content', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'swear', + 'promise' + ], + modifiable: true), + Emoji( + name: 'foot', + char: '\u{1F9B6}', + shortName: 'foot', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.bodyParts, + keywords: [ + 'uc11', + 'diversity', + 'body', + 'feet', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'toes', + 'heel', + 'ankle' + ]), + Emoji( + name: 'foot: light skin tone', + char: '\u{1F9B6}\u{1F3FB}', + shortName: 'foot_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.bodyParts, + keywords: [ + 'uc11', + 'diversity', + 'body', + 'feet', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'toes', + 'heel', + 'ankle' + ], + modifiable: true), + Emoji( + name: 'foot: medium-light skin tone', + char: '\u{1F9B6}\u{1F3FC}', + shortName: 'foot_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.bodyParts, + keywords: [ + 'uc11', + 'diversity', + 'body', + 'feet', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'toes', + 'heel', + 'ankle' + ], + modifiable: true), + Emoji( + name: 'foot: medium skin tone', + char: '\u{1F9B6}\u{1F3FD}', + shortName: 'foot_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.bodyParts, + keywords: [ + 'uc11', + 'diversity', + 'body', + 'feet', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'toes', + 'heel', + 'ankle' + ], + modifiable: true), + Emoji( + name: 'foot: medium-dark skin tone', + char: '\u{1F9B6}\u{1F3FE}', + shortName: 'foot_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.bodyParts, + keywords: [ + 'uc11', + 'diversity', + 'body', + 'feet', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'toes', + 'heel', + 'ankle' + ], + modifiable: true), + Emoji( + name: 'foot: dark skin tone', + char: '\u{1F9B6}\u{1F3FF}', + shortName: 'foot_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.bodyParts, + keywords: [ + 'uc11', + 'diversity', + 'body', + 'feet', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'toes', + 'heel', + 'ankle' + ], + modifiable: true), + Emoji( + name: 'leg', + char: '\u{1F9B5}', + shortName: 'leg', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.bodyParts, + keywords: [ + 'uc11', + 'diversity', + 'body', + 'feet', + 'knee', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'toes', + 'heel', + 'ankle', + 'thigh', + 'calf' + ]), + Emoji( + name: 'leg: light skin tone', + char: '\u{1F9B5}\u{1F3FB}', + shortName: 'leg_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.bodyParts, + keywords: [ + 'uc11', + 'diversity', + 'body', + 'feet', + 'knee', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'toes', + 'heel', + 'ankle', + 'thigh', + 'calf' + ], + modifiable: true), + Emoji( + name: 'leg: medium-light skin tone', + char: '\u{1F9B5}\u{1F3FC}', + shortName: 'leg_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.bodyParts, + keywords: [ + 'uc11', + 'diversity', + 'body', + 'feet', + 'knee', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'toes', + 'heel', + 'ankle', + 'thigh', + 'calf' + ], + modifiable: true), + Emoji( + name: 'leg: medium skin tone', + char: '\u{1F9B5}\u{1F3FD}', + shortName: 'leg_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.bodyParts, + keywords: [ + 'uc11', + 'diversity', + 'body', + 'feet', + 'knee', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'toes', + 'heel', + 'ankle', + 'thigh', + 'calf' + ], + modifiable: true), + Emoji( + name: 'leg: medium-dark skin tone', + char: '\u{1F9B5}\u{1F3FE}', + shortName: 'leg_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.bodyParts, + keywords: [ + 'uc11', + 'diversity', + 'body', + 'feet', + 'knee', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'toes', + 'heel', + 'ankle', + 'thigh', + 'calf' + ], + modifiable: true), + Emoji( + name: 'leg: dark skin tone', + char: '\u{1F9B5}\u{1F3FF}', + shortName: 'leg_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.bodyParts, + keywords: [ + 'uc11', + 'diversity', + 'body', + 'feet', + 'knee', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'toes', + 'heel', + 'ankle', + 'thigh', + 'calf' + ], + modifiable: true), + Emoji( + name: 'mechanical leg', + char: '\u{1F9BF}', + shortName: 'mechanical_leg', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.bodyParts, + keywords: [ + 'uc12', + 'body', + 'science', + 'handicap', + 'feet', + 'knee', + 'prosthetic', + 'fake leg', + 'accessibility', + 'medical', + 'body part', + 'anatomy', + 'lab', + 'disabled', + 'disability', + 'toes', + 'heel', + 'ankle', + 'thigh', + 'calf', + 'prosthetics', + 'robotic leg' + ]), + Emoji( + name: 'lipstick', + char: '\u{1F484}', + shortName: 'lipstick', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.clothing, + keywords: [ + 'cosmetics', + 'makeup', + 'uc6', + 'fashion', + 'women', + 'love', + 'sexy', + 'lipstick', + 'beautiful', + 'girls night', + 'color', + 'mirror', + 'clothes', + 'clothing', + 'style', + 'woman', + 'female', + 'i love you', + 'te amo', + "je t'aime", + 'anniversary', + 'lovin', + 'amour', + 'aimer', + 'amor', + 'valentines day', + 'enamour', + 'lovey', + 'mouth', + 'mouths', + 'makeup', + 'lip', + 'lips', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'ladies night', + 'girls only', + 'girlfriend', + 'colour', + 'coloring', + 'colouring', + 'drawing', + 'marker', + 'sketch' + ]), + Emoji( + name: 'kiss mark', + char: '\u{1F48B}', + shortName: 'kiss', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.emotion, + keywords: [ + 'kiss', + 'lips', + 'uc6', + 'women', + 'love', + 'sexy', + 'lipstick', + 'beautiful', + 'girls night', + 'pink', + 'kisses', + 'mirror', + 'porn', + 'woman', + 'female', + 'i love you', + 'te amo', + "je t'aime", + 'anniversary', + 'lovin', + 'amour', + 'aimer', + 'amor', + 'valentines day', + 'enamour', + 'lovey', + 'mouth', + 'mouths', + 'makeup', + 'lip', + 'lips', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'ladies night', + 'girls only', + 'girlfriend', + 'rose', + 'bisous', + 'beijos', + 'besos', + 'bise', + 'blowing kisses', + 'kissy' + ]), + Emoji( + name: 'mouth', + char: '\u{1F444}', + shortName: 'lips', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.bodyParts, + keywords: [ + 'lips', + 'uc6', + 'women', + 'body', + 'sexy', + 'lipstick', + 'beautiful', + 'porn', + 'woman', + 'female', + 'body part', + 'anatomy', + 'mouth', + 'mouths', + 'makeup', + 'lip', + 'lips', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely' + ]), + Emoji( + name: 'tooth', + char: '\u{1F9B7}', + shortName: 'tooth', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.bodyParts, + keywords: [ + 'uc11', + 'body', + 'teeth', + 'bite', + 'bones', + 'medical', + 'body part', + 'anatomy', + 'dentist', + 'Os', + 'hueso' + ]), + Emoji( + name: 'bone', + char: '\u{1F9B4}', + shortName: 'bone', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.bodyParts, + keywords: [ + 'uc11', + 'body', + 'science', + 'mystery', + 'bones', + 'medical', + 'body part', + 'anatomy', + 'lab', + 'Os', + 'hueso' + ]), + Emoji( + name: 'tongue', + char: '\u{1F445}', + shortName: 'tongue', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.bodyParts, + keywords: [ + 'body', + 'uc6', + 'body', + 'sexy', + 'sex', + 'lipstick', + 'pussy', + 'pink', + 'lick', + 'porn', + 'tongue', + 'medical', + 'body part', + 'anatomy', + 'fuck', + 'fucking', + 'horny', + 'humping', + 'mouth', + 'mouths', + 'makeup', + 'lip', + 'lips', + 'condom', + 'rose', + 'toung', + 'tounge' + ]), + Emoji( + name: 'ear', + char: '\u{1F442}', + shortName: 'ear', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.bodyParts, + keywords: [ + 'body', + 'uc6', + 'diversity', + 'body', + 'sound', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'volume', + 'speaker', + 'loud', + 'mic', + 'audio', + 'hear' + ]), + Emoji( + name: 'ear: light skin tone', + char: '\u{1F442}\u{1F3FB}', + shortName: 'ear_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.bodyParts, + keywords: [ + 'body', + 'light skin tone', + 'uc8', + 'diversity', + 'body', + 'sound', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'volume', + 'speaker', + 'loud', + 'mic', + 'audio', + 'hear' + ], + modifiable: true), + Emoji( + name: 'ear: medium-light skin tone', + char: '\u{1F442}\u{1F3FC}', + shortName: 'ear_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.bodyParts, + keywords: [ + 'body', + 'medium-light skin tone', + 'uc8', + 'diversity', + 'body', + 'sound', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'volume', + 'speaker', + 'loud', + 'mic', + 'audio', + 'hear' + ], + modifiable: true), + Emoji( + name: 'ear: medium skin tone', + char: '\u{1F442}\u{1F3FD}', + shortName: 'ear_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.bodyParts, + keywords: [ + 'body', + 'medium skin tone', + 'uc8', + 'diversity', + 'body', + 'sound', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'volume', + 'speaker', + 'loud', + 'mic', + 'audio', + 'hear' + ], + modifiable: true), + Emoji( + name: 'ear: medium-dark skin tone', + char: '\u{1F442}\u{1F3FE}', + shortName: 'ear_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.bodyParts, + keywords: [ + 'body', + 'medium-dark skin tone', + 'uc8', + 'diversity', + 'body', + 'sound', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'volume', + 'speaker', + 'loud', + 'mic', + 'audio', + 'hear' + ], + modifiable: true), + Emoji( + name: 'ear: dark skin tone', + char: '\u{1F442}\u{1F3FF}', + shortName: 'ear_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.bodyParts, + keywords: [ + 'body', + 'dark skin tone', + 'uc8', + 'diversity', + 'body', + 'sound', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'volume', + 'speaker', + 'loud', + 'mic', + 'audio', + 'hear' + ], + modifiable: true), + Emoji( + name: 'ear with hearing aid', + char: '\u{1F9BB}', + shortName: 'ear_with_hearing_aid', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.bodyParts, + keywords: [ + 'uc12', + 'old people', + 'diversity', + 'earphone', + 'sound', + 'deaf', + 'accessibility', + 'medical', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'earbud', + 'volume', + 'speaker', + 'loud', + 'mic', + 'audio', + 'hear', + 'hard of hearing' + ]), + Emoji( + name: 'ear with hearing aid: light skin tone', + char: '\u{1F9BB}\u{1F3FB}', + shortName: 'ear_with_hearing_aid_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.bodyParts, + keywords: [ + 'uc12', + 'old people', + 'diversity', + 'earphone', + 'sound', + 'deaf', + 'accessibility', + 'medical', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'earbud', + 'volume', + 'speaker', + 'loud', + 'mic', + 'audio', + 'hear', + 'hard of hearing' + ], + modifiable: true), + Emoji( + name: 'ear with hearing aid: medium-light skin tone', + char: '\u{1F9BB}\u{1F3FC}', + shortName: 'ear_with_hearing_aid_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.bodyParts, + keywords: [ + 'uc12', + 'old people', + 'diversity', + 'earphone', + 'sound', + 'deaf', + 'accessibility', + 'medical', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'earbud', + 'volume', + 'speaker', + 'loud', + 'mic', + 'audio', + 'hear', + 'hard of hearing' + ], + modifiable: true), + Emoji( + name: 'ear with hearing aid: medium skin tone', + char: '\u{1F9BB}\u{1F3FD}', + shortName: 'ear_with_hearing_aid_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.bodyParts, + keywords: [ + 'uc12', + 'old people', + 'diversity', + 'earphone', + 'sound', + 'deaf', + 'accessibility', + 'medical', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'earbud', + 'volume', + 'speaker', + 'loud', + 'mic', + 'audio', + 'hear', + 'hard of hearing' + ], + modifiable: true), + Emoji( + name: 'ear with hearing aid: medium-dark skin tone', + char: '\u{1F9BB}\u{1F3FE}', + shortName: 'ear_with_hearing_aid_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.bodyParts, + keywords: [ + 'uc12', + 'old people', + 'diversity', + 'earphone', + 'sound', + 'deaf', + 'accessibility', + 'medical', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'earbud', + 'volume', + 'speaker', + 'loud', + 'mic', + 'audio', + 'hear', + 'hard of hearing' + ], + modifiable: true), + Emoji( + name: 'ear with hearing aid: dark skin tone', + char: '\u{1F9BB}\u{1F3FF}', + shortName: 'ear_with_hearing_aid_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.bodyParts, + keywords: [ + 'uc12', + 'old people', + 'diversity', + 'earphone', + 'sound', + 'deaf', + 'accessibility', + 'medical', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'earbud', + 'volume', + 'speaker', + 'loud', + 'mic', + 'audio', + 'hear', + 'hard of hearing' + ], + modifiable: true), + Emoji( + name: 'nose', + char: '\u{1F443}', + shortName: 'nose', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.bodyParts, + keywords: [ + 'body', + 'uc6', + 'diversity', + 'body', + 'stinky', + 'booger', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'smell', + 'stink', + 'odor' + ]), + Emoji( + name: 'nose: light skin tone', + char: '\u{1F443}\u{1F3FB}', + shortName: 'nose_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.bodyParts, + keywords: [ + 'body', + 'light skin tone', + 'uc8', + 'diversity', + 'body', + 'stinky', + 'booger', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'smell', + 'stink', + 'odor' + ], + modifiable: true), + Emoji( + name: 'nose: medium-light skin tone', + char: '\u{1F443}\u{1F3FC}', + shortName: 'nose_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.bodyParts, + keywords: [ + 'body', + 'medium-light skin tone', + 'uc8', + 'diversity', + 'body', + 'stinky', + 'booger', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'smell', + 'stink', + 'odor' + ], + modifiable: true), + Emoji( + name: 'nose: medium skin tone', + char: '\u{1F443}\u{1F3FD}', + shortName: 'nose_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.bodyParts, + keywords: [ + 'body', + 'medium skin tone', + 'uc8', + 'diversity', + 'body', + 'stinky', + 'booger', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'smell', + 'stink', + 'odor' + ], + modifiable: true), + Emoji( + name: 'nose: medium-dark skin tone', + char: '\u{1F443}\u{1F3FE}', + shortName: 'nose_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.bodyParts, + keywords: [ + 'body', + 'medium-dark skin tone', + 'uc8', + 'diversity', + 'body', + 'stinky', + 'booger', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'smell', + 'stink', + 'odor' + ], + modifiable: true), + Emoji( + name: 'nose: dark skin tone', + char: '\u{1F443}\u{1F3FF}', + shortName: 'nose_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.bodyParts, + keywords: [ + 'body', + 'dark skin tone', + 'uc8', + 'diversity', + 'body', + 'stinky', + 'booger', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'smell', + 'stink', + 'odor' + ], + modifiable: true), + Emoji( + name: 'footprints', + char: '\u{1F463}', + shortName: 'footprints', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSymbol, + keywords: [ + 'clothing', + 'footprint', + 'print', + 'uc6', + 'baby', + 'paws', + 'feet', + 'kid', + 'babies', + 'infant', + 'infants', + 'crying kid', + 'bebe', + 'little', + 'petite', + 'bambino', + 'toes', + 'heel', + 'ankle' + ]), + Emoji( + name: 'eye', + char: '\u{1F441}\u{FE0F}', + shortName: 'eye', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.bodyParts, + keywords: [ + 'body', + 'uc7', + 'body', + 'eyes', + 'search', + 'medical', + 'body part', + 'anatomy', + 'eye', + 'eyebrow', + 'look', + 'find', + 'looking', + 'see' + ]), + Emoji( + name: 'eyes', + char: '\u{1F440}', + shortName: 'eyes', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.bodyParts, + keywords: [ + 'eye', + 'face', + 'uc6', + 'halloween', + 'body', + 'rolling eyes', + 'eyes', + 'google', + 'brain', + 'disney', + 'search', + 'eyeroll', + 'porn', + 'samhain', + 'body part', + 'anatomy', + 'eye roll', + 'side eye', + 'eye', + 'eyebrow', + 'mind', + 'memory', + 'thought', + 'conscience', + 'cartoon', + 'look', + 'find', + 'looking', + 'see' + ]), + Emoji( + name: 'brain', + char: '\u{1F9E0}', + shortName: 'brain', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.bodyParts, + keywords: [ + 'intelligent', + 'uc10', + 'halloween', + 'body', + 'science', + 'nerd', + 'brain', + 'medical', + 'samhain', + 'body part', + 'anatomy', + 'lab', + 'smart', + 'geek', + 'serious', + 'mind', + 'memory', + 'thought', + 'conscience' + ]), + Emoji( + name: 'anatomical heart', + char: '\u{1FAC0}', + shortName: 'anatomical_heart', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.bodyParts, + keywords: [ + 'uc13', + 'body', + 'science', + 'heart', + 'covid', + 'medical', + 'body part', + 'anatomy', + 'lab', + 'hearts', + 'serce', + 'corazón', + 'coração', + 'coeur' + ]), + Emoji( + name: 'lungs', + char: '\u{1FAC1}', + shortName: 'lungs', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.bodyParts, + keywords: [ + 'uc13', + 'body', + 'smoking', + 'science', + 'breathe', + 'covid', + 'medical', + 'body part', + 'anatomy', + 'smoke', + 'cigarette', + 'puff', + 'lab', + 'sigh', + 'inhale' + ]), + Emoji( + name: 'speaking head', + char: '\u{1F5E3}\u{FE0F}', + shortName: 'speaking_head', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSymbol, + keywords: [ + 'face', + 'head', + 'silhouette', + 'speak', + 'speaking', + 'uc7', + 'talk', + 'sound', + 'language', + 'talking', + 'speech', + 'social', + 'chat', + 'voice', + 'speechless', + 'speak', + 'volume', + 'speaker', + 'loud', + 'mic', + 'audio', + 'hear' + ]), + Emoji( + name: 'bust in silhouette', + char: '\u{1F464}', + shortName: 'bust_in_silhouette', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSymbol, + keywords: [ + 'bust', + 'silhouette', + 'uc6', + 'facebook', + 'fame', + 'famous', + 'celebrity' + ]), + Emoji( + name: 'busts in silhouette', + char: '\u{1F465}', + shortName: 'busts_in_silhouette', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSymbol, + keywords: ['bust', 'silhouette', 'uc6', 'magnet', 'facebook', 'network']), + Emoji( + name: 'people hugging', + char: '\u{1FAC2}', + shortName: 'people_hugging', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSymbol, + keywords: ['uc13', 'hug', 'embrace', 'hugs']), + Emoji( + name: 'baby', + char: '\u{1F476}', + shortName: 'baby', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'baby', + 'young', + 'uc6', + 'diversity', + 'baby', + 'christmas', + 'human', + 'child', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'kid', + 'babies', + 'infant', + 'infants', + 'crying kid', + 'bebe', + 'little', + 'petite', + 'bambino', + 'navidad', + 'xmas', + 'noel', + 'merry christmas', + 'gender', + 'people', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant' + ]), + Emoji( + name: 'baby: light skin tone', + char: '\u{1F476}\u{1F3FB}', + shortName: 'baby_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'baby', + 'light skin tone', + 'young', + 'uc8', + 'diversity', + 'baby', + 'christmas', + 'human', + 'child', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'kid', + 'babies', + 'infant', + 'infants', + 'crying kid', + 'bebe', + 'little', + 'petite', + 'bambino', + 'navidad', + 'xmas', + 'noel', + 'merry christmas', + 'gender', + 'people', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant' + ], + modifiable: true), + Emoji( + name: 'baby: medium-light skin tone', + char: '\u{1F476}\u{1F3FC}', + shortName: 'baby_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'baby', + 'medium-light skin tone', + 'young', + 'uc8', + 'diversity', + 'baby', + 'christmas', + 'human', + 'child', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'kid', + 'babies', + 'infant', + 'infants', + 'crying kid', + 'bebe', + 'little', + 'petite', + 'bambino', + 'navidad', + 'xmas', + 'noel', + 'merry christmas', + 'gender', + 'people', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant' + ], + modifiable: true), + Emoji( + name: 'baby: medium skin tone', + char: '\u{1F476}\u{1F3FD}', + shortName: 'baby_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'baby', + 'medium skin tone', + 'young', + 'uc8', + 'diversity', + 'baby', + 'christmas', + 'human', + 'child', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'kid', + 'babies', + 'infant', + 'infants', + 'crying kid', + 'bebe', + 'little', + 'petite', + 'bambino', + 'navidad', + 'xmas', + 'noel', + 'merry christmas', + 'gender', + 'people', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant' + ], + modifiable: true), + Emoji( + name: 'baby: medium-dark skin tone', + char: '\u{1F476}\u{1F3FE}', + shortName: 'baby_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'baby', + 'medium-dark skin tone', + 'young', + 'uc8', + 'diversity', + 'baby', + 'christmas', + 'human', + 'child', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'kid', + 'babies', + 'infant', + 'infants', + 'crying kid', + 'bebe', + 'little', + 'petite', + 'bambino', + 'navidad', + 'xmas', + 'noel', + 'merry christmas', + 'gender', + 'people', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant' + ], + modifiable: true), + Emoji( + name: 'baby: dark skin tone', + char: '\u{1F476}\u{1F3FF}', + shortName: 'baby_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'baby', + 'dark skin tone', + 'young', + 'uc8', + 'diversity', + 'baby', + 'christmas', + 'human', + 'child', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'kid', + 'babies', + 'infant', + 'infants', + 'crying kid', + 'bebe', + 'little', + 'petite', + 'bambino', + 'navidad', + 'xmas', + 'noel', + 'merry christmas', + 'gender', + 'people', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant' + ], + modifiable: true), + Emoji( + name: 'girl', + char: '\u{1F467}', + shortName: 'girl', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'Virgo', + 'young', + 'zodiac', + 'uc6', + 'diversity', + 'women', + 'beautiful', + 'human', + 'wife', + 'child', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'gender', + 'people', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant' + ]), + Emoji( + name: 'girl: light skin tone', + char: '\u{1F467}\u{1F3FB}', + shortName: 'girl_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'Virgo', + 'light skin tone', + 'young', + 'zodiac', + 'uc8', + 'diversity', + 'women', + 'beautiful', + 'human', + 'wife', + 'child', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'gender', + 'people', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant' + ], + modifiable: true), + Emoji( + name: 'girl: medium-light skin tone', + char: '\u{1F467}\u{1F3FC}', + shortName: 'girl_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'Virgo', + 'medium-light skin tone', + 'young', + 'zodiac', + 'uc8', + 'diversity', + 'women', + 'beautiful', + 'human', + 'wife', + 'child', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'gender', + 'people', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant' + ], + modifiable: true), + Emoji( + name: 'girl: medium skin tone', + char: '\u{1F467}\u{1F3FD}', + shortName: 'girl_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'Virgo', + 'medium skin tone', + 'young', + 'zodiac', + 'uc8', + 'diversity', + 'women', + 'beautiful', + 'human', + 'wife', + 'child', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'gender', + 'people', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant' + ], + modifiable: true), + Emoji( + name: 'girl: medium-dark skin tone', + char: '\u{1F467}\u{1F3FE}', + shortName: 'girl_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'Virgo', + 'medium-dark skin tone', + 'young', + 'zodiac', + 'uc8', + 'diversity', + 'women', + 'beautiful', + 'human', + 'wife', + 'child', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'gender', + 'people', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant' + ], + modifiable: true), + Emoji( + name: 'girl: dark skin tone', + char: '\u{1F467}\u{1F3FF}', + shortName: 'girl_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'Virgo', + 'dark skin tone', + 'young', + 'zodiac', + 'uc8', + 'diversity', + 'women', + 'beautiful', + 'human', + 'wife', + 'child', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'gender', + 'people', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant' + ], + modifiable: true), + Emoji( + name: 'child', + char: '\u{1F9D2}', + shortName: 'child', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc10', + 'men', + 'feminist', + 'beautiful', + 'human', + 'child', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'feminism', + 'strong woman', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'gender', + 'people', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant' + ]), + Emoji( + name: 'child: light skin tone', + char: '\u{1F9D2}\u{1F3FB}', + shortName: 'child_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'gender-neutral', + 'light skin tone', + 'young', + 'uc10', + 'men', + 'feminist', + 'beautiful', + 'human', + 'child', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'feminism', + 'strong woman', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'gender', + 'people', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant' + ], + modifiable: true), + Emoji( + name: 'child: medium-light skin tone', + char: '\u{1F9D2}\u{1F3FC}', + shortName: 'child_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'gender-neutral', + 'medium-light skin tone', + 'young', + 'uc10', + 'men', + 'feminist', + 'beautiful', + 'human', + 'child', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'feminism', + 'strong woman', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'gender', + 'people', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant' + ], + modifiable: true), + Emoji( + name: 'child: medium skin tone', + char: '\u{1F9D2}\u{1F3FD}', + shortName: 'child_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'gender-neutral', + 'medium skin tone', + 'young', + 'uc10', + 'men', + 'feminist', + 'beautiful', + 'human', + 'child', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'feminism', + 'strong woman', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'gender', + 'people', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant' + ], + modifiable: true), + Emoji( + name: 'child: medium-dark skin tone', + char: '\u{1F9D2}\u{1F3FE}', + shortName: 'child_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'gender-neutral', + 'medium-dark skin tone', + 'young', + 'uc10', + 'men', + 'feminist', + 'beautiful', + 'human', + 'child', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'feminism', + 'strong woman', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'gender', + 'people', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant' + ], + modifiable: true), + Emoji( + name: 'child: dark skin tone', + char: '\u{1F9D2}\u{1F3FF}', + shortName: 'child_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'dark skin tone', + 'gender-neutral', + 'young', + 'uc10', + 'men', + 'feminist', + 'beautiful', + 'human', + 'child', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'feminism', + 'strong woman', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'gender', + 'people', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant' + ], + modifiable: true), + Emoji( + name: 'boy', + char: '\u{1F466}', + shortName: 'boy', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'boy', + 'young', + 'uc6', + 'diversity', + 'men', + 'human', + 'handsome', + 'child', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'stud', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant' + ]), + Emoji( + name: 'boy: light skin tone', + char: '\u{1F466}\u{1F3FB}', + shortName: 'boy_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'boy', + 'light skin tone', + 'young', + 'uc8', + 'diversity', + 'men', + 'human', + 'handsome', + 'child', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'stud', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant' + ], + modifiable: true), + Emoji( + name: 'boy: medium-light skin tone', + char: '\u{1F466}\u{1F3FC}', + shortName: 'boy_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'boy', + 'medium-light skin tone', + 'young', + 'uc8', + 'diversity', + 'men', + 'human', + 'handsome', + 'child', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'stud', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant' + ], + modifiable: true), + Emoji( + name: 'boy: medium skin tone', + char: '\u{1F466}\u{1F3FD}', + shortName: 'boy_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'boy', + 'medium skin tone', + 'young', + 'uc8', + 'diversity', + 'men', + 'human', + 'handsome', + 'child', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'stud', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant' + ], + modifiable: true), + Emoji( + name: 'boy: medium-dark skin tone', + char: '\u{1F466}\u{1F3FE}', + shortName: 'boy_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'boy', + 'medium-dark skin tone', + 'young', + 'uc8', + 'diversity', + 'men', + 'human', + 'handsome', + 'child', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'stud', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant' + ], + modifiable: true), + Emoji( + name: 'boy: dark skin tone', + char: '\u{1F466}\u{1F3FF}', + shortName: 'boy_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'boy', + 'dark skin tone', + 'young', + 'uc8', + 'diversity', + 'men', + 'human', + 'handsome', + 'child', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'stud', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant' + ], + modifiable: true), + Emoji( + name: 'woman', + char: '\u{1F469}', + shortName: 'woman', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'woman', + 'uc6', + 'diversity', + 'lesbian', + 'women', + 'feminist', + 'beautiful', + 'girls night', + 'human', + 'parent', + 'wife', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'feminism', + 'strong woman', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'ladies night', + 'girls only', + 'girlfriend', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ]), + Emoji( + name: 'woman: light skin tone', + char: '\u{1F469}\u{1F3FB}', + shortName: 'woman_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'light skin tone', + 'woman', + 'uc8', + 'diversity', + 'lesbian', + 'women', + 'feminist', + 'beautiful', + 'girls night', + 'human', + 'parent', + 'wife', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'feminism', + 'strong woman', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'ladies night', + 'girls only', + 'girlfriend', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman: medium-light skin tone', + char: '\u{1F469}\u{1F3FC}', + shortName: 'woman_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'medium-light skin tone', + 'woman', + 'uc8', + 'diversity', + 'lesbian', + 'women', + 'feminist', + 'beautiful', + 'girls night', + 'human', + 'parent', + 'wife', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'feminism', + 'strong woman', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'ladies night', + 'girls only', + 'girlfriend', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman: medium skin tone', + char: '\u{1F469}\u{1F3FD}', + shortName: 'woman_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'medium skin tone', + 'woman', + 'uc8', + 'diversity', + 'lesbian', + 'women', + 'feminist', + 'beautiful', + 'girls night', + 'human', + 'parent', + 'wife', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'feminism', + 'strong woman', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'ladies night', + 'girls only', + 'girlfriend', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman: medium-dark skin tone', + char: '\u{1F469}\u{1F3FE}', + shortName: 'woman_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'medium-dark skin tone', + 'woman', + 'uc8', + 'diversity', + 'lesbian', + 'women', + 'feminist', + 'beautiful', + 'girls night', + 'human', + 'parent', + 'wife', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'feminism', + 'strong woman', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'ladies night', + 'girls only', + 'girlfriend', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman: dark skin tone', + char: '\u{1F469}\u{1F3FF}', + shortName: 'woman_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'dark skin tone', + 'woman', + 'uc8', + 'diversity', + 'lesbian', + 'women', + 'feminist', + 'beautiful', + 'girls night', + 'human', + 'parent', + 'wife', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'feminism', + 'strong woman', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'ladies night', + 'girls only', + 'girlfriend', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'person', + char: '\u{1F9D1}', + shortName: 'adult', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc10', + 'diversity', + 'lesbian', + 'men', + 'feminist', + 'girls night', + 'boys night', + 'human', + 'parent', + 'handsome', + 'wife', + 'husband', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'feminism', + 'strong woman', + 'ladies night', + 'girls only', + 'girlfriend', + 'guys night', + 'gender', + 'people', + 'parents', + 'adult', + 'stud', + 'maman', + 'mommy', + 'mama', + 'mother' + ]), + Emoji( + name: 'person: light skin tone', + char: '\u{1F9D1}\u{1F3FB}', + shortName: 'adult_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'gender-neutral', + 'light skin tone', + 'uc10', + 'diversity', + 'lesbian', + 'men', + 'feminist', + 'girls night', + 'boys night', + 'human', + 'parent', + 'handsome', + 'wife', + 'husband', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'feminism', + 'strong woman', + 'ladies night', + 'girls only', + 'girlfriend', + 'guys night', + 'gender', + 'people', + 'parents', + 'adult', + 'stud', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'person: medium-light skin tone', + char: '\u{1F9D1}\u{1F3FC}', + shortName: 'adult_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'gender-neutral', + 'medium-light skin tone', + 'uc10', + 'diversity', + 'lesbian', + 'men', + 'feminist', + 'girls night', + 'boys night', + 'human', + 'parent', + 'handsome', + 'wife', + 'husband', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'feminism', + 'strong woman', + 'ladies night', + 'girls only', + 'girlfriend', + 'guys night', + 'gender', + 'people', + 'parents', + 'adult', + 'stud', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'person: medium skin tone', + char: '\u{1F9D1}\u{1F3FD}', + shortName: 'adult_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'gender-neutral', + 'medium skin tone', + 'uc10', + 'diversity', + 'lesbian', + 'men', + 'feminist', + 'girls night', + 'boys night', + 'human', + 'parent', + 'handsome', + 'wife', + 'husband', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'feminism', + 'strong woman', + 'ladies night', + 'girls only', + 'girlfriend', + 'guys night', + 'gender', + 'people', + 'parents', + 'adult', + 'stud', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'person: medium-dark skin tone', + char: '\u{1F9D1}\u{1F3FE}', + shortName: 'adult_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'gender-neutral', + 'medium-dark skin tone', + 'uc10', + 'diversity', + 'lesbian', + 'men', + 'feminist', + 'girls night', + 'boys night', + 'human', + 'parent', + 'handsome', + 'wife', + 'husband', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'feminism', + 'strong woman', + 'ladies night', + 'girls only', + 'girlfriend', + 'guys night', + 'gender', + 'people', + 'parents', + 'adult', + 'stud', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'person: dark skin tone', + char: '\u{1F9D1}\u{1F3FF}', + shortName: 'adult_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'dark skin tone', + 'gender-neutral', + 'uc10', + 'diversity', + 'lesbian', + 'men', + 'feminist', + 'girls night', + 'boys night', + 'human', + 'parent', + 'handsome', + 'wife', + 'husband', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'feminism', + 'strong woman', + 'ladies night', + 'girls only', + 'girlfriend', + 'guys night', + 'gender', + 'people', + 'parents', + 'adult', + 'stud', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'man', + char: '\u{1F468}', + shortName: 'man', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'man', + 'uc6', + 'diversity', + 'men', + 'boys night', + 'human', + 'daddy', + 'parent', + 'handsome', + 'husband', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'guys night', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud' + ]), + Emoji( + name: 'man: light skin tone', + char: '\u{1F468}\u{1F3FB}', + shortName: 'man_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'light skin tone', + 'man', + 'uc8', + 'diversity', + 'men', + 'boys night', + 'human', + 'daddy', + 'parent', + 'handsome', + 'husband', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'guys night', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud' + ], + modifiable: true), + Emoji( + name: 'man: medium-light skin tone', + char: '\u{1F468}\u{1F3FC}', + shortName: 'man_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'man', + 'medium-light skin tone', + 'uc8', + 'diversity', + 'men', + 'boys night', + 'human', + 'daddy', + 'parent', + 'handsome', + 'husband', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'guys night', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud' + ], + modifiable: true), + Emoji( + name: 'man: medium skin tone', + char: '\u{1F468}\u{1F3FD}', + shortName: 'man_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'man', + 'medium skin tone', + 'uc8', + 'diversity', + 'men', + 'boys night', + 'human', + 'daddy', + 'parent', + 'handsome', + 'husband', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'guys night', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud' + ], + modifiable: true), + Emoji( + name: 'man: medium-dark skin tone', + char: '\u{1F468}\u{1F3FE}', + shortName: 'man_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'man', + 'medium-dark skin tone', + 'uc8', + 'diversity', + 'men', + 'boys night', + 'human', + 'daddy', + 'parent', + 'handsome', + 'husband', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'guys night', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud' + ], + modifiable: true), + Emoji( + name: 'man: dark skin tone', + char: '\u{1F468}\u{1F3FF}', + shortName: 'man_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'dark skin tone', + 'man', + 'uc8', + 'diversity', + 'men', + 'boys night', + 'human', + 'daddy', + 'parent', + 'handsome', + 'husband', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'guys night', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud' + ], + modifiable: true), + Emoji( + name: 'person: curly hair', + char: '\u{1F9D1}\u{200D}\u{1F9B1}', + shortName: 'person_curly_hair', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc12', + 'men', + 'human', + 'daddy', + 'parent', + 'handsome', + 'husband', + 'afro', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud', + "'fro", + 'curls', + 'frizzy', + 'perm' + ]), + Emoji( + name: 'person: light skin tone, curly hair', + char: '\u{1F9D1}\u{1F3FB}\u{200D}\u{1F9B1}', + shortName: 'person_tone1_curly_hair', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc12', + 'men', + 'human', + 'daddy', + 'parent', + 'handsome', + 'husband', + 'afro', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud', + "'fro", + 'curls', + 'frizzy', + 'perm' + ], + modifiable: true), + Emoji( + name: 'person: medium-light skin tone, curly hair', + char: '\u{1F9D1}\u{1F3FC}\u{200D}\u{1F9B1}', + shortName: 'person_tone2_curly_hair', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc12', + 'men', + 'human', + 'daddy', + 'parent', + 'handsome', + 'husband', + 'afro', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud', + "'fro", + 'curls', + 'frizzy', + 'perm' + ], + modifiable: true), + Emoji( + name: 'person: medium skin tone, curly hair', + char: '\u{1F9D1}\u{1F3FD}\u{200D}\u{1F9B1}', + shortName: 'person_tone3_curly_hair', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc12', + 'men', + 'human', + 'daddy', + 'parent', + 'handsome', + 'husband', + 'afro', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud', + "'fro", + 'curls', + 'frizzy', + 'perm' + ], + modifiable: true), + Emoji( + name: 'person: medium-dark skin tone, curly hair', + char: '\u{1F9D1}\u{1F3FE}\u{200D}\u{1F9B1}', + shortName: 'person_tone4_curly_hair', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc12', + 'men', + 'human', + 'daddy', + 'parent', + 'handsome', + 'husband', + 'afro', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud', + "'fro", + 'curls', + 'frizzy', + 'perm' + ], + modifiable: true), + Emoji( + name: 'person: dark skin tone, curly hair', + char: '\u{1F9D1}\u{1F3FF}\u{200D}\u{1F9B1}', + shortName: 'person_tone5_curly_hair', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc12', + 'men', + 'human', + 'daddy', + 'parent', + 'handsome', + 'husband', + 'afro', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud', + "'fro", + 'curls', + 'frizzy', + 'perm' + ], + modifiable: true), + Emoji( + name: 'woman: curly hair', + char: '\u{1F469}\u{200D}\u{1F9B1}', + shortName: 'woman_curly_haired', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc11', + 'diversity', + 'lesbian', + 'women', + 'beautiful', + 'girls night', + 'human', + 'parent', + 'wife', + 'mom', + 'afro', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'ladies night', + 'girls only', + 'girlfriend', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother', + "'fro", + 'curls', + 'frizzy', + 'perm' + ]), + Emoji( + name: 'woman: light skin tone, curly hair', + char: '\u{1F469}\u{1F3FB}\u{200D}\u{1F9B1}', + shortName: 'woman_curly_haired_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc11', + 'diversity', + 'lesbian', + 'women', + 'beautiful', + 'girls night', + 'human', + 'parent', + 'wife', + 'mom', + 'afro', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'ladies night', + 'girls only', + 'girlfriend', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother', + "'fro", + 'curls', + 'frizzy', + 'perm' + ], + modifiable: true), + Emoji( + name: 'woman: medium-light skin tone, curly hair', + char: '\u{1F469}\u{1F3FC}\u{200D}\u{1F9B1}', + shortName: 'woman_curly_haired_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc11', + 'diversity', + 'lesbian', + 'women', + 'beautiful', + 'girls night', + 'human', + 'parent', + 'wife', + 'mom', + 'afro', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'ladies night', + 'girls only', + 'girlfriend', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother', + "'fro", + 'curls', + 'frizzy', + 'perm' + ], + modifiable: true), + Emoji( + name: 'woman: medium skin tone, curly hair', + char: '\u{1F469}\u{1F3FD}\u{200D}\u{1F9B1}', + shortName: 'woman_curly_haired_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc11', + 'diversity', + 'lesbian', + 'women', + 'beautiful', + 'girls night', + 'human', + 'parent', + 'wife', + 'mom', + 'afro', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'ladies night', + 'girls only', + 'girlfriend', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother', + "'fro", + 'curls', + 'frizzy', + 'perm' + ], + modifiable: true), + Emoji( + name: 'woman: medium-dark skin tone, curly hair', + char: '\u{1F469}\u{1F3FE}\u{200D}\u{1F9B1}', + shortName: 'woman_curly_haired_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc11', + 'diversity', + 'lesbian', + 'women', + 'beautiful', + 'girls night', + 'human', + 'parent', + 'wife', + 'mom', + 'afro', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'ladies night', + 'girls only', + 'girlfriend', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother', + "'fro", + 'curls', + 'frizzy', + 'perm' + ], + modifiable: true), + Emoji( + name: 'woman: dark skin tone, curly hair', + char: '\u{1F469}\u{1F3FF}\u{200D}\u{1F9B1}', + shortName: 'woman_curly_haired_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc11', + 'diversity', + 'lesbian', + 'women', + 'beautiful', + 'girls night', + 'human', + 'parent', + 'wife', + 'mom', + 'afro', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'ladies night', + 'girls only', + 'girlfriend', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother', + "'fro", + 'curls', + 'frizzy', + 'perm' + ], + modifiable: true), + Emoji( + name: 'man: curly hair', + char: '\u{1F468}\u{200D}\u{1F9B1}', + shortName: 'man_curly_haired', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc11', + 'diversity', + 'men', + 'human', + 'daddy', + 'parent', + 'handsome', + 'husband', + 'afro', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud', + "'fro", + 'curls', + 'frizzy', + 'perm' + ]), + Emoji( + name: 'man: light skin tone, curly hair', + char: '\u{1F468}\u{1F3FB}\u{200D}\u{1F9B1}', + shortName: 'man_curly_haired_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc11', + 'diversity', + 'men', + 'human', + 'daddy', + 'parent', + 'handsome', + 'husband', + 'afro', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud', + "'fro", + 'curls', + 'frizzy', + 'perm' + ], + modifiable: true), + Emoji( + name: 'man: medium-light skin tone, curly hair', + char: '\u{1F468}\u{1F3FC}\u{200D}\u{1F9B1}', + shortName: 'man_curly_haired_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc11', + 'diversity', + 'men', + 'human', + 'daddy', + 'parent', + 'handsome', + 'husband', + 'afro', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud', + "'fro", + 'curls', + 'frizzy', + 'perm' + ], + modifiable: true), + Emoji( + name: 'man: medium skin tone, curly hair', + char: '\u{1F468}\u{1F3FD}\u{200D}\u{1F9B1}', + shortName: 'man_curly_haired_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc11', + 'diversity', + 'men', + 'human', + 'daddy', + 'parent', + 'handsome', + 'husband', + 'afro', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud', + "'fro", + 'curls', + 'frizzy', + 'perm' + ], + modifiable: true), + Emoji( + name: 'man: medium-dark skin tone, curly hair', + char: '\u{1F468}\u{1F3FE}\u{200D}\u{1F9B1}', + shortName: 'man_curly_haired_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc11', + 'diversity', + 'men', + 'human', + 'daddy', + 'parent', + 'handsome', + 'husband', + 'afro', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud', + "'fro", + 'curls', + 'frizzy', + 'perm' + ], + modifiable: true), + Emoji( + name: 'man: dark skin tone, curly hair', + char: '\u{1F468}\u{1F3FF}\u{200D}\u{1F9B1}', + shortName: 'man_curly_haired_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc11', + 'diversity', + 'men', + 'human', + 'daddy', + 'parent', + 'handsome', + 'husband', + 'afro', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud', + "'fro", + 'curls', + 'frizzy', + 'perm' + ], + modifiable: true), + Emoji( + name: 'person: red hair', + char: '\u{1F9D1}\u{200D}\u{1F9B0}', + shortName: 'person_red_hair', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc12', + 'lesbian', + 'men', + 'girls night', + 'human', + 'daddy', + 'parent', + 'handsome', + 'wife', + 'husband', + 'mom', + 'ginger', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'ladies night', + 'girls only', + 'girlfriend', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud', + 'maman', + 'mommy', + 'mama', + 'mother' + ]), + Emoji( + name: 'person: light skin tone, red hair', + char: '\u{1F9D1}\u{1F3FB}\u{200D}\u{1F9B0}', + shortName: 'person_tone1_red_hair', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc12', + 'lesbian', + 'men', + 'girls night', + 'human', + 'daddy', + 'parent', + 'handsome', + 'wife', + 'husband', + 'mom', + 'ginger', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'ladies night', + 'girls only', + 'girlfriend', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'person: medium-light skin tone, red hair', + char: '\u{1F9D1}\u{1F3FC}\u{200D}\u{1F9B0}', + shortName: 'person_tone2_red_hair', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc12', + 'lesbian', + 'men', + 'girls night', + 'human', + 'daddy', + 'parent', + 'handsome', + 'wife', + 'husband', + 'mom', + 'ginger', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'ladies night', + 'girls only', + 'girlfriend', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'person: medium skin tone, red hair', + char: '\u{1F9D1}\u{1F3FD}\u{200D}\u{1F9B0}', + shortName: 'person_tone3_red_hair', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc12', + 'lesbian', + 'men', + 'girls night', + 'human', + 'daddy', + 'parent', + 'handsome', + 'wife', + 'husband', + 'mom', + 'ginger', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'ladies night', + 'girls only', + 'girlfriend', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'person: medium-dark skin tone, red hair', + char: '\u{1F9D1}\u{1F3FE}\u{200D}\u{1F9B0}', + shortName: 'person_tone4_red_hair', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc12', + 'lesbian', + 'men', + 'girls night', + 'human', + 'daddy', + 'parent', + 'handsome', + 'wife', + 'husband', + 'mom', + 'ginger', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'ladies night', + 'girls only', + 'girlfriend', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'person: dark skin tone, red hair', + char: '\u{1F9D1}\u{1F3FF}\u{200D}\u{1F9B0}', + shortName: 'person_tone5_red_hair', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc12', + 'lesbian', + 'men', + 'girls night', + 'human', + 'daddy', + 'parent', + 'handsome', + 'wife', + 'husband', + 'mom', + 'ginger', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'ladies night', + 'girls only', + 'girlfriend', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman: red hair', + char: '\u{1F469}\u{200D}\u{1F9B0}', + shortName: 'woman_red_haired', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc11', + 'diversity', + 'lesbian', + 'women', + 'beautiful', + 'human', + 'parent', + 'wife', + 'mom', + 'ginger', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ]), + Emoji( + name: 'woman: light skin tone, red hair', + char: '\u{1F469}\u{1F3FB}\u{200D}\u{1F9B0}', + shortName: 'woman_red_haired_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc11', + 'diversity', + 'lesbian', + 'women', + 'beautiful', + 'human', + 'parent', + 'wife', + 'mom', + 'ginger', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman: medium-light skin tone, red hair', + char: '\u{1F469}\u{1F3FC}\u{200D}\u{1F9B0}', + shortName: 'woman_red_haired_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc11', + 'diversity', + 'lesbian', + 'women', + 'beautiful', + 'human', + 'parent', + 'wife', + 'mom', + 'ginger', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman: medium skin tone, red hair', + char: '\u{1F469}\u{1F3FD}\u{200D}\u{1F9B0}', + shortName: 'woman_red_haired_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc11', + 'diversity', + 'lesbian', + 'women', + 'beautiful', + 'human', + 'parent', + 'wife', + 'mom', + 'ginger', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman: medium-dark skin tone, red hair', + char: '\u{1F469}\u{1F3FE}\u{200D}\u{1F9B0}', + shortName: 'woman_red_haired_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc11', + 'diversity', + 'lesbian', + 'women', + 'beautiful', + 'human', + 'parent', + 'wife', + 'mom', + 'ginger', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman: dark skin tone, red hair', + char: '\u{1F469}\u{1F3FF}\u{200D}\u{1F9B0}', + shortName: 'woman_red_haired_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc11', + 'diversity', + 'lesbian', + 'women', + 'beautiful', + 'human', + 'parent', + 'wife', + 'mom', + 'ginger', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'man: red hair', + char: '\u{1F468}\u{200D}\u{1F9B0}', + shortName: 'man_red_haired', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc11', + 'diversity', + 'men', + 'human', + 'daddy', + 'parent', + 'handsome', + 'husband', + 'ginger', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud' + ]), + Emoji( + name: 'man: light skin tone, red hair', + char: '\u{1F468}\u{1F3FB}\u{200D}\u{1F9B0}', + shortName: 'man_red_haired_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc11', + 'diversity', + 'men', + 'human', + 'daddy', + 'parent', + 'handsome', + 'husband', + 'ginger', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud' + ], + modifiable: true), + Emoji( + name: 'man: medium-light skin tone, red hair', + char: '\u{1F468}\u{1F3FC}\u{200D}\u{1F9B0}', + shortName: 'man_red_haired_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc11', + 'diversity', + 'men', + 'human', + 'daddy', + 'parent', + 'handsome', + 'husband', + 'ginger', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud' + ], + modifiable: true), + Emoji( + name: 'man: medium skin tone, red hair', + char: '\u{1F468}\u{1F3FD}\u{200D}\u{1F9B0}', + shortName: 'man_red_haired_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc11', + 'diversity', + 'men', + 'human', + 'daddy', + 'parent', + 'handsome', + 'husband', + 'ginger', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud' + ], + modifiable: true), + Emoji( + name: 'man: medium-dark skin tone, red hair', + char: '\u{1F468}\u{1F3FE}\u{200D}\u{1F9B0}', + shortName: 'man_red_haired_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc11', + 'diversity', + 'men', + 'human', + 'daddy', + 'parent', + 'handsome', + 'husband', + 'ginger', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud' + ], + modifiable: true), + Emoji( + name: 'man: dark skin tone, red hair', + char: '\u{1F468}\u{1F3FF}\u{200D}\u{1F9B0}', + shortName: 'man_red_haired_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc11', + 'diversity', + 'men', + 'human', + 'daddy', + 'parent', + 'handsome', + 'husband', + 'ginger', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud' + ], + modifiable: true), + Emoji( + name: 'woman: blond hair', + char: '\u{1F471}\u{200D}\u{2640}\u{FE0F}', + shortName: 'blond-haired_woman', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'blonde', + 'woman', + 'uc6', + 'diversity', + 'lesbian', + 'women', + 'feminist', + 'beautiful', + 'girls night', + 'human', + 'parent', + 'wife', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'feminism', + 'strong woman', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'ladies night', + 'girls only', + 'girlfriend', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ]), + Emoji( + name: 'woman: light skin tone, blond hair', + char: '\u{1F471}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}', + shortName: 'blond-haired_woman_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'blonde', + 'light skin tone', + 'woman', + 'uc8', + 'diversity', + 'lesbian', + 'women', + 'feminist', + 'beautiful', + 'girls night', + 'human', + 'parent', + 'wife', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'feminism', + 'strong woman', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'ladies night', + 'girls only', + 'girlfriend', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman: medium-light skin tone, blond hair', + char: '\u{1F471}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}', + shortName: 'blond-haired_woman_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'blonde', + 'medium-light skin tone', + 'woman', + 'uc8', + 'diversity', + 'lesbian', + 'women', + 'feminist', + 'beautiful', + 'girls night', + 'human', + 'parent', + 'wife', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'feminism', + 'strong woman', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'ladies night', + 'girls only', + 'girlfriend', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman: medium skin tone, blond hair', + char: '\u{1F471}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}', + shortName: 'blond-haired_woman_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'blonde', + 'medium skin tone', + 'woman', + 'uc8', + 'diversity', + 'lesbian', + 'women', + 'feminist', + 'beautiful', + 'girls night', + 'human', + 'parent', + 'wife', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'feminism', + 'strong woman', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'ladies night', + 'girls only', + 'girlfriend', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman: medium-dark skin tone, blond hair', + char: '\u{1F471}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}', + shortName: 'blond-haired_woman_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'blonde', + 'medium-dark skin tone', + 'woman', + 'uc8', + 'diversity', + 'lesbian', + 'women', + 'feminist', + 'beautiful', + 'girls night', + 'human', + 'parent', + 'wife', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'feminism', + 'strong woman', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'ladies night', + 'girls only', + 'girlfriend', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman: dark skin tone, blond hair', + char: '\u{1F471}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}', + shortName: 'blond-haired_woman_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'blonde', + 'dark skin tone', + 'woman', + 'uc8', + 'diversity', + 'lesbian', + 'women', + 'feminist', + 'beautiful', + 'girls night', + 'human', + 'parent', + 'wife', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'feminism', + 'strong woman', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'ladies night', + 'girls only', + 'girlfriend', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'person: blond hair', + char: '\u{1F471}', + shortName: 'blond_haired_person', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'blond', + 'uc6', + 'diversity', + 'lesbian', + 'men', + 'feminist', + 'boys night', + 'human', + 'daddy', + 'parent', + 'handsome', + 'wife', + 'husband', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'feminism', + 'strong woman', + 'guys night', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud', + 'maman', + 'mommy', + 'mama', + 'mother' + ]), + Emoji( + name: 'person: light skin tone, blond hair', + char: '\u{1F471}\u{1F3FB}', + shortName: 'blond_haired_person_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'blond', + 'light skin tone', + 'uc8', + 'diversity', + 'lesbian', + 'men', + 'feminist', + 'boys night', + 'human', + 'daddy', + 'parent', + 'handsome', + 'wife', + 'husband', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'feminism', + 'strong woman', + 'guys night', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'person: medium-light skin tone, blond hair', + char: '\u{1F471}\u{1F3FC}', + shortName: 'blond_haired_person_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'blond', + 'medium-light skin tone', + 'uc8', + 'diversity', + 'lesbian', + 'men', + 'feminist', + 'boys night', + 'human', + 'daddy', + 'parent', + 'handsome', + 'wife', + 'husband', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'feminism', + 'strong woman', + 'guys night', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'person: medium skin tone, blond hair', + char: '\u{1F471}\u{1F3FD}', + shortName: 'blond_haired_person_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'blond', + 'medium skin tone', + 'uc8', + 'diversity', + 'lesbian', + 'men', + 'feminist', + 'boys night', + 'human', + 'daddy', + 'parent', + 'handsome', + 'wife', + 'husband', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'feminism', + 'strong woman', + 'guys night', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'person: medium-dark skin tone, blond hair', + char: '\u{1F471}\u{1F3FE}', + shortName: 'blond_haired_person_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'blond', + 'medium-dark skin tone', + 'uc8', + 'diversity', + 'lesbian', + 'men', + 'feminist', + 'boys night', + 'human', + 'daddy', + 'parent', + 'handsome', + 'wife', + 'husband', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'feminism', + 'strong woman', + 'guys night', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'person: dark skin tone, blond hair', + char: '\u{1F471}\u{1F3FF}', + shortName: 'blond_haired_person_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'blond', + 'dark skin tone', + 'uc8', + 'diversity', + 'lesbian', + 'men', + 'feminist', + 'boys night', + 'human', + 'daddy', + 'parent', + 'handsome', + 'wife', + 'husband', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'feminism', + 'strong woman', + 'guys night', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'man: blond hair', + char: '\u{1F471}\u{200D}\u{2642}\u{FE0F}', + shortName: 'blond-haired_man', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'blond', + 'man', + 'uc6', + 'diversity', + 'men', + 'boys night', + 'human', + 'daddy', + 'parent', + 'husband', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'guys night', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ]), + Emoji( + name: 'man: light skin tone, blond hair', + char: '\u{1F471}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}', + shortName: 'blond-haired_man_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'blond', + 'light skin tone', + 'man', + 'uc8', + 'diversity', + 'men', + 'boys night', + 'human', + 'daddy', + 'parent', + 'husband', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'guys night', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'man: medium-light skin tone, blond hair', + char: '\u{1F471}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}', + shortName: 'blond-haired_man_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'blond', + 'man', + 'medium-light skin tone', + 'uc8', + 'diversity', + 'men', + 'boys night', + 'human', + 'daddy', + 'parent', + 'husband', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'guys night', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'man: medium skin tone, blond hair', + char: '\u{1F471}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}', + shortName: 'blond-haired_man_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'blond', + 'man', + 'medium skin tone', + 'uc8', + 'diversity', + 'men', + 'boys night', + 'human', + 'daddy', + 'parent', + 'husband', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'guys night', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'man: medium-dark skin tone, blond hair', + char: '\u{1F471}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}', + shortName: 'blond-haired_man_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'blond', + 'man', + 'medium-dark skin tone', + 'uc8', + 'diversity', + 'men', + 'boys night', + 'human', + 'daddy', + 'parent', + 'husband', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'guys night', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'man: dark skin tone, blond hair', + char: '\u{1F471}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}', + shortName: 'blond-haired_man_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'blond', + 'dark skin tone', + 'man', + 'uc8', + 'diversity', + 'men', + 'boys night', + 'human', + 'daddy', + 'parent', + 'husband', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'guys night', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'person: white hair', + char: '\u{1F9D1}\u{200D}\u{1F9B3}', + shortName: 'person_white_hair', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc12', + 'old people', + 'lesbian', + 'men', + 'human', + 'daddy', + 'parent', + 'handsome', + 'wife', + 'husband', + 'mom', + 'grey hair', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud', + 'maman', + 'mommy', + 'mama', + 'mother', + 'silver hair' + ]), + Emoji( + name: 'person: light skin tone, white hair', + char: '\u{1F9D1}\u{1F3FB}\u{200D}\u{1F9B3}', + shortName: 'person_tone1_white_hair', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc12', + 'old people', + 'lesbian', + 'men', + 'human', + 'daddy', + 'parent', + 'handsome', + 'wife', + 'husband', + 'mom', + 'grey hair', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud', + 'maman', + 'mommy', + 'mama', + 'mother', + 'silver hair' + ], + modifiable: true), + Emoji( + name: 'person: medium-light skin tone, white hair', + char: '\u{1F9D1}\u{1F3FC}\u{200D}\u{1F9B3}', + shortName: 'person_tone2_white_hair', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc12', + 'old people', + 'lesbian', + 'men', + 'human', + 'daddy', + 'parent', + 'handsome', + 'wife', + 'husband', + 'mom', + 'grey hair', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud', + 'maman', + 'mommy', + 'mama', + 'mother', + 'silver hair' + ], + modifiable: true), + Emoji( + name: 'person: medium skin tone, white hair', + char: '\u{1F9D1}\u{1F3FD}\u{200D}\u{1F9B3}', + shortName: 'person_tone3_white_hair', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc12', + 'old people', + 'lesbian', + 'men', + 'human', + 'daddy', + 'parent', + 'handsome', + 'wife', + 'husband', + 'mom', + 'grey hair', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud', + 'maman', + 'mommy', + 'mama', + 'mother', + 'silver hair' + ], + modifiable: true), + Emoji( + name: 'person: medium-dark skin tone, white hair', + char: '\u{1F9D1}\u{1F3FE}\u{200D}\u{1F9B3}', + shortName: 'person_tone4_white_hair', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc12', + 'old people', + 'lesbian', + 'men', + 'human', + 'daddy', + 'parent', + 'handsome', + 'wife', + 'husband', + 'mom', + 'grey hair', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud', + 'maman', + 'mommy', + 'mama', + 'mother', + 'silver hair' + ], + modifiable: true), + Emoji( + name: 'person: dark skin tone, white hair', + char: '\u{1F9D1}\u{1F3FF}\u{200D}\u{1F9B3}', + shortName: 'person_tone5_white_hair', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc12', + 'old people', + 'lesbian', + 'men', + 'human', + 'daddy', + 'parent', + 'handsome', + 'wife', + 'husband', + 'mom', + 'grey hair', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud', + 'maman', + 'mommy', + 'mama', + 'mother', + 'silver hair' + ], + modifiable: true), + Emoji( + name: 'woman: white hair', + char: '\u{1F469}\u{200D}\u{1F9B3}', + shortName: 'woman_white_haired', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc11', + 'old people', + 'diversity', + 'lesbian', + 'women', + 'beautiful', + 'human', + 'parent', + 'wife', + 'mom', + 'grey hair', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother', + 'silver hair' + ]), + Emoji( + name: 'woman: light skin tone, white hair', + char: '\u{1F469}\u{1F3FB}\u{200D}\u{1F9B3}', + shortName: 'woman_white_haired_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc11', + 'old people', + 'diversity', + 'lesbian', + 'women', + 'beautiful', + 'human', + 'parent', + 'wife', + 'mom', + 'grey hair', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother', + 'silver hair' + ], + modifiable: true), + Emoji( + name: 'woman: medium-light skin tone, white hair', + char: '\u{1F469}\u{1F3FC}\u{200D}\u{1F9B3}', + shortName: 'woman_white_haired_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc11', + 'old people', + 'diversity', + 'lesbian', + 'women', + 'beautiful', + 'human', + 'parent', + 'wife', + 'mom', + 'grey hair', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother', + 'silver hair' + ], + modifiable: true), + Emoji( + name: 'woman: medium skin tone, white hair', + char: '\u{1F469}\u{1F3FD}\u{200D}\u{1F9B3}', + shortName: 'woman_white_haired_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc11', + 'old people', + 'diversity', + 'lesbian', + 'women', + 'beautiful', + 'human', + 'parent', + 'wife', + 'mom', + 'grey hair', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother', + 'silver hair' + ], + modifiable: true), + Emoji( + name: 'woman: medium-dark skin tone, white hair', + char: '\u{1F469}\u{1F3FE}\u{200D}\u{1F9B3}', + shortName: 'woman_white_haired_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc11', + 'old people', + 'diversity', + 'lesbian', + 'women', + 'beautiful', + 'human', + 'parent', + 'wife', + 'mom', + 'grey hair', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother', + 'silver hair' + ], + modifiable: true), + Emoji( + name: 'woman: dark skin tone, white hair', + char: '\u{1F469}\u{1F3FF}\u{200D}\u{1F9B3}', + shortName: 'woman_white_haired_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc11', + 'old people', + 'diversity', + 'lesbian', + 'women', + 'beautiful', + 'human', + 'parent', + 'wife', + 'mom', + 'grey hair', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother', + 'silver hair' + ], + modifiable: true), + Emoji( + name: 'man: white hair', + char: '\u{1F468}\u{200D}\u{1F9B3}', + shortName: 'man_white_haired', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc11', + 'old people', + 'diversity', + 'men', + 'human', + 'daddy', + 'parent', + 'handsome', + 'husband', + 'grey hair', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud', + 'silver hair' + ]), + Emoji( + name: 'man: light skin tone, white hair', + char: '\u{1F468}\u{1F3FB}\u{200D}\u{1F9B3}', + shortName: 'man_white_haired_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc11', + 'old people', + 'diversity', + 'men', + 'human', + 'daddy', + 'parent', + 'handsome', + 'husband', + 'grey hair', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud', + 'silver hair' + ], + modifiable: true), + Emoji( + name: 'man: medium-light skin tone, white hair', + char: '\u{1F468}\u{1F3FC}\u{200D}\u{1F9B3}', + shortName: 'man_white_haired_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc11', + 'old people', + 'diversity', + 'men', + 'human', + 'daddy', + 'parent', + 'handsome', + 'husband', + 'grey hair', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud', + 'silver hair' + ], + modifiable: true), + Emoji( + name: 'man: medium skin tone, white hair', + char: '\u{1F468}\u{1F3FD}\u{200D}\u{1F9B3}', + shortName: 'man_white_haired_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc11', + 'old people', + 'diversity', + 'men', + 'human', + 'daddy', + 'parent', + 'handsome', + 'husband', + 'grey hair', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud', + 'silver hair' + ], + modifiable: true), + Emoji( + name: 'man: medium-dark skin tone, white hair', + char: '\u{1F468}\u{1F3FE}\u{200D}\u{1F9B3}', + shortName: 'man_white_haired_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc11', + 'old people', + 'diversity', + 'men', + 'human', + 'daddy', + 'parent', + 'handsome', + 'husband', + 'grey hair', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud', + 'silver hair' + ], + modifiable: true), + Emoji( + name: 'man: dark skin tone, white hair', + char: '\u{1F468}\u{1F3FF}\u{200D}\u{1F9B3}', + shortName: 'man_white_haired_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc11', + 'old people', + 'diversity', + 'men', + 'human', + 'daddy', + 'parent', + 'handsome', + 'husband', + 'grey hair', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud', + 'silver hair' + ], + modifiable: true), + Emoji( + name: 'person: bald', + char: '\u{1F9D1}\u{200D}\u{1F9B2}', + shortName: 'person_bald', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc12', + 'lesbian', + 'men', + 'human', + 'daddy', + 'parent', + 'handsome', + 'wife', + 'husband', + 'mom', + 'shaved head', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud', + 'maman', + 'mommy', + 'mama', + 'mother' + ]), + Emoji( + name: 'person: light skin tone, bald', + char: '\u{1F9D1}\u{1F3FB}\u{200D}\u{1F9B2}', + shortName: 'person_tone1_bald', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc12', + 'lesbian', + 'men', + 'human', + 'daddy', + 'parent', + 'handsome', + 'wife', + 'husband', + 'mom', + 'shaved head', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'person: medium-light skin tone, bald', + char: '\u{1F9D1}\u{1F3FC}\u{200D}\u{1F9B2}', + shortName: 'person_tone2_bald', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc12', + 'lesbian', + 'men', + 'human', + 'daddy', + 'parent', + 'handsome', + 'wife', + 'husband', + 'mom', + 'shaved head', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'person: medium skin tone, bald', + char: '\u{1F9D1}\u{1F3FD}\u{200D}\u{1F9B2}', + shortName: 'person_tone3_bald', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc12', + 'lesbian', + 'men', + 'human', + 'daddy', + 'parent', + 'handsome', + 'wife', + 'husband', + 'mom', + 'shaved head', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'person: medium-dark skin tone, bald', + char: '\u{1F9D1}\u{1F3FE}\u{200D}\u{1F9B2}', + shortName: 'person_tone4_bald', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc12', + 'lesbian', + 'men', + 'human', + 'daddy', + 'parent', + 'handsome', + 'wife', + 'husband', + 'mom', + 'shaved head', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'person: dark skin tone, bald', + char: '\u{1F9D1}\u{1F3FF}\u{200D}\u{1F9B2}', + shortName: 'person_tone5_bald', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc12', + 'lesbian', + 'men', + 'human', + 'daddy', + 'parent', + 'handsome', + 'wife', + 'husband', + 'mom', + 'shaved head', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman: bald', + char: '\u{1F469}\u{200D}\u{1F9B2}', + shortName: 'woman_bald', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc11', + 'old people', + 'diversity', + 'lesbian', + 'women', + 'beautiful', + 'human', + 'parent', + 'wife', + 'mom', + 'shaved head', + 'hairless', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother', + 'balding' + ]), + Emoji( + name: 'woman: light skin tone, bald', + char: '\u{1F469}\u{1F3FB}\u{200D}\u{1F9B2}', + shortName: 'woman_bald_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc11', + 'old people', + 'diversity', + 'lesbian', + 'women', + 'beautiful', + 'human', + 'parent', + 'wife', + 'mom', + 'shaved head', + 'hairless', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother', + 'balding' + ], + modifiable: true), + Emoji( + name: 'woman: medium-light skin tone, bald', + char: '\u{1F469}\u{1F3FC}\u{200D}\u{1F9B2}', + shortName: 'woman_bald_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc11', + 'old people', + 'diversity', + 'lesbian', + 'women', + 'beautiful', + 'human', + 'parent', + 'wife', + 'mom', + 'shaved head', + 'hairless', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother', + 'balding' + ], + modifiable: true), + Emoji( + name: 'woman: medium skin tone, bald', + char: '\u{1F469}\u{1F3FD}\u{200D}\u{1F9B2}', + shortName: 'woman_bald_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc11', + 'old people', + 'diversity', + 'lesbian', + 'women', + 'beautiful', + 'human', + 'parent', + 'wife', + 'mom', + 'shaved head', + 'hairless', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother', + 'balding' + ], + modifiable: true), + Emoji( + name: 'woman: medium-dark skin tone, bald', + char: '\u{1F469}\u{1F3FE}\u{200D}\u{1F9B2}', + shortName: 'woman_bald_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc11', + 'old people', + 'diversity', + 'lesbian', + 'women', + 'beautiful', + 'human', + 'parent', + 'wife', + 'mom', + 'shaved head', + 'hairless', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother', + 'balding' + ], + modifiable: true), + Emoji( + name: 'woman: dark skin tone, bald', + char: '\u{1F469}\u{1F3FF}\u{200D}\u{1F9B2}', + shortName: 'woman_bald_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc11', + 'old people', + 'diversity', + 'lesbian', + 'women', + 'beautiful', + 'human', + 'parent', + 'wife', + 'mom', + 'shaved head', + 'hairless', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother', + 'balding' + ], + modifiable: true), + Emoji( + name: 'man: bald', + char: '\u{1F468}\u{200D}\u{1F9B2}', + shortName: 'man_bald', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc11', + 'old people', + 'diversity', + 'men', + 'human', + 'daddy', + 'parent', + 'handsome', + 'husband', + 'shaved head', + 'hairless', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud', + 'balding' + ]), + Emoji( + name: 'man: light skin tone, bald', + char: '\u{1F468}\u{1F3FB}\u{200D}\u{1F9B2}', + shortName: 'man_bald_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc11', + 'old people', + 'diversity', + 'men', + 'human', + 'daddy', + 'parent', + 'handsome', + 'husband', + 'shaved head', + 'hairless', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud', + 'balding' + ], + modifiable: true), + Emoji( + name: 'man: medium-light skin tone, bald', + char: '\u{1F468}\u{1F3FC}\u{200D}\u{1F9B2}', + shortName: 'man_bald_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc11', + 'old people', + 'diversity', + 'men', + 'human', + 'daddy', + 'parent', + 'handsome', + 'husband', + 'shaved head', + 'hairless', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud', + 'balding' + ], + modifiable: true), + Emoji( + name: 'man: medium skin tone, bald', + char: '\u{1F468}\u{1F3FD}\u{200D}\u{1F9B2}', + shortName: 'man_bald_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc11', + 'old people', + 'diversity', + 'men', + 'human', + 'daddy', + 'parent', + 'handsome', + 'husband', + 'shaved head', + 'hairless', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud', + 'balding' + ], + modifiable: true), + Emoji( + name: 'man: medium-dark skin tone, bald', + char: '\u{1F468}\u{1F3FE}\u{200D}\u{1F9B2}', + shortName: 'man_bald_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc11', + 'old people', + 'diversity', + 'men', + 'human', + 'daddy', + 'parent', + 'handsome', + 'husband', + 'shaved head', + 'hairless', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud', + 'balding' + ], + modifiable: true), + Emoji( + name: 'man: dark skin tone, bald', + char: '\u{1F468}\u{1F3FF}\u{200D}\u{1F9B2}', + shortName: 'man_bald_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc11', + 'old people', + 'diversity', + 'men', + 'human', + 'daddy', + 'parent', + 'handsome', + 'husband', + 'shaved head', + 'hairless', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud', + 'balding' + ], + modifiable: true), + Emoji( + name: 'man: beard', + char: '\u{1F9D4}', + shortName: 'bearded_person', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc10', + 'diversity', + 'men', + 'boys night', + 'mustache', + 'human', + 'daddy', + 'parent', + 'handsome', + 'husband', + 'beard', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'guys night', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud' + ]), + Emoji( + name: 'man: light skin tone, beard', + char: '\u{1F9D4}\u{1F3FB}', + shortName: 'bearded_person_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'beard', + 'light skin tone', + 'uc10', + 'diversity', + 'men', + 'boys night', + 'mustache', + 'human', + 'daddy', + 'parent', + 'handsome', + 'husband', + 'beard', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'guys night', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud' + ], + modifiable: true), + Emoji( + name: 'man: medium-light skin tone, beard', + char: '\u{1F9D4}\u{1F3FC}', + shortName: 'bearded_person_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'beard', + 'medium-light skin tone', + 'uc10', + 'diversity', + 'men', + 'boys night', + 'mustache', + 'human', + 'daddy', + 'parent', + 'handsome', + 'husband', + 'beard', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'guys night', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud' + ], + modifiable: true), + Emoji( + name: 'man: medium skin tone, beard', + char: '\u{1F9D4}\u{1F3FD}', + shortName: 'bearded_person_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'beard', + 'medium skin tone', + 'uc10', + 'diversity', + 'men', + 'boys night', + 'mustache', + 'human', + 'daddy', + 'parent', + 'handsome', + 'husband', + 'beard', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'guys night', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud' + ], + modifiable: true), + Emoji( + name: 'man: medium-dark skin tone, beard', + char: '\u{1F9D4}\u{1F3FE}', + shortName: 'bearded_person_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'beard', + 'medium-dark skin tone', + 'uc10', + 'diversity', + 'men', + 'boys night', + 'mustache', + 'human', + 'daddy', + 'parent', + 'handsome', + 'husband', + 'beard', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'guys night', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud' + ], + modifiable: true), + Emoji( + name: 'man: dark skin tone, beard', + char: '\u{1F9D4}\u{1F3FF}', + shortName: 'bearded_person_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'beard', + 'dark skin tone', + 'uc10', + 'diversity', + 'men', + 'boys night', + 'mustache', + 'human', + 'daddy', + 'parent', + 'handsome', + 'husband', + 'beard', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'guys night', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud' + ], + modifiable: true), + Emoji( + name: 'old woman', + char: '\u{1F475}', + shortName: 'older_woman', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'old', + 'woman', + 'uc6', + 'old people', + 'diversity', + 'lesbian', + 'women', + 'vintage', + 'human', + 'cane', + 'parent', + 'wife', + 'mom', + 'grey hair', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother', + 'silver hair' + ]), + Emoji( + name: 'old woman: light skin tone', + char: '\u{1F475}\u{1F3FB}', + shortName: 'older_woman_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'light skin tone', + 'old', + 'woman', + 'uc8', + 'old people', + 'diversity', + 'lesbian', + 'women', + 'vintage', + 'human', + 'cane', + 'parent', + 'wife', + 'mom', + 'grey hair', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother', + 'silver hair' + ], + modifiable: true), + Emoji( + name: 'old woman: medium-light skin tone', + char: '\u{1F475}\u{1F3FC}', + shortName: 'older_woman_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'medium-light skin tone', + 'old', + 'woman', + 'uc8', + 'old people', + 'diversity', + 'lesbian', + 'women', + 'vintage', + 'human', + 'cane', + 'parent', + 'wife', + 'mom', + 'grey hair', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother', + 'silver hair' + ], + modifiable: true), + Emoji( + name: 'old woman: medium skin tone', + char: '\u{1F475}\u{1F3FD}', + shortName: 'older_woman_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'medium skin tone', + 'old', + 'woman', + 'uc8', + 'old people', + 'diversity', + 'lesbian', + 'women', + 'vintage', + 'human', + 'cane', + 'parent', + 'wife', + 'mom', + 'grey hair', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother', + 'silver hair' + ], + modifiable: true), + Emoji( + name: 'old woman: medium-dark skin tone', + char: '\u{1F475}\u{1F3FE}', + shortName: 'older_woman_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'medium-dark skin tone', + 'old', + 'woman', + 'uc8', + 'old people', + 'diversity', + 'lesbian', + 'women', + 'vintage', + 'human', + 'cane', + 'parent', + 'wife', + 'mom', + 'grey hair', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother', + 'silver hair' + ], + modifiable: true), + Emoji( + name: 'old woman: dark skin tone', + char: '\u{1F475}\u{1F3FF}', + shortName: 'older_woman_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'dark skin tone', + 'old', + 'woman', + 'uc8', + 'old people', + 'diversity', + 'lesbian', + 'women', + 'vintage', + 'human', + 'cane', + 'parent', + 'wife', + 'mom', + 'grey hair', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother', + 'silver hair' + ], + modifiable: true), + Emoji( + name: 'older person', + char: '\u{1F9D3}', + shortName: 'older_adult', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'uc10', + 'old people', + 'diversity', + 'lesbian', + 'men', + 'feminist', + 'vintage', + 'human', + 'cane', + 'daddy', + 'parent', + 'handsome', + 'wife', + 'husband', + 'mom', + 'grey hair', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'feminism', + 'strong woman', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud', + 'maman', + 'mommy', + 'mama', + 'mother', + 'silver hair' + ]), + Emoji( + name: 'older person: light skin tone', + char: '\u{1F9D3}\u{1F3FB}', + shortName: 'older_adult_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'gender-neutral', + 'light skin tone', + 'old', + 'uc10', + 'old people', + 'diversity', + 'lesbian', + 'men', + 'feminist', + 'vintage', + 'human', + 'cane', + 'daddy', + 'parent', + 'handsome', + 'wife', + 'husband', + 'mom', + 'grey hair', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'feminism', + 'strong woman', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud', + 'maman', + 'mommy', + 'mama', + 'mother', + 'silver hair' + ], + modifiable: true), + Emoji( + name: 'older person: medium-light skin tone', + char: '\u{1F9D3}\u{1F3FC}', + shortName: 'older_adult_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'gender-neutral', + 'medium-light skin tone', + 'old', + 'uc10', + 'old people', + 'diversity', + 'lesbian', + 'men', + 'feminist', + 'vintage', + 'human', + 'cane', + 'daddy', + 'parent', + 'handsome', + 'wife', + 'husband', + 'mom', + 'grey hair', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'feminism', + 'strong woman', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud', + 'maman', + 'mommy', + 'mama', + 'mother', + 'silver hair' + ], + modifiable: true), + Emoji( + name: 'older person: medium skin tone', + char: '\u{1F9D3}\u{1F3FD}', + shortName: 'older_adult_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'gender-neutral', + 'medium skin tone', + 'old', + 'uc10', + 'old people', + 'diversity', + 'lesbian', + 'men', + 'feminist', + 'vintage', + 'human', + 'cane', + 'daddy', + 'parent', + 'handsome', + 'wife', + 'husband', + 'mom', + 'grey hair', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'feminism', + 'strong woman', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud', + 'maman', + 'mommy', + 'mama', + 'mother', + 'silver hair' + ], + modifiable: true), + Emoji( + name: 'older person: medium-dark skin tone', + char: '\u{1F9D3}\u{1F3FE}', + shortName: 'older_adult_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'gender-neutral', + 'medium-dark skin tone', + 'old', + 'uc10', + 'old people', + 'diversity', + 'lesbian', + 'men', + 'feminist', + 'vintage', + 'human', + 'cane', + 'daddy', + 'parent', + 'handsome', + 'wife', + 'husband', + 'mom', + 'grey hair', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'feminism', + 'strong woman', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud', + 'maman', + 'mommy', + 'mama', + 'mother', + 'silver hair' + ], + modifiable: true), + Emoji( + name: 'older person: dark skin tone', + char: '\u{1F9D3}\u{1F3FF}', + shortName: 'older_adult_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'dark skin tone', + 'gender-neutral', + 'old', + 'uc10', + 'old people', + 'diversity', + 'lesbian', + 'men', + 'feminist', + 'vintage', + 'human', + 'cane', + 'daddy', + 'parent', + 'handsome', + 'wife', + 'husband', + 'mom', + 'grey hair', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'feminism', + 'strong woman', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud', + 'maman', + 'mommy', + 'mama', + 'mother', + 'silver hair' + ], + modifiable: true), + Emoji( + name: 'old man', + char: '\u{1F474}', + shortName: 'older_man', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'man', + 'old', + 'uc6', + 'old people', + 'diversity', + 'men', + 'vintage', + 'human', + 'cane', + 'daddy', + 'parent', + 'handsome', + 'husband', + 'grey hair', + 'hairless', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud', + 'silver hair', + 'balding' + ]), + Emoji( + name: 'old man: light skin tone', + char: '\u{1F474}\u{1F3FB}', + shortName: 'older_man_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'light skin tone', + 'man', + 'old', + 'uc8', + 'old people', + 'diversity', + 'men', + 'vintage', + 'human', + 'cane', + 'daddy', + 'parent', + 'handsome', + 'husband', + 'grey hair', + 'hairless', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud', + 'silver hair', + 'balding' + ], + modifiable: true), + Emoji( + name: 'old man: medium-light skin tone', + char: '\u{1F474}\u{1F3FC}', + shortName: 'older_man_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'man', + 'medium-light skin tone', + 'old', + 'uc8', + 'old people', + 'diversity', + 'men', + 'vintage', + 'human', + 'cane', + 'daddy', + 'parent', + 'handsome', + 'husband', + 'grey hair', + 'hairless', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud', + 'silver hair', + 'balding' + ], + modifiable: true), + Emoji( + name: 'old man: medium skin tone', + char: '\u{1F474}\u{1F3FD}', + shortName: 'older_man_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'man', + 'medium skin tone', + 'old', + 'uc8', + 'old people', + 'diversity', + 'men', + 'vintage', + 'human', + 'cane', + 'daddy', + 'parent', + 'handsome', + 'husband', + 'grey hair', + 'hairless', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud', + 'silver hair', + 'balding' + ], + modifiable: true), + Emoji( + name: 'old man: medium-dark skin tone', + char: '\u{1F474}\u{1F3FE}', + shortName: 'older_man_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'man', + 'medium-dark skin tone', + 'old', + 'uc8', + 'old people', + 'diversity', + 'men', + 'vintage', + 'human', + 'cane', + 'daddy', + 'parent', + 'handsome', + 'husband', + 'grey hair', + 'hairless', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud', + 'silver hair', + 'balding' + ], + modifiable: true), + Emoji( + name: 'old man: dark skin tone', + char: '\u{1F474}\u{1F3FF}', + shortName: 'older_man_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.person, + keywords: [ + 'dark skin tone', + 'man', + 'old', + 'uc8', + 'old people', + 'diversity', + 'men', + 'vintage', + 'human', + 'cane', + 'daddy', + 'parent', + 'handsome', + 'husband', + 'grey hair', + 'hairless', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'stud', + 'silver hair', + 'balding' + ], + modifiable: true), + Emoji( + name: 'person with skullcap', + char: '\u{1F472}', + shortName: 'man_with_chinese_cap', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'gua pi mao', + 'hat', + 'man', + 'uc6', + 'diversity', + 'men', + 'human', + 'chinese', + 'parent', + 'handsome', + 'husband', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'chinois', + 'asian', + 'chine', + 'parents', + 'adult', + 'stud' + ]), + Emoji( + name: 'person with skullcap: light skin tone', + char: '\u{1F472}\u{1F3FB}', + shortName: 'man_with_chinese_cap_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'gua pi mao', + 'hat', + 'light skin tone', + 'man', + 'uc8', + 'diversity', + 'men', + 'human', + 'chinese', + 'parent', + 'handsome', + 'husband', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'chinois', + 'asian', + 'chine', + 'parents', + 'adult', + 'stud' + ], + modifiable: true), + Emoji( + name: 'person with skullcap: medium-light skin tone', + char: '\u{1F472}\u{1F3FC}', + shortName: 'man_with_chinese_cap_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'gua pi mao', + 'hat', + 'man', + 'medium-light skin tone', + 'uc8', + 'diversity', + 'men', + 'human', + 'chinese', + 'parent', + 'handsome', + 'husband', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'chinois', + 'asian', + 'chine', + 'parents', + 'adult', + 'stud' + ], + modifiable: true), + Emoji( + name: 'person with skullcap: medium skin tone', + char: '\u{1F472}\u{1F3FD}', + shortName: 'man_with_chinese_cap_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'gua pi mao', + 'hat', + 'man', + 'medium skin tone', + 'uc8', + 'diversity', + 'men', + 'human', + 'chinese', + 'parent', + 'handsome', + 'husband', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'chinois', + 'asian', + 'chine', + 'parents', + 'adult', + 'stud' + ], + modifiable: true), + Emoji( + name: 'person with skullcap: medium-dark skin tone', + char: '\u{1F472}\u{1F3FE}', + shortName: 'man_with_chinese_cap_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'gua pi mao', + 'hat', + 'man', + 'medium-dark skin tone', + 'uc8', + 'diversity', + 'men', + 'human', + 'chinese', + 'parent', + 'handsome', + 'husband', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'chinois', + 'asian', + 'chine', + 'parents', + 'adult', + 'stud' + ], + modifiable: true), + Emoji( + name: 'person with skullcap: dark skin tone', + char: '\u{1F472}\u{1F3FF}', + shortName: 'man_with_chinese_cap_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'dark skin tone', + 'gua pi mao', + 'hat', + 'man', + 'uc8', + 'diversity', + 'men', + 'human', + 'chinese', + 'parent', + 'handsome', + 'husband', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'chinois', + 'asian', + 'chine', + 'parents', + 'adult', + 'stud' + ], + modifiable: true), + Emoji( + name: 'person wearing turban', + char: '\u{1F473}', + shortName: 'person_wearing_turban', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'turban', + 'uc6', + 'diversity', + 'men', + 'human', + 'disney', + 'daddy', + 'islam', + 'parent', + 'wife', + 'husband', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'cartoon', + 'dad', + 'papa', + 'pere', + 'father', + 'muslim', + 'arab', + 'parents', + 'adult' + ]), + Emoji( + name: 'person wearing turban: light skin tone', + char: '\u{1F473}\u{1F3FB}', + shortName: 'person_wearing_turban_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'light skin tone', + 'turban', + 'uc8', + 'diversity', + 'men', + 'human', + 'disney', + 'daddy', + 'islam', + 'parent', + 'wife', + 'husband', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'cartoon', + 'dad', + 'papa', + 'pere', + 'father', + 'muslim', + 'arab', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'person wearing turban: medium-light skin tone', + char: '\u{1F473}\u{1F3FC}', + shortName: 'person_wearing_turban_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'medium-light skin tone', + 'turban', + 'uc8', + 'diversity', + 'men', + 'human', + 'disney', + 'daddy', + 'islam', + 'parent', + 'wife', + 'husband', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'cartoon', + 'dad', + 'papa', + 'pere', + 'father', + 'muslim', + 'arab', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'person wearing turban: medium skin tone', + char: '\u{1F473}\u{1F3FD}', + shortName: 'person_wearing_turban_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'medium skin tone', + 'turban', + 'uc8', + 'diversity', + 'men', + 'human', + 'disney', + 'daddy', + 'islam', + 'parent', + 'wife', + 'husband', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'cartoon', + 'dad', + 'papa', + 'pere', + 'father', + 'muslim', + 'arab', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'person wearing turban: medium-dark skin tone', + char: '\u{1F473}\u{1F3FE}', + shortName: 'person_wearing_turban_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'medium-dark skin tone', + 'turban', + 'uc8', + 'diversity', + 'men', + 'human', + 'disney', + 'daddy', + 'islam', + 'parent', + 'wife', + 'husband', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'cartoon', + 'dad', + 'papa', + 'pere', + 'father', + 'muslim', + 'arab', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'person wearing turban: dark skin tone', + char: '\u{1F473}\u{1F3FF}', + shortName: 'person_wearing_turban_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'dark skin tone', + 'turban', + 'uc8', + 'diversity', + 'men', + 'human', + 'disney', + 'daddy', + 'islam', + 'parent', + 'wife', + 'husband', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'cartoon', + 'dad', + 'papa', + 'pere', + 'father', + 'muslim', + 'arab', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'woman wearing turban', + char: '\u{1F473}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_wearing_turban', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'turban', + 'woman', + 'uc6', + 'diversity', + 'women', + 'human', + 'islam', + 'parent', + 'wife', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'gender', + 'people', + 'muslim', + 'arab', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ]), + Emoji( + name: 'woman wearing turban: light skin tone', + char: '\u{1F473}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_wearing_turban_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'light skin tone', + 'turban', + 'woman', + 'uc8', + 'diversity', + 'women', + 'human', + 'islam', + 'parent', + 'wife', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'gender', + 'people', + 'muslim', + 'arab', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman wearing turban: medium-light skin tone', + char: '\u{1F473}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_wearing_turban_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'medium-light skin tone', + 'turban', + 'woman', + 'uc8', + 'diversity', + 'women', + 'human', + 'islam', + 'parent', + 'wife', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'gender', + 'people', + 'muslim', + 'arab', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman wearing turban: medium skin tone', + char: '\u{1F473}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_wearing_turban_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'medium skin tone', + 'turban', + 'woman', + 'uc8', + 'diversity', + 'women', + 'human', + 'islam', + 'parent', + 'wife', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'gender', + 'people', + 'muslim', + 'arab', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman wearing turban: medium-dark skin tone', + char: '\u{1F473}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_wearing_turban_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'medium-dark skin tone', + 'turban', + 'woman', + 'uc8', + 'diversity', + 'women', + 'human', + 'islam', + 'parent', + 'wife', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'gender', + 'people', + 'muslim', + 'arab', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman wearing turban: dark skin tone', + char: '\u{1F473}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_wearing_turban_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'dark skin tone', + 'turban', + 'woman', + 'uc8', + 'diversity', + 'women', + 'human', + 'islam', + 'parent', + 'wife', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'gender', + 'people', + 'muslim', + 'arab', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'man wearing turban', + char: '\u{1F473}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_wearing_turban', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'man', + 'turban', + 'uc6', + 'diversity', + 'men', + 'mustache', + 'human', + 'daddy', + 'islam', + 'parent', + 'handsome', + 'husband', + 'beard', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'muslim', + 'arab', + 'parents', + 'adult', + 'stud' + ]), + Emoji( + name: 'man wearing turban: light skin tone', + char: '\u{1F473}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_wearing_turban_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'light skin tone', + 'man', + 'turban', + 'uc8', + 'diversity', + 'men', + 'mustache', + 'human', + 'daddy', + 'islam', + 'parent', + 'handsome', + 'husband', + 'beard', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'muslim', + 'arab', + 'parents', + 'adult', + 'stud' + ], + modifiable: true), + Emoji( + name: 'man wearing turban: medium-light skin tone', + char: '\u{1F473}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_wearing_turban_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'man', + 'medium-light skin tone', + 'turban', + 'uc8', + 'diversity', + 'men', + 'mustache', + 'human', + 'daddy', + 'islam', + 'parent', + 'handsome', + 'husband', + 'beard', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'muslim', + 'arab', + 'parents', + 'adult', + 'stud' + ], + modifiable: true), + Emoji( + name: 'man wearing turban: medium skin tone', + char: '\u{1F473}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_wearing_turban_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'man', + 'medium skin tone', + 'turban', + 'uc8', + 'diversity', + 'men', + 'mustache', + 'human', + 'daddy', + 'islam', + 'parent', + 'handsome', + 'husband', + 'beard', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'muslim', + 'arab', + 'parents', + 'adult', + 'stud' + ], + modifiable: true), + Emoji( + name: 'man wearing turban: medium-dark skin tone', + char: '\u{1F473}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_wearing_turban_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'man', + 'medium-dark skin tone', + 'turban', + 'uc8', + 'diversity', + 'men', + 'mustache', + 'human', + 'daddy', + 'islam', + 'parent', + 'handsome', + 'husband', + 'beard', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'muslim', + 'arab', + 'parents', + 'adult', + 'stud' + ], + modifiable: true), + Emoji( + name: 'man wearing turban: dark skin tone', + char: '\u{1F473}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_wearing_turban_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'dark skin tone', + 'man', + 'turban', + 'uc8', + 'diversity', + 'men', + 'mustache', + 'human', + 'daddy', + 'islam', + 'parent', + 'handsome', + 'husband', + 'beard', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'muslim', + 'arab', + 'parents', + 'adult', + 'stud' + ], + modifiable: true), + Emoji( + name: 'woman with headscarf', + char: '\u{1F9D5}', + shortName: 'woman_with_headscarf', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc10', + 'diversity', + 'women', + 'girls night', + 'human', + 'parent', + 'hijab', + 'wife', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'ladies night', + 'girls only', + 'girlfriend', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ]), + Emoji( + name: 'woman with headscarf: light skin tone', + char: '\u{1F9D5}\u{1F3FB}', + shortName: 'woman_with_headscarf_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'headscarf', + 'hijab', + 'light skin tone', + 'mantilla', + 'tichel', + 'uc10', + 'diversity', + 'women', + 'girls night', + 'human', + 'parent', + 'hijab', + 'wife', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'ladies night', + 'girls only', + 'girlfriend', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman with headscarf: medium-light skin tone', + char: '\u{1F9D5}\u{1F3FC}', + shortName: 'woman_with_headscarf_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'headscarf', + 'hijab', + 'mantilla', + 'medium-light skin tone', + 'tichel', + 'uc10', + 'diversity', + 'women', + 'girls night', + 'human', + 'parent', + 'hijab', + 'wife', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'ladies night', + 'girls only', + 'girlfriend', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman with headscarf: medium skin tone', + char: '\u{1F9D5}\u{1F3FD}', + shortName: 'woman_with_headscarf_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'headscarf', + 'hijab', + 'mantilla', + 'medium skin tone', + 'tichel', + 'uc10', + 'diversity', + 'women', + 'girls night', + 'human', + 'parent', + 'hijab', + 'wife', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'ladies night', + 'girls only', + 'girlfriend', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman with headscarf: medium-dark skin tone', + char: '\u{1F9D5}\u{1F3FE}', + shortName: 'woman_with_headscarf_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'headscarf', + 'hijab', + 'mantilla', + 'medium-dark skin tone', + 'tichel', + 'uc10', + 'diversity', + 'women', + 'girls night', + 'human', + 'parent', + 'hijab', + 'wife', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'ladies night', + 'girls only', + 'girlfriend', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman with headscarf: dark skin tone', + char: '\u{1F9D5}\u{1F3FF}', + shortName: 'woman_with_headscarf_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'dark skin tone', + 'headscarf', + 'hijab', + 'mantilla', + 'tichel', + 'uc10', + 'diversity', + 'women', + 'girls night', + 'human', + 'parent', + 'hijab', + 'wife', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'ladies night', + 'girls only', + 'girlfriend', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'police officer', + char: '\u{1F46E}', + shortName: 'police_officer', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'cop', + 'officer', + 'police', + 'uc6', + 'diversity', + 'job', + 'police', + '911', + 'mustache', + 'power', + 'pig', + 'help', + 'private', + 'mystery', + 'court', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'cop', + 'policeman', + 'popo', + 'prison', + 'handcuff', + 'jail', + 'justice', + 'emergency', + 'injury', + 'pork', + 'прив', + 'privé', + 'privado', + 'reserved' + ]), + Emoji( + name: 'police officer: light skin tone', + char: '\u{1F46E}\u{1F3FB}', + shortName: 'police_officer_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'cop', + 'light skin tone', + 'officer', + 'police', + 'uc8', + 'diversity', + 'job', + 'police', + '911', + 'mustache', + 'power', + 'pig', + 'help', + 'private', + 'mystery', + 'court', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'cop', + 'policeman', + 'popo', + 'prison', + 'handcuff', + 'jail', + 'justice', + 'emergency', + 'injury', + 'pork', + 'прив', + 'privé', + 'privado', + 'reserved' + ], + modifiable: true), + Emoji( + name: 'police officer: medium-light skin tone', + char: '\u{1F46E}\u{1F3FC}', + shortName: 'police_officer_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'cop', + 'medium-light skin tone', + 'officer', + 'police', + 'uc8', + 'diversity', + 'job', + 'police', + '911', + 'mustache', + 'power', + 'pig', + 'help', + 'private', + 'mystery', + 'court', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'cop', + 'policeman', + 'popo', + 'prison', + 'handcuff', + 'jail', + 'justice', + 'emergency', + 'injury', + 'pork', + 'прив', + 'privé', + 'privado', + 'reserved' + ], + modifiable: true), + Emoji( + name: 'police officer: medium skin tone', + char: '\u{1F46E}\u{1F3FD}', + shortName: 'police_officer_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'cop', + 'medium skin tone', + 'officer', + 'police', + 'uc8', + 'diversity', + 'job', + 'police', + '911', + 'mustache', + 'power', + 'pig', + 'help', + 'private', + 'mystery', + 'court', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'cop', + 'policeman', + 'popo', + 'prison', + 'handcuff', + 'jail', + 'justice', + 'emergency', + 'injury', + 'pork', + 'прив', + 'privé', + 'privado', + 'reserved' + ], + modifiable: true), + Emoji( + name: 'police officer: medium-dark skin tone', + char: '\u{1F46E}\u{1F3FE}', + shortName: 'police_officer_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'cop', + 'medium-dark skin tone', + 'officer', + 'police', + 'uc8', + 'diversity', + 'job', + 'police', + '911', + 'mustache', + 'power', + 'pig', + 'help', + 'private', + 'mystery', + 'court', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'cop', + 'policeman', + 'popo', + 'prison', + 'handcuff', + 'jail', + 'justice', + 'emergency', + 'injury', + 'pork', + 'прив', + 'privé', + 'privado', + 'reserved' + ], + modifiable: true), + Emoji( + name: 'police officer: dark skin tone', + char: '\u{1F46E}\u{1F3FF}', + shortName: 'police_officer_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'cop', + 'dark skin tone', + 'officer', + 'police', + 'uc8', + 'diversity', + 'job', + 'police', + '911', + 'mustache', + 'power', + 'pig', + 'help', + 'private', + 'mystery', + 'court', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'cop', + 'policeman', + 'popo', + 'prison', + 'handcuff', + 'jail', + 'justice', + 'emergency', + 'injury', + 'pork', + 'прив', + 'privé', + 'privado', + 'reserved' + ], + modifiable: true), + Emoji( + name: 'woman police officer', + char: '\u{1F46E}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_police_officer', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'cop', + 'officer', + 'police', + 'woman', + 'uc6', + 'diversity', + 'job', + 'police', + '911', + 'power', + 'pig', + 'help', + 'private', + 'mystery', + 'court', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'cop', + 'policeman', + 'popo', + 'prison', + 'handcuff', + 'jail', + 'justice', + 'emergency', + 'injury', + 'pork', + 'прив', + 'privé', + 'privado', + 'reserved' + ]), + Emoji( + name: 'woman police officer: light skin tone', + char: '\u{1F46E}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_police_officer_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'cop', + 'light skin tone', + 'officer', + 'police', + 'woman', + 'uc8', + 'diversity', + 'job', + 'police', + '911', + 'power', + 'pig', + 'help', + 'private', + 'mystery', + 'court', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'cop', + 'policeman', + 'popo', + 'prison', + 'handcuff', + 'jail', + 'justice', + 'emergency', + 'injury', + 'pork', + 'прив', + 'privé', + 'privado', + 'reserved' + ], + modifiable: true), + Emoji( + name: 'woman police officer: medium-light skin tone', + char: '\u{1F46E}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_police_officer_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'cop', + 'medium-light skin tone', + 'officer', + 'police', + 'woman', + 'uc8', + 'diversity', + 'job', + 'police', + '911', + 'power', + 'pig', + 'help', + 'private', + 'mystery', + 'court', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'cop', + 'policeman', + 'popo', + 'prison', + 'handcuff', + 'jail', + 'justice', + 'emergency', + 'injury', + 'pork', + 'прив', + 'privé', + 'privado', + 'reserved' + ], + modifiable: true), + Emoji( + name: 'woman police officer: medium skin tone', + char: '\u{1F46E}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_police_officer_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'cop', + 'medium skin tone', + 'officer', + 'police', + 'woman', + 'uc8', + 'diversity', + 'job', + 'police', + '911', + 'power', + 'pig', + 'help', + 'private', + 'mystery', + 'court', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'cop', + 'policeman', + 'popo', + 'prison', + 'handcuff', + 'jail', + 'justice', + 'emergency', + 'injury', + 'pork', + 'прив', + 'privé', + 'privado', + 'reserved' + ], + modifiable: true), + Emoji( + name: 'woman police officer: medium-dark skin tone', + char: '\u{1F46E}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_police_officer_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'cop', + 'medium-dark skin tone', + 'officer', + 'police', + 'woman', + 'uc8', + 'diversity', + 'job', + 'police', + '911', + 'power', + 'pig', + 'help', + 'private', + 'mystery', + 'court', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'cop', + 'policeman', + 'popo', + 'prison', + 'handcuff', + 'jail', + 'justice', + 'emergency', + 'injury', + 'pork', + 'прив', + 'privé', + 'privado', + 'reserved' + ], + modifiable: true), + Emoji( + name: 'woman police officer: dark skin tone', + char: '\u{1F46E}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_police_officer_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'cop', + 'dark skin tone', + 'officer', + 'police', + 'woman', + 'uc8', + 'diversity', + 'job', + 'police', + '911', + 'power', + 'pig', + 'help', + 'private', + 'mystery', + 'court', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'cop', + 'policeman', + 'popo', + 'prison', + 'handcuff', + 'jail', + 'justice', + 'emergency', + 'injury', + 'pork', + 'прив', + 'privé', + 'privado', + 'reserved' + ], + modifiable: true), + Emoji( + name: 'man police officer', + char: '\u{1F46E}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_police_officer', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'cop', + 'man', + 'officer', + 'police', + 'uc6', + 'diversity', + 'job', + 'police', + '911', + 'mustache', + 'power', + 'pig', + 'help', + 'private', + 'mystery', + 'court', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'cop', + 'policeman', + 'popo', + 'prison', + 'handcuff', + 'jail', + 'justice', + 'emergency', + 'injury', + 'pork', + 'прив', + 'privé', + 'privado', + 'reserved' + ]), + Emoji( + name: 'man police officer: light skin tone', + char: '\u{1F46E}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_police_officer_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'cop', + 'light skin tone', + 'man', + 'officer', + 'police', + 'uc8', + 'diversity', + 'job', + 'police', + '911', + 'mustache', + 'power', + 'pig', + 'help', + 'private', + 'mystery', + 'court', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'cop', + 'policeman', + 'popo', + 'prison', + 'handcuff', + 'jail', + 'justice', + 'emergency', + 'injury', + 'pork', + 'прив', + 'privé', + 'privado', + 'reserved' + ], + modifiable: true), + Emoji( + name: 'man police officer: medium-light skin tone', + char: '\u{1F46E}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_police_officer_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'cop', + 'man', + 'medium-light skin tone', + 'officer', + 'police', + 'uc8', + 'diversity', + 'job', + 'police', + '911', + 'mustache', + 'power', + 'pig', + 'help', + 'private', + 'mystery', + 'court', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'cop', + 'policeman', + 'popo', + 'prison', + 'handcuff', + 'jail', + 'justice', + 'emergency', + 'injury', + 'pork', + 'прив', + 'privé', + 'privado', + 'reserved' + ], + modifiable: true), + Emoji( + name: 'man police officer: medium skin tone', + char: '\u{1F46E}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_police_officer_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'cop', + 'man', + 'medium skin tone', + 'officer', + 'police', + 'uc8', + 'diversity', + 'job', + 'police', + '911', + 'mustache', + 'power', + 'pig', + 'help', + 'private', + 'mystery', + 'court', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'cop', + 'policeman', + 'popo', + 'prison', + 'handcuff', + 'jail', + 'justice', + 'emergency', + 'injury', + 'pork', + 'прив', + 'privé', + 'privado', + 'reserved' + ], + modifiable: true), + Emoji( + name: 'man police officer: medium-dark skin tone', + char: '\u{1F46E}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_police_officer_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'cop', + 'man', + 'medium-dark skin tone', + 'officer', + 'police', + 'uc8', + 'diversity', + 'job', + 'police', + '911', + 'mustache', + 'power', + 'pig', + 'help', + 'private', + 'mystery', + 'court', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'cop', + 'policeman', + 'popo', + 'prison', + 'handcuff', + 'jail', + 'justice', + 'emergency', + 'injury', + 'pork', + 'прив', + 'privé', + 'privado', + 'reserved' + ], + modifiable: true), + Emoji( + name: 'man police officer: dark skin tone', + char: '\u{1F46E}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_police_officer_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'cop', + 'dark skin tone', + 'man', + 'officer', + 'police', + 'uc8', + 'diversity', + 'job', + 'police', + '911', + 'mustache', + 'power', + 'pig', + 'help', + 'private', + 'mystery', + 'court', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'cop', + 'policeman', + 'popo', + 'prison', + 'handcuff', + 'jail', + 'justice', + 'emergency', + 'injury', + 'pork', + 'прив', + 'privé', + 'privado', + 'reserved' + ], + modifiable: true), + Emoji( + name: 'construction worker', + char: '\u{1F477}', + shortName: 'construction_worker', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'construction', + 'hat', + 'worker', + 'uc6', + 'diversity', + 'job', + 'build', + 'construction', + 'helmet', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career' + ]), + Emoji( + name: 'construction worker: light skin tone', + char: '\u{1F477}\u{1F3FB}', + shortName: 'construction_worker_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'construction', + 'hat', + 'light skin tone', + 'worker', + 'uc8', + 'diversity', + 'job', + 'build', + 'construction', + 'helmet', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'construction worker: medium-light skin tone', + char: '\u{1F477}\u{1F3FC}', + shortName: 'construction_worker_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'construction', + 'hat', + 'medium-light skin tone', + 'worker', + 'uc8', + 'diversity', + 'job', + 'build', + 'construction', + 'helmet', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'construction worker: medium skin tone', + char: '\u{1F477}\u{1F3FD}', + shortName: 'construction_worker_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'construction', + 'hat', + 'medium skin tone', + 'worker', + 'uc8', + 'diversity', + 'job', + 'build', + 'construction', + 'helmet', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'construction worker: medium-dark skin tone', + char: '\u{1F477}\u{1F3FE}', + shortName: 'construction_worker_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'construction', + 'hat', + 'medium-dark skin tone', + 'worker', + 'uc8', + 'diversity', + 'job', + 'build', + 'construction', + 'helmet', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'construction worker: dark skin tone', + char: '\u{1F477}\u{1F3FF}', + shortName: 'construction_worker_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'construction', + 'dark skin tone', + 'hat', + 'worker', + 'uc8', + 'diversity', + 'job', + 'build', + 'construction', + 'helmet', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'woman construction worker', + char: '\u{1F477}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_construction_worker', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'construction', + 'woman', + 'worker', + 'uc6', + 'diversity', + 'job', + 'build', + 'construction', + 'helmet', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career' + ]), + Emoji( + name: 'woman construction worker: light skin tone', + char: '\u{1F477}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_construction_worker_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'construction', + 'light skin tone', + 'woman', + 'worker', + 'uc8', + 'diversity', + 'job', + 'build', + 'construction', + 'helmet', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'woman construction worker: medium-light skin tone', + char: '\u{1F477}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_construction_worker_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'construction', + 'medium-light skin tone', + 'woman', + 'worker', + 'uc8', + 'diversity', + 'job', + 'build', + 'construction', + 'helmet', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'woman construction worker: medium skin tone', + char: '\u{1F477}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_construction_worker_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'construction', + 'medium skin tone', + 'woman', + 'worker', + 'uc8', + 'diversity', + 'job', + 'build', + 'construction', + 'helmet', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'woman construction worker: medium-dark skin tone', + char: '\u{1F477}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_construction_worker_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'construction', + 'medium-dark skin tone', + 'woman', + 'worker', + 'uc8', + 'diversity', + 'job', + 'build', + 'construction', + 'helmet', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'woman construction worker: dark skin tone', + char: '\u{1F477}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_construction_worker_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'construction', + 'dark skin tone', + 'woman', + 'worker', + 'uc8', + 'diversity', + 'job', + 'build', + 'construction', + 'helmet', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'man construction worker', + char: '\u{1F477}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_construction_worker', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'construction', + 'man', + 'worker', + 'uc6', + 'diversity', + 'job', + 'build', + 'construction', + 'helmet', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career' + ]), + Emoji( + name: 'man construction worker: light skin tone', + char: '\u{1F477}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_construction_worker_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'construction', + 'light skin tone', + 'man', + 'worker', + 'uc8', + 'diversity', + 'job', + 'build', + 'construction', + 'helmet', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'man construction worker: medium-light skin tone', + char: '\u{1F477}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_construction_worker_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'construction', + 'man', + 'medium-light skin tone', + 'worker', + 'uc8', + 'diversity', + 'job', + 'build', + 'construction', + 'helmet', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'man construction worker: medium skin tone', + char: '\u{1F477}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_construction_worker_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'construction', + 'man', + 'medium skin tone', + 'worker', + 'uc8', + 'diversity', + 'job', + 'build', + 'construction', + 'helmet', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'man construction worker: medium-dark skin tone', + char: '\u{1F477}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_construction_worker_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'construction', + 'man', + 'medium-dark skin tone', + 'worker', + 'uc8', + 'diversity', + 'job', + 'build', + 'construction', + 'helmet', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'man construction worker: dark skin tone', + char: '\u{1F477}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_construction_worker_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'construction', + 'dark skin tone', + 'man', + 'worker', + 'uc8', + 'diversity', + 'job', + 'build', + 'construction', + 'helmet', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'guard', + char: '\u{1F482}', + shortName: 'guard', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'guard', + 'uc6', + 'diversity', + 'job', + 'queen', + 'england', + 'private', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'king', + 'prince', + 'princess', + 'united kingdom', + 'london', + 'uk', + 'прив', + 'privé', + 'privado', + 'reserved' + ]), + Emoji( + name: 'guard: light skin tone', + char: '\u{1F482}\u{1F3FB}', + shortName: 'guard_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'guard', + 'light skin tone', + 'uc8', + 'diversity', + 'job', + 'queen', + 'england', + 'private', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'king', + 'prince', + 'princess', + 'united kingdom', + 'london', + 'uk', + 'прив', + 'privé', + 'privado', + 'reserved' + ], + modifiable: true), + Emoji( + name: 'guard: medium-light skin tone', + char: '\u{1F482}\u{1F3FC}', + shortName: 'guard_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'guard', + 'medium-light skin tone', + 'uc8', + 'diversity', + 'job', + 'queen', + 'england', + 'private', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'king', + 'prince', + 'princess', + 'united kingdom', + 'london', + 'uk', + 'прив', + 'privé', + 'privado', + 'reserved' + ], + modifiable: true), + Emoji( + name: 'guard: medium skin tone', + char: '\u{1F482}\u{1F3FD}', + shortName: 'guard_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'guard', + 'medium skin tone', + 'uc8', + 'diversity', + 'job', + 'queen', + 'england', + 'private', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'king', + 'prince', + 'princess', + 'united kingdom', + 'london', + 'uk', + 'прив', + 'privé', + 'privado', + 'reserved' + ], + modifiable: true), + Emoji( + name: 'guard: medium-dark skin tone', + char: '\u{1F482}\u{1F3FE}', + shortName: 'guard_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'guard', + 'medium-dark skin tone', + 'uc8', + 'diversity', + 'job', + 'queen', + 'england', + 'private', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'king', + 'prince', + 'princess', + 'united kingdom', + 'london', + 'uk', + 'прив', + 'privé', + 'privado', + 'reserved' + ], + modifiable: true), + Emoji( + name: 'guard: dark skin tone', + char: '\u{1F482}\u{1F3FF}', + shortName: 'guard_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'dark skin tone', + 'guard', + 'uc8', + 'diversity', + 'job', + 'queen', + 'england', + 'private', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'king', + 'prince', + 'princess', + 'united kingdom', + 'london', + 'uk', + 'прив', + 'privé', + 'privado', + 'reserved' + ], + modifiable: true), + Emoji( + name: 'woman guard', + char: '\u{1F482}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_guard', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'guard', + 'woman', + 'uc6', + 'diversity', + 'job', + 'queen', + 'england', + 'private', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'king', + 'prince', + 'princess', + 'united kingdom', + 'london', + 'uk', + 'прив', + 'privé', + 'privado', + 'reserved' + ]), + Emoji( + name: 'woman guard: light skin tone', + char: '\u{1F482}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_guard_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'guard', + 'light skin tone', + 'woman', + 'uc8', + 'diversity', + 'job', + 'queen', + 'england', + 'private', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'king', + 'prince', + 'princess', + 'united kingdom', + 'london', + 'uk', + 'прив', + 'privé', + 'privado', + 'reserved' + ], + modifiable: true), + Emoji( + name: 'woman guard: medium-light skin tone', + char: '\u{1F482}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_guard_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'guard', + 'medium-light skin tone', + 'woman', + 'uc8', + 'diversity', + 'job', + 'queen', + 'england', + 'private', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'king', + 'prince', + 'princess', + 'united kingdom', + 'london', + 'uk', + 'прив', + 'privé', + 'privado', + 'reserved' + ], + modifiable: true), + Emoji( + name: 'woman guard: medium skin tone', + char: '\u{1F482}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_guard_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'guard', + 'medium skin tone', + 'woman', + 'uc8', + 'diversity', + 'job', + 'queen', + 'england', + 'private', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'king', + 'prince', + 'princess', + 'united kingdom', + 'london', + 'uk', + 'прив', + 'privé', + 'privado', + 'reserved' + ], + modifiable: true), + Emoji( + name: 'woman guard: medium-dark skin tone', + char: '\u{1F482}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_guard_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'guard', + 'medium-dark skin tone', + 'woman', + 'uc8', + 'diversity', + 'job', + 'queen', + 'england', + 'private', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'king', + 'prince', + 'princess', + 'united kingdom', + 'london', + 'uk', + 'прив', + 'privé', + 'privado', + 'reserved' + ], + modifiable: true), + Emoji( + name: 'woman guard: dark skin tone', + char: '\u{1F482}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_guard_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'dark skin tone', + 'guard', + 'woman', + 'uc8', + 'diversity', + 'job', + 'queen', + 'england', + 'private', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'king', + 'prince', + 'princess', + 'united kingdom', + 'london', + 'uk', + 'прив', + 'privé', + 'privado', + 'reserved' + ], + modifiable: true), + Emoji( + name: 'man guard', + char: '\u{1F482}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_guard', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'guard', + 'man', + 'uc6', + 'diversity', + 'job', + 'queen', + 'england', + 'private', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'king', + 'prince', + 'princess', + 'united kingdom', + 'london', + 'uk', + 'прив', + 'privé', + 'privado', + 'reserved' + ]), + Emoji( + name: 'man guard: light skin tone', + char: '\u{1F482}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_guard_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'guard', + 'light skin tone', + 'man', + 'uc8', + 'diversity', + 'job', + 'queen', + 'england', + 'private', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'king', + 'prince', + 'princess', + 'united kingdom', + 'london', + 'uk', + 'прив', + 'privé', + 'privado', + 'reserved' + ], + modifiable: true), + Emoji( + name: 'man guard: medium-light skin tone', + char: '\u{1F482}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_guard_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'guard', + 'man', + 'medium-light skin tone', + 'uc8', + 'diversity', + 'job', + 'queen', + 'england', + 'private', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'king', + 'prince', + 'princess', + 'united kingdom', + 'london', + 'uk', + 'прив', + 'privé', + 'privado', + 'reserved' + ], + modifiable: true), + Emoji( + name: 'man guard: medium skin tone', + char: '\u{1F482}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_guard_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'guard', + 'man', + 'medium skin tone', + 'uc8', + 'diversity', + 'job', + 'queen', + 'england', + 'private', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'king', + 'prince', + 'princess', + 'united kingdom', + 'london', + 'uk', + 'прив', + 'privé', + 'privado', + 'reserved' + ], + modifiable: true), + Emoji( + name: 'man guard: medium-dark skin tone', + char: '\u{1F482}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_guard_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'guard', + 'man', + 'medium-dark skin tone', + 'uc8', + 'diversity', + 'job', + 'queen', + 'england', + 'private', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'king', + 'prince', + 'princess', + 'united kingdom', + 'london', + 'uk', + 'прив', + 'privé', + 'privado', + 'reserved' + ], + modifiable: true), + Emoji( + name: 'man guard: dark skin tone', + char: '\u{1F482}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_guard_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'dark skin tone', + 'guard', + 'man', + 'uc8', + 'diversity', + 'job', + 'queen', + 'england', + 'private', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'king', + 'prince', + 'princess', + 'united kingdom', + 'london', + 'uk', + 'прив', + 'privé', + 'privado', + 'reserved' + ], + modifiable: true), + Emoji( + name: 'detective', + char: '\u{1F575}', + shortName: 'detective', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'detective', + 'sleuth', + 'spy', + 'uc7', + 'diversity', + 'glasses', + 'halloween', + 'job', + 'google', + 'search', + 'detective', + 'super hero', + 'private', + 'mystery', + 'clever', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'eyeglasses', + 'eye glasses', + 'samhain', + 'profession', + 'boss', + 'career', + 'look', + 'find', + 'looking', + 'see', + 'superhero', + 'superman', + 'batman', + 'прив', + 'privé', + 'privado', + 'reserved', + 'witty' + ]), + Emoji( + name: 'detective: light skin tone', + char: '\u{1F575}\u{1F3FB}', + shortName: 'detective_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'detective', + 'light skin tone', + 'sleuth', + 'spy', + 'uc8', + 'diversity', + 'glasses', + 'halloween', + 'job', + 'google', + 'search', + 'detective', + 'super hero', + 'private', + 'mystery', + 'clever', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'eyeglasses', + 'eye glasses', + 'samhain', + 'profession', + 'boss', + 'career', + 'look', + 'find', + 'looking', + 'see', + 'superhero', + 'superman', + 'batman', + 'прив', + 'privé', + 'privado', + 'reserved', + 'witty' + ], + modifiable: true), + Emoji( + name: 'detective: medium-light skin tone', + char: '\u{1F575}\u{1F3FC}', + shortName: 'detective_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'detective', + 'medium-light skin tone', + 'sleuth', + 'spy', + 'uc8', + 'diversity', + 'glasses', + 'halloween', + 'job', + 'google', + 'search', + 'detective', + 'super hero', + 'private', + 'mystery', + 'clever', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'eyeglasses', + 'eye glasses', + 'samhain', + 'profession', + 'boss', + 'career', + 'look', + 'find', + 'looking', + 'see', + 'superhero', + 'superman', + 'batman', + 'прив', + 'privé', + 'privado', + 'reserved', + 'witty' + ], + modifiable: true), + Emoji( + name: 'detective: medium skin tone', + char: '\u{1F575}\u{1F3FD}', + shortName: 'detective_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'detective', + 'medium skin tone', + 'sleuth', + 'spy', + 'uc8', + 'diversity', + 'glasses', + 'halloween', + 'job', + 'google', + 'search', + 'detective', + 'super hero', + 'private', + 'mystery', + 'clever', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'eyeglasses', + 'eye glasses', + 'samhain', + 'profession', + 'boss', + 'career', + 'look', + 'find', + 'looking', + 'see', + 'superhero', + 'superman', + 'batman', + 'прив', + 'privé', + 'privado', + 'reserved', + 'witty' + ], + modifiable: true), + Emoji( + name: 'detective: medium-dark skin tone', + char: '\u{1F575}\u{1F3FE}', + shortName: 'detective_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'detective', + 'medium-dark skin tone', + 'sleuth', + 'spy', + 'uc8', + 'diversity', + 'glasses', + 'halloween', + 'job', + 'google', + 'search', + 'detective', + 'super hero', + 'private', + 'mystery', + 'clever', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'eyeglasses', + 'eye glasses', + 'samhain', + 'profession', + 'boss', + 'career', + 'look', + 'find', + 'looking', + 'see', + 'superhero', + 'superman', + 'batman', + 'прив', + 'privé', + 'privado', + 'reserved', + 'witty' + ], + modifiable: true), + Emoji( + name: 'detective: dark skin tone', + char: '\u{1F575}\u{1F3FF}', + shortName: 'detective_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'dark skin tone', + 'detective', + 'sleuth', + 'spy', + 'uc8', + 'diversity', + 'glasses', + 'halloween', + 'job', + 'google', + 'search', + 'detective', + 'super hero', + 'private', + 'mystery', + 'clever', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'eyeglasses', + 'eye glasses', + 'samhain', + 'profession', + 'boss', + 'career', + 'look', + 'find', + 'looking', + 'see', + 'superhero', + 'superman', + 'batman', + 'прив', + 'privé', + 'privado', + 'reserved', + 'witty' + ], + modifiable: true), + Emoji( + name: 'woman detective', + char: '\u{1F575}\u{FE0F}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_detective', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'detective', + 'sleuth', + 'spy', + 'woman', + 'uc7', + 'diversity', + 'halloween', + 'job', + 'search', + 'detective', + 'super hero', + 'private', + 'mystery', + 'clever', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'profession', + 'boss', + 'career', + 'look', + 'find', + 'looking', + 'see', + 'superhero', + 'superman', + 'batman', + 'прив', + 'privé', + 'privado', + 'reserved', + 'witty' + ]), + Emoji( + name: 'woman detective: light skin tone', + char: '\u{1F575}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_detective_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'detective', + 'light skin tone', + 'sleuth', + 'spy', + 'woman', + 'uc8', + 'diversity', + 'halloween', + 'job', + 'search', + 'detective', + 'super hero', + 'private', + 'mystery', + 'clever', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'profession', + 'boss', + 'career', + 'look', + 'find', + 'looking', + 'see', + 'superhero', + 'superman', + 'batman', + 'прив', + 'privé', + 'privado', + 'reserved', + 'witty' + ], + modifiable: true), + Emoji( + name: 'woman detective: medium-light skin tone', + char: '\u{1F575}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_detective_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'detective', + 'medium-light skin tone', + 'sleuth', + 'spy', + 'woman', + 'uc8', + 'diversity', + 'halloween', + 'job', + 'search', + 'detective', + 'super hero', + 'private', + 'mystery', + 'clever', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'profession', + 'boss', + 'career', + 'look', + 'find', + 'looking', + 'see', + 'superhero', + 'superman', + 'batman', + 'прив', + 'privé', + 'privado', + 'reserved', + 'witty' + ], + modifiable: true), + Emoji( + name: 'woman detective: medium skin tone', + char: '\u{1F575}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_detective_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'detective', + 'medium skin tone', + 'sleuth', + 'spy', + 'woman', + 'uc8', + 'diversity', + 'halloween', + 'job', + 'search', + 'detective', + 'super hero', + 'private', + 'mystery', + 'clever', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'profession', + 'boss', + 'career', + 'look', + 'find', + 'looking', + 'see', + 'superhero', + 'superman', + 'batman', + 'прив', + 'privé', + 'privado', + 'reserved', + 'witty' + ], + modifiable: true), + Emoji( + name: 'woman detective: medium-dark skin tone', + char: '\u{1F575}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_detective_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'detective', + 'medium-dark skin tone', + 'sleuth', + 'spy', + 'woman', + 'uc8', + 'diversity', + 'halloween', + 'job', + 'search', + 'detective', + 'super hero', + 'private', + 'mystery', + 'clever', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'profession', + 'boss', + 'career', + 'look', + 'find', + 'looking', + 'see', + 'superhero', + 'superman', + 'batman', + 'прив', + 'privé', + 'privado', + 'reserved', + 'witty' + ], + modifiable: true), + Emoji( + name: 'woman detective: dark skin tone', + char: '\u{1F575}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_detective_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'dark skin tone', + 'detective', + 'sleuth', + 'spy', + 'woman', + 'uc8', + 'diversity', + 'halloween', + 'job', + 'search', + 'detective', + 'super hero', + 'private', + 'mystery', + 'clever', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'profession', + 'boss', + 'career', + 'look', + 'find', + 'looking', + 'see', + 'superhero', + 'superman', + 'batman', + 'прив', + 'privé', + 'privado', + 'reserved', + 'witty' + ], + modifiable: true), + Emoji( + name: 'man detective', + char: '\u{1F575}\u{FE0F}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_detective', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'detective', + 'man', + 'sleuth', + 'spy', + 'uc7', + 'diversity', + 'halloween', + 'job', + 'search', + 'detective', + 'super hero', + 'private', + 'mystery', + 'clever', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'profession', + 'boss', + 'career', + 'look', + 'find', + 'looking', + 'see', + 'superhero', + 'superman', + 'batman', + 'прив', + 'privé', + 'privado', + 'reserved', + 'witty' + ]), + Emoji( + name: 'man detective: light skin tone', + char: '\u{1F575}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_detective_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'detective', + 'light skin tone', + 'man', + 'sleuth', + 'spy', + 'uc8', + 'diversity', + 'halloween', + 'job', + 'search', + 'detective', + 'super hero', + 'private', + 'mystery', + 'clever', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'profession', + 'boss', + 'career', + 'look', + 'find', + 'looking', + 'see', + 'superhero', + 'superman', + 'batman', + 'прив', + 'privé', + 'privado', + 'reserved', + 'witty' + ], + modifiable: true), + Emoji( + name: 'man detective: medium-light skin tone', + char: '\u{1F575}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_detective_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'detective', + 'man', + 'medium-light skin tone', + 'sleuth', + 'spy', + 'uc8', + 'diversity', + 'halloween', + 'job', + 'search', + 'detective', + 'super hero', + 'private', + 'mystery', + 'clever', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'profession', + 'boss', + 'career', + 'look', + 'find', + 'looking', + 'see', + 'superhero', + 'superman', + 'batman', + 'прив', + 'privé', + 'privado', + 'reserved', + 'witty' + ], + modifiable: true), + Emoji( + name: 'man detective: medium skin tone', + char: '\u{1F575}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_detective_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'detective', + 'man', + 'medium skin tone', + 'sleuth', + 'spy', + 'uc8', + 'diversity', + 'halloween', + 'job', + 'search', + 'detective', + 'super hero', + 'private', + 'mystery', + 'clever', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'profession', + 'boss', + 'career', + 'look', + 'find', + 'looking', + 'see', + 'superhero', + 'superman', + 'batman', + 'прив', + 'privé', + 'privado', + 'reserved', + 'witty' + ], + modifiable: true), + Emoji( + name: 'man detective: medium-dark skin tone', + char: '\u{1F575}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_detective_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'detective', + 'man', + 'medium-dark skin tone', + 'sleuth', + 'spy', + 'uc8', + 'diversity', + 'halloween', + 'job', + 'search', + 'detective', + 'super hero', + 'private', + 'mystery', + 'clever', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'profession', + 'boss', + 'career', + 'look', + 'find', + 'looking', + 'see', + 'superhero', + 'superman', + 'batman', + 'прив', + 'privé', + 'privado', + 'reserved', + 'witty' + ], + modifiable: true), + Emoji( + name: 'man detective: dark skin tone', + char: '\u{1F575}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_detective_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'dark skin tone', + 'detective', + 'man', + 'sleuth', + 'spy', + 'uc8', + 'diversity', + 'halloween', + 'job', + 'search', + 'detective', + 'super hero', + 'private', + 'mystery', + 'clever', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'profession', + 'boss', + 'career', + 'look', + 'find', + 'looking', + 'see', + 'superhero', + 'superman', + 'batman', + 'прив', + 'privé', + 'privado', + 'reserved', + 'witty' + ], + modifiable: true), + Emoji( + name: 'health worker', + char: '\u{1F9D1}\u{200D}\u{2695}\u{FE0F}', + shortName: 'health_worker', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'health', + 'sick', + 'job', + '911', + 'nerd', + 'nurse', + 'help', + 'disguise', + 'medical', + 'medicine', + 'doctor', + 'barf', + 'vomit', + 'throw up', + 'puke', + 'get well', + 'cough', + 'puking', + 'barfing', + 'malade', + 'spew', + 'profession', + 'boss', + 'career', + 'emergency', + 'injury', + 'smart', + 'geek', + 'serious' + ]), + Emoji( + name: 'health worker: light skin tone', + char: '\u{1F9D1}\u{1F3FB}\u{200D}\u{2695}\u{FE0F}', + shortName: 'health_worker_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'health', + 'sick', + 'job', + '911', + 'nerd', + 'nurse', + 'help', + 'disguise', + 'medical', + 'medicine', + 'doctor', + 'barf', + 'vomit', + 'throw up', + 'puke', + 'get well', + 'cough', + 'puking', + 'barfing', + 'malade', + 'spew', + 'profession', + 'boss', + 'career', + 'emergency', + 'injury', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'health worker: medium-light skin tone', + char: '\u{1F9D1}\u{1F3FC}\u{200D}\u{2695}\u{FE0F}', + shortName: 'health_worker_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'health', + 'sick', + 'job', + '911', + 'nerd', + 'nurse', + 'help', + 'disguise', + 'medical', + 'medicine', + 'doctor', + 'barf', + 'vomit', + 'throw up', + 'puke', + 'get well', + 'cough', + 'puking', + 'barfing', + 'malade', + 'spew', + 'profession', + 'boss', + 'career', + 'emergency', + 'injury', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'health worker: medium skin tone', + char: '\u{1F9D1}\u{1F3FD}\u{200D}\u{2695}\u{FE0F}', + shortName: 'health_worker_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'health', + 'sick', + 'job', + '911', + 'nerd', + 'nurse', + 'help', + 'disguise', + 'medical', + 'medicine', + 'doctor', + 'barf', + 'vomit', + 'throw up', + 'puke', + 'get well', + 'cough', + 'puking', + 'barfing', + 'malade', + 'spew', + 'profession', + 'boss', + 'career', + 'emergency', + 'injury', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'health worker: medium-dark skin tone', + char: '\u{1F9D1}\u{1F3FE}\u{200D}\u{2695}\u{FE0F}', + shortName: 'health_worker_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'health', + 'sick', + 'job', + '911', + 'nerd', + 'nurse', + 'help', + 'disguise', + 'medical', + 'medicine', + 'doctor', + 'barf', + 'vomit', + 'throw up', + 'puke', + 'get well', + 'cough', + 'puking', + 'barfing', + 'malade', + 'spew', + 'profession', + 'boss', + 'career', + 'emergency', + 'injury', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'health worker: dark skin tone', + char: '\u{1F9D1}\u{1F3FF}\u{200D}\u{2695}\u{FE0F}', + shortName: 'health_worker_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'health', + 'sick', + 'job', + '911', + 'nerd', + 'nurse', + 'help', + 'disguise', + 'medical', + 'medicine', + 'doctor', + 'barf', + 'vomit', + 'throw up', + 'puke', + 'get well', + 'cough', + 'puking', + 'barfing', + 'malade', + 'spew', + 'profession', + 'boss', + 'career', + 'emergency', + 'injury', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'woman health worker', + char: '\u{1F469}\u{200D}\u{2695}\u{FE0F}', + shortName: 'woman_health_worker', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'doctor', + 'healthcare', + 'nurse', + 'therapist', + 'woman', + 'uc6', + 'diversity', + 'health', + 'sick', + 'job', + '911', + 'nerd', + 'nurse', + 'help', + 'disguise', + 'medical', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'medicine', + 'doctor', + 'barf', + 'vomit', + 'throw up', + 'puke', + 'get well', + 'cough', + 'puking', + 'barfing', + 'malade', + 'spew', + 'profession', + 'boss', + 'career', + 'emergency', + 'injury', + 'smart', + 'geek', + 'serious' + ]), + Emoji( + name: 'woman health worker: light skin tone', + char: '\u{1F469}\u{1F3FB}\u{200D}\u{2695}\u{FE0F}', + shortName: 'woman_health_worker_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'doctor', + 'healthcare', + 'light skin tone', + 'nurse', + 'therapist', + 'woman', + 'uc8', + 'diversity', + 'health', + 'sick', + 'job', + '911', + 'nerd', + 'nurse', + 'help', + 'disguise', + 'medical', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'medicine', + 'doctor', + 'barf', + 'vomit', + 'throw up', + 'puke', + 'get well', + 'cough', + 'puking', + 'barfing', + 'malade', + 'spew', + 'profession', + 'boss', + 'career', + 'emergency', + 'injury', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'woman health worker: medium-light skin tone', + char: '\u{1F469}\u{1F3FC}\u{200D}\u{2695}\u{FE0F}', + shortName: 'woman_health_worker_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'doctor', + 'healthcare', + 'medium-light skin tone', + 'nurse', + 'therapist', + 'woman', + 'uc8', + 'diversity', + 'health', + 'sick', + 'job', + '911', + 'nerd', + 'nurse', + 'help', + 'disguise', + 'medical', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'medicine', + 'doctor', + 'barf', + 'vomit', + 'throw up', + 'puke', + 'get well', + 'cough', + 'puking', + 'barfing', + 'malade', + 'spew', + 'profession', + 'boss', + 'career', + 'emergency', + 'injury', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'woman health worker: medium skin tone', + char: '\u{1F469}\u{1F3FD}\u{200D}\u{2695}\u{FE0F}', + shortName: 'woman_health_worker_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'doctor', + 'healthcare', + 'medium skin tone', + 'nurse', + 'therapist', + 'woman', + 'uc8', + 'diversity', + 'health', + 'sick', + 'job', + '911', + 'nerd', + 'nurse', + 'help', + 'disguise', + 'medical', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'medicine', + 'doctor', + 'barf', + 'vomit', + 'throw up', + 'puke', + 'get well', + 'cough', + 'puking', + 'barfing', + 'malade', + 'spew', + 'profession', + 'boss', + 'career', + 'emergency', + 'injury', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'woman health worker: medium-dark skin tone', + char: '\u{1F469}\u{1F3FE}\u{200D}\u{2695}\u{FE0F}', + shortName: 'woman_health_worker_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'doctor', + 'healthcare', + 'medium-dark skin tone', + 'nurse', + 'therapist', + 'woman', + 'uc8', + 'diversity', + 'health', + 'sick', + 'job', + '911', + 'nerd', + 'nurse', + 'help', + 'disguise', + 'medical', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'medicine', + 'doctor', + 'barf', + 'vomit', + 'throw up', + 'puke', + 'get well', + 'cough', + 'puking', + 'barfing', + 'malade', + 'spew', + 'profession', + 'boss', + 'career', + 'emergency', + 'injury', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'woman health worker: dark skin tone', + char: '\u{1F469}\u{1F3FF}\u{200D}\u{2695}\u{FE0F}', + shortName: 'woman_health_worker_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'dark skin tone', + 'doctor', + 'healthcare', + 'nurse', + 'therapist', + 'woman', + 'uc8', + 'diversity', + 'health', + 'sick', + 'job', + '911', + 'nerd', + 'nurse', + 'help', + 'disguise', + 'medical', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'medicine', + 'doctor', + 'barf', + 'vomit', + 'throw up', + 'puke', + 'get well', + 'cough', + 'puking', + 'barfing', + 'malade', + 'spew', + 'profession', + 'boss', + 'career', + 'emergency', + 'injury', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'man health worker', + char: '\u{1F468}\u{200D}\u{2695}\u{FE0F}', + shortName: 'man_health_worker', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'doctor', + 'healthcare', + 'man', + 'nurse', + 'therapist', + 'uc6', + 'diversity', + 'health', + 'sick', + 'job', + '911', + 'nerd', + 'nurse', + 'help', + 'disguise', + 'medical', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'medicine', + 'doctor', + 'barf', + 'vomit', + 'throw up', + 'puke', + 'get well', + 'cough', + 'puking', + 'barfing', + 'malade', + 'spew', + 'profession', + 'boss', + 'career', + 'emergency', + 'injury', + 'smart', + 'geek', + 'serious' + ]), + Emoji( + name: 'man health worker: light skin tone', + char: '\u{1F468}\u{1F3FB}\u{200D}\u{2695}\u{FE0F}', + shortName: 'man_health_worker_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'doctor', + 'healthcare', + 'light skin tone', + 'man', + 'nurse', + 'therapist', + 'uc8', + 'diversity', + 'health', + 'sick', + 'job', + '911', + 'nerd', + 'nurse', + 'help', + 'disguise', + 'medical', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'medicine', + 'doctor', + 'barf', + 'vomit', + 'throw up', + 'puke', + 'get well', + 'cough', + 'puking', + 'barfing', + 'malade', + 'spew', + 'profession', + 'boss', + 'career', + 'emergency', + 'injury', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'man health worker: medium-light skin tone', + char: '\u{1F468}\u{1F3FC}\u{200D}\u{2695}\u{FE0F}', + shortName: 'man_health_worker_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'doctor', + 'healthcare', + 'man', + 'medium-light skin tone', + 'nurse', + 'therapist', + 'uc8', + 'diversity', + 'health', + 'sick', + 'job', + '911', + 'nerd', + 'nurse', + 'help', + 'disguise', + 'medical', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'medicine', + 'doctor', + 'barf', + 'vomit', + 'throw up', + 'puke', + 'get well', + 'cough', + 'puking', + 'barfing', + 'malade', + 'spew', + 'profession', + 'boss', + 'career', + 'emergency', + 'injury', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'man health worker: medium skin tone', + char: '\u{1F468}\u{1F3FD}\u{200D}\u{2695}\u{FE0F}', + shortName: 'man_health_worker_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'doctor', + 'healthcare', + 'man', + 'medium skin tone', + 'nurse', + 'therapist', + 'uc8', + 'diversity', + 'health', + 'sick', + 'job', + '911', + 'nerd', + 'nurse', + 'help', + 'disguise', + 'medical', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'medicine', + 'doctor', + 'barf', + 'vomit', + 'throw up', + 'puke', + 'get well', + 'cough', + 'puking', + 'barfing', + 'malade', + 'spew', + 'profession', + 'boss', + 'career', + 'emergency', + 'injury', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'man health worker: medium-dark skin tone', + char: '\u{1F468}\u{1F3FE}\u{200D}\u{2695}\u{FE0F}', + shortName: 'man_health_worker_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'doctor', + 'healthcare', + 'man', + 'medium-dark skin tone', + 'nurse', + 'therapist', + 'uc8', + 'diversity', + 'health', + 'sick', + 'job', + '911', + 'nerd', + 'nurse', + 'help', + 'disguise', + 'medical', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'medicine', + 'doctor', + 'barf', + 'vomit', + 'throw up', + 'puke', + 'get well', + 'cough', + 'puking', + 'barfing', + 'malade', + 'spew', + 'profession', + 'boss', + 'career', + 'emergency', + 'injury', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'man health worker: dark skin tone', + char: '\u{1F468}\u{1F3FF}\u{200D}\u{2695}\u{FE0F}', + shortName: 'man_health_worker_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'dark skin tone', + 'doctor', + 'healthcare', + 'man', + 'nurse', + 'therapist', + 'uc8', + 'diversity', + 'health', + 'sick', + 'job', + '911', + 'nerd', + 'nurse', + 'help', + 'disguise', + 'medical', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'medicine', + 'doctor', + 'barf', + 'vomit', + 'throw up', + 'puke', + 'get well', + 'cough', + 'puking', + 'barfing', + 'malade', + 'spew', + 'profession', + 'boss', + 'career', + 'emergency', + 'injury', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'farmer', + char: '\u{1F9D1}\u{200D}\u{1F33E}', + shortName: 'farmer', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'job', + 'farm', + 'disguise', + 'profession', + 'boss', + 'career' + ]), + Emoji( + name: 'farmer: light skin tone', + char: '\u{1F9D1}\u{1F3FB}\u{200D}\u{1F33E}', + shortName: 'farmer_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'job', + 'farm', + 'disguise', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'farmer: medium-light skin tone', + char: '\u{1F9D1}\u{1F3FC}\u{200D}\u{1F33E}', + shortName: 'farmer_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'job', + 'farm', + 'disguise', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'farmer: medium skin tone', + char: '\u{1F9D1}\u{1F3FD}\u{200D}\u{1F33E}', + shortName: 'farmer_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'job', + 'farm', + 'disguise', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'farmer: medium-dark skin tone', + char: '\u{1F9D1}\u{1F3FE}\u{200D}\u{1F33E}', + shortName: 'farmer_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'job', + 'farm', + 'disguise', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'farmer: dark skin tone', + char: '\u{1F9D1}\u{1F3FF}\u{200D}\u{1F33E}', + shortName: 'farmer_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'job', + 'farm', + 'disguise', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'woman farmer', + char: '\u{1F469}\u{200D}\u{1F33E}', + shortName: 'woman_farmer', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'farmer', + 'gardener', + 'rancher', + 'woman', + 'uc6', + 'diversity', + 'job', + 'farm', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career' + ]), + Emoji( + name: 'woman farmer: light skin tone', + char: '\u{1F469}\u{1F3FB}\u{200D}\u{1F33E}', + shortName: 'woman_farmer_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'farmer', + 'gardener', + 'light skin tone', + 'rancher', + 'woman', + 'uc8', + 'diversity', + 'job', + 'farm', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'woman farmer: medium-light skin tone', + char: '\u{1F469}\u{1F3FC}\u{200D}\u{1F33E}', + shortName: 'woman_farmer_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'farmer', + 'gardener', + 'medium-light skin tone', + 'rancher', + 'woman', + 'uc8', + 'diversity', + 'job', + 'farm', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'woman farmer: medium skin tone', + char: '\u{1F469}\u{1F3FD}\u{200D}\u{1F33E}', + shortName: 'woman_farmer_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'farmer', + 'gardener', + 'medium skin tone', + 'rancher', + 'woman', + 'uc8', + 'diversity', + 'job', + 'farm', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'woman farmer: medium-dark skin tone', + char: '\u{1F469}\u{1F3FE}\u{200D}\u{1F33E}', + shortName: 'woman_farmer_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'farmer', + 'gardener', + 'medium-dark skin tone', + 'rancher', + 'woman', + 'uc8', + 'diversity', + 'job', + 'farm', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'woman farmer: dark skin tone', + char: '\u{1F469}\u{1F3FF}\u{200D}\u{1F33E}', + shortName: 'woman_farmer_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'dark skin tone', + 'farmer', + 'gardener', + 'rancher', + 'woman', + 'uc8', + 'diversity', + 'job', + 'farm', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'man farmer', + char: '\u{1F468}\u{200D}\u{1F33E}', + shortName: 'man_farmer', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'farmer', + 'gardener', + 'man', + 'rancher', + 'uc6', + 'diversity', + 'job', + 'farm', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career' + ]), + Emoji( + name: 'man farmer: light skin tone', + char: '\u{1F468}\u{1F3FB}\u{200D}\u{1F33E}', + shortName: 'man_farmer_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'farmer', + 'gardener', + 'light skin tone', + 'man', + 'rancher', + 'uc8', + 'diversity', + 'job', + 'farm', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'man farmer: medium-light skin tone', + char: '\u{1F468}\u{1F3FC}\u{200D}\u{1F33E}', + shortName: 'man_farmer_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'farmer', + 'gardener', + 'man', + 'medium-light skin tone', + 'rancher', + 'uc8', + 'diversity', + 'job', + 'farm', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'man farmer: medium skin tone', + char: '\u{1F468}\u{1F3FD}\u{200D}\u{1F33E}', + shortName: 'man_farmer_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'farmer', + 'gardener', + 'man', + 'medium skin tone', + 'rancher', + 'uc8', + 'diversity', + 'job', + 'farm', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'man farmer: medium-dark skin tone', + char: '\u{1F468}\u{1F3FE}\u{200D}\u{1F33E}', + shortName: 'man_farmer_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'farmer', + 'gardener', + 'man', + 'medium-dark skin tone', + 'rancher', + 'uc8', + 'diversity', + 'job', + 'farm', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'man farmer: dark skin tone', + char: '\u{1F468}\u{1F3FF}\u{200D}\u{1F33E}', + shortName: 'man_farmer_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'dark skin tone', + 'farmer', + 'gardener', + 'man', + 'rancher', + 'uc8', + 'diversity', + 'job', + 'farm', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'cook', + char: '\u{1F9D1}\u{200D}\u{1F373}', + shortName: 'cook', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'job', + 'chef', + 'dinner', + 'disguise', + 'profession', + 'boss', + 'career', + 'cuisinière', + 'cuisinier', + 'lunch' + ]), + Emoji( + name: 'cook: light skin tone', + char: '\u{1F9D1}\u{1F3FB}\u{200D}\u{1F373}', + shortName: 'cook_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'job', + 'chef', + 'dinner', + 'disguise', + 'profession', + 'boss', + 'career', + 'cuisinière', + 'cuisinier', + 'lunch' + ], + modifiable: true), + Emoji( + name: 'cook: medium-light skin tone', + char: '\u{1F9D1}\u{1F3FC}\u{200D}\u{1F373}', + shortName: 'cook_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'job', + 'chef', + 'dinner', + 'disguise', + 'profession', + 'boss', + 'career', + 'cuisinière', + 'cuisinier', + 'lunch' + ], + modifiable: true), + Emoji( + name: 'cook: medium skin tone', + char: '\u{1F9D1}\u{1F3FD}\u{200D}\u{1F373}', + shortName: 'cook_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'job', + 'chef', + 'dinner', + 'disguise', + 'profession', + 'boss', + 'career', + 'cuisinière', + 'cuisinier', + 'lunch' + ], + modifiable: true), + Emoji( + name: 'cook: medium-dark skin tone', + char: '\u{1F9D1}\u{1F3FE}\u{200D}\u{1F373}', + shortName: 'cook_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'job', + 'chef', + 'dinner', + 'disguise', + 'profession', + 'boss', + 'career', + 'cuisinière', + 'cuisinier', + 'lunch' + ], + modifiable: true), + Emoji( + name: 'cook: dark skin tone', + char: '\u{1F9D1}\u{1F3FF}\u{200D}\u{1F373}', + shortName: 'cook_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'job', + 'chef', + 'dinner', + 'disguise', + 'profession', + 'boss', + 'career', + 'cuisinière', + 'cuisinier', + 'lunch' + ], + modifiable: true), + Emoji( + name: 'woman cook', + char: '\u{1F469}\u{200D}\u{1F373}', + shortName: 'woman_cook', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'chef', + 'cook', + 'woman', + 'uc6', + 'diversity', + 'job', + 'bake', + 'chef', + 'dinner', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'baking', + 'cuisinière', + 'cuisinier', + 'lunch' + ]), + Emoji( + name: 'woman cook: light skin tone', + char: '\u{1F469}\u{1F3FB}\u{200D}\u{1F373}', + shortName: 'woman_cook_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'chef', + 'cook', + 'light skin tone', + 'woman', + 'uc8', + 'diversity', + 'job', + 'bake', + 'chef', + 'dinner', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'baking', + 'cuisinière', + 'cuisinier', + 'lunch' + ], + modifiable: true), + Emoji( + name: 'woman cook: medium-light skin tone', + char: '\u{1F469}\u{1F3FC}\u{200D}\u{1F373}', + shortName: 'woman_cook_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'chef', + 'cook', + 'medium-light skin tone', + 'woman', + 'uc8', + 'diversity', + 'job', + 'bake', + 'chef', + 'dinner', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'baking', + 'cuisinière', + 'cuisinier', + 'lunch' + ], + modifiable: true), + Emoji( + name: 'woman cook: medium skin tone', + char: '\u{1F469}\u{1F3FD}\u{200D}\u{1F373}', + shortName: 'woman_cook_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'chef', + 'cook', + 'medium skin tone', + 'woman', + 'uc8', + 'diversity', + 'job', + 'bake', + 'chef', + 'dinner', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'baking', + 'cuisinière', + 'cuisinier', + 'lunch' + ], + modifiable: true), + Emoji( + name: 'woman cook: medium-dark skin tone', + char: '\u{1F469}\u{1F3FE}\u{200D}\u{1F373}', + shortName: 'woman_cook_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'chef', + 'cook', + 'medium-dark skin tone', + 'woman', + 'uc8', + 'diversity', + 'job', + 'bake', + 'chef', + 'dinner', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'baking', + 'cuisinière', + 'cuisinier', + 'lunch' + ], + modifiable: true), + Emoji( + name: 'woman cook: dark skin tone', + char: '\u{1F469}\u{1F3FF}\u{200D}\u{1F373}', + shortName: 'woman_cook_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'chef', + 'cook', + 'dark skin tone', + 'woman', + 'uc8', + 'diversity', + 'job', + 'bake', + 'chef', + 'dinner', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'baking', + 'cuisinière', + 'cuisinier', + 'lunch' + ], + modifiable: true), + Emoji( + name: 'man cook', + char: '\u{1F468}\u{200D}\u{1F373}', + shortName: 'man_cook', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'chef', + 'cook', + 'man', + 'uc6', + 'diversity', + 'job', + 'bake', + 'chef', + 'dinner', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'baking', + 'cuisinière', + 'cuisinier', + 'lunch' + ]), + Emoji( + name: 'man cook: light skin tone', + char: '\u{1F468}\u{1F3FB}\u{200D}\u{1F373}', + shortName: 'man_cook_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'chef', + 'cook', + 'light skin tone', + 'man', + 'uc8', + 'diversity', + 'job', + 'bake', + 'chef', + 'dinner', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'baking', + 'cuisinière', + 'cuisinier', + 'lunch' + ], + modifiable: true), + Emoji( + name: 'man cook: medium-light skin tone', + char: '\u{1F468}\u{1F3FC}\u{200D}\u{1F373}', + shortName: 'man_cook_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'chef', + 'cook', + 'man', + 'medium-light skin tone', + 'uc8', + 'diversity', + 'job', + 'bake', + 'chef', + 'dinner', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'baking', + 'cuisinière', + 'cuisinier', + 'lunch' + ], + modifiable: true), + Emoji( + name: 'man cook: medium skin tone', + char: '\u{1F468}\u{1F3FD}\u{200D}\u{1F373}', + shortName: 'man_cook_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'chef', + 'cook', + 'man', + 'medium skin tone', + 'uc8', + 'diversity', + 'job', + 'bake', + 'chef', + 'dinner', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'baking', + 'cuisinière', + 'cuisinier', + 'lunch' + ], + modifiable: true), + Emoji( + name: 'man cook: medium-dark skin tone', + char: '\u{1F468}\u{1F3FE}\u{200D}\u{1F373}', + shortName: 'man_cook_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'chef', + 'cook', + 'man', + 'medium-dark skin tone', + 'uc8', + 'diversity', + 'job', + 'bake', + 'chef', + 'dinner', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'baking', + 'cuisinière', + 'cuisinier', + 'lunch' + ], + modifiable: true), + Emoji( + name: 'man cook: dark skin tone', + char: '\u{1F468}\u{1F3FF}\u{200D}\u{1F373}', + shortName: 'man_cook_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'chef', + 'cook', + 'dark skin tone', + 'man', + 'uc8', + 'diversity', + 'job', + 'bake', + 'chef', + 'dinner', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'baking', + 'cuisinière', + 'cuisinier', + 'lunch' + ], + modifiable: true), + Emoji( + name: 'student', + char: '\u{1F9D1}\u{200D}\u{1F393}', + shortName: 'student', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'classroom', + 'nerd', + 'graduate', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'smart', + 'geek', + 'serious' + ]), + Emoji( + name: 'student: light skin tone', + char: '\u{1F9D1}\u{1F3FB}\u{200D}\u{1F393}', + shortName: 'student_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'classroom', + 'nerd', + 'graduate', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'student: medium-light skin tone', + char: '\u{1F9D1}\u{1F3FC}\u{200D}\u{1F393}', + shortName: 'student_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'classroom', + 'nerd', + 'graduate', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'student: medium skin tone', + char: '\u{1F9D1}\u{1F3FD}\u{200D}\u{1F393}', + shortName: 'student_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'classroom', + 'nerd', + 'graduate', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'student: medium-dark skin tone', + char: '\u{1F9D1}\u{1F3FE}\u{200D}\u{1F393}', + shortName: 'student_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'classroom', + 'nerd', + 'graduate', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'student: dark skin tone', + char: '\u{1F9D1}\u{1F3FF}\u{200D}\u{1F393}', + shortName: 'student_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'classroom', + 'nerd', + 'graduate', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'woman student', + char: '\u{1F469}\u{200D}\u{1F393}', + shortName: 'woman_student', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'graduate', + 'student', + 'woman', + 'uc6', + 'diversity', + 'classroom', + 'nerd', + 'graduate', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'smart', + 'geek', + 'serious' + ]), + Emoji( + name: 'woman student: light skin tone', + char: '\u{1F469}\u{1F3FB}\u{200D}\u{1F393}', + shortName: 'woman_student_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'graduate', + 'light skin tone', + 'student', + 'woman', + 'uc8', + 'diversity', + 'classroom', + 'nerd', + 'graduate', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'woman student: medium-light skin tone', + char: '\u{1F469}\u{1F3FC}\u{200D}\u{1F393}', + shortName: 'woman_student_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'graduate', + 'medium-light skin tone', + 'student', + 'woman', + 'uc8', + 'diversity', + 'classroom', + 'nerd', + 'graduate', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'woman student: medium skin tone', + char: '\u{1F469}\u{1F3FD}\u{200D}\u{1F393}', + shortName: 'woman_student_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'graduate', + 'medium skin tone', + 'student', + 'woman', + 'uc8', + 'diversity', + 'classroom', + 'nerd', + 'graduate', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'woman student: medium-dark skin tone', + char: '\u{1F469}\u{1F3FE}\u{200D}\u{1F393}', + shortName: 'woman_student_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'graduate', + 'medium-dark skin tone', + 'student', + 'woman', + 'uc8', + 'diversity', + 'classroom', + 'nerd', + 'graduate', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'woman student: dark skin tone', + char: '\u{1F469}\u{1F3FF}\u{200D}\u{1F393}', + shortName: 'woman_student_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'dark skin tone', + 'graduate', + 'student', + 'woman', + 'uc8', + 'diversity', + 'classroom', + 'nerd', + 'graduate', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'man student', + char: '\u{1F468}\u{200D}\u{1F393}', + shortName: 'man_student', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'graduate', + 'man', + 'student', + 'uc6', + 'diversity', + 'classroom', + 'nerd', + 'graduate', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'smart', + 'geek', + 'serious' + ]), + Emoji( + name: 'man student: light skin tone', + char: '\u{1F468}\u{1F3FB}\u{200D}\u{1F393}', + shortName: 'man_student_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'graduate', + 'light skin tone', + 'man', + 'student', + 'uc8', + 'diversity', + 'classroom', + 'nerd', + 'graduate', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'man student: medium-light skin tone', + char: '\u{1F468}\u{1F3FC}\u{200D}\u{1F393}', + shortName: 'man_student_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'graduate', + 'man', + 'medium-light skin tone', + 'student', + 'uc8', + 'diversity', + 'classroom', + 'nerd', + 'graduate', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'man student: medium skin tone', + char: '\u{1F468}\u{1F3FD}\u{200D}\u{1F393}', + shortName: 'man_student_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'graduate', + 'man', + 'medium skin tone', + 'student', + 'uc8', + 'diversity', + 'classroom', + 'nerd', + 'graduate', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'man student: medium-dark skin tone', + char: '\u{1F468}\u{1F3FE}\u{200D}\u{1F393}', + shortName: 'man_student_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'graduate', + 'man', + 'medium-dark skin tone', + 'student', + 'uc8', + 'diversity', + 'classroom', + 'nerd', + 'graduate', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'man student: dark skin tone', + char: '\u{1F468}\u{1F3FF}\u{200D}\u{1F393}', + shortName: 'man_student_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'dark skin tone', + 'graduate', + 'man', + 'student', + 'uc8', + 'diversity', + 'classroom', + 'nerd', + 'graduate', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'singer', + char: '\u{1F9D1}\u{200D}\u{1F3A4}', + shortName: 'singer', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'job', + 'rock and roll', + 'fame', + 'artist', + 'disguise', + 'profession', + 'boss', + 'career', + 'famous', + 'celebrity' + ]), + Emoji( + name: 'singer: light skin tone', + char: '\u{1F9D1}\u{1F3FB}\u{200D}\u{1F3A4}', + shortName: 'singer_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'job', + 'rock and roll', + 'fame', + 'artist', + 'disguise', + 'profession', + 'boss', + 'career', + 'famous', + 'celebrity' + ], + modifiable: true), + Emoji( + name: 'singer: medium-light skin tone', + char: '\u{1F9D1}\u{1F3FC}\u{200D}\u{1F3A4}', + shortName: 'singer_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'job', + 'rock and roll', + 'fame', + 'artist', + 'disguise', + 'profession', + 'boss', + 'career', + 'famous', + 'celebrity' + ], + modifiable: true), + Emoji( + name: 'singer: medium skin tone', + char: '\u{1F9D1}\u{1F3FD}\u{200D}\u{1F3A4}', + shortName: 'singer_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'job', + 'rock and roll', + 'fame', + 'artist', + 'disguise', + 'profession', + 'boss', + 'career', + 'famous', + 'celebrity' + ], + modifiable: true), + Emoji( + name: 'singer: medium-dark skin tone', + char: '\u{1F9D1}\u{1F3FE}\u{200D}\u{1F3A4}', + shortName: 'singer_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'job', + 'rock and roll', + 'fame', + 'artist', + 'disguise', + 'profession', + 'boss', + 'career', + 'famous', + 'celebrity' + ], + modifiable: true), + Emoji( + name: 'singer: dark skin tone', + char: '\u{1F9D1}\u{1F3FF}\u{200D}\u{1F3A4}', + shortName: 'singer_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'job', + 'rock and roll', + 'fame', + 'artist', + 'disguise', + 'profession', + 'boss', + 'career', + 'famous', + 'celebrity' + ], + modifiable: true), + Emoji( + name: 'woman singer', + char: '\u{1F469}\u{200D}\u{1F3A4}', + shortName: 'woman_singer', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'actor', + 'entertainer', + 'rock', + 'singer', + 'star', + 'woman', + 'uc6', + 'instruments', + 'diversity', + 'job', + 'rock and roll', + 'fame', + 'artist', + 'disguise', + 'music', + 'instrument', + 'singing', + 'concert', + 'jaz', + 'listen', + 'singer', + 'song', + 'musique', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'famous', + 'celebrity' + ]), + Emoji( + name: 'woman singer: light skin tone', + char: '\u{1F469}\u{1F3FB}\u{200D}\u{1F3A4}', + shortName: 'woman_singer_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'actor', + 'entertainer', + 'light skin tone', + 'rock', + 'singer', + 'star', + 'woman', + 'uc8', + 'instruments', + 'diversity', + 'job', + 'rock and roll', + 'fame', + 'artist', + 'disguise', + 'music', + 'instrument', + 'singing', + 'concert', + 'jaz', + 'listen', + 'singer', + 'song', + 'musique', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'famous', + 'celebrity' + ], + modifiable: true), + Emoji( + name: 'woman singer: medium-light skin tone', + char: '\u{1F469}\u{1F3FC}\u{200D}\u{1F3A4}', + shortName: 'woman_singer_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'actor', + 'entertainer', + 'medium-light skin tone', + 'rock', + 'singer', + 'star', + 'woman', + 'uc8', + 'instruments', + 'diversity', + 'job', + 'rock and roll', + 'fame', + 'artist', + 'disguise', + 'music', + 'instrument', + 'singing', + 'concert', + 'jaz', + 'listen', + 'singer', + 'song', + 'musique', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'famous', + 'celebrity' + ], + modifiable: true), + Emoji( + name: 'woman singer: medium skin tone', + char: '\u{1F469}\u{1F3FD}\u{200D}\u{1F3A4}', + shortName: 'woman_singer_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'actor', + 'entertainer', + 'medium skin tone', + 'rock', + 'singer', + 'star', + 'woman', + 'uc8', + 'instruments', + 'diversity', + 'job', + 'rock and roll', + 'fame', + 'artist', + 'disguise', + 'music', + 'instrument', + 'singing', + 'concert', + 'jaz', + 'listen', + 'singer', + 'song', + 'musique', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'famous', + 'celebrity' + ], + modifiable: true), + Emoji( + name: 'woman singer: medium-dark skin tone', + char: '\u{1F469}\u{1F3FE}\u{200D}\u{1F3A4}', + shortName: 'woman_singer_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'actor', + 'entertainer', + 'medium-dark skin tone', + 'rock', + 'singer', + 'star', + 'woman', + 'uc8', + 'instruments', + 'diversity', + 'job', + 'rock and roll', + 'fame', + 'artist', + 'disguise', + 'music', + 'instrument', + 'singing', + 'concert', + 'jaz', + 'listen', + 'singer', + 'song', + 'musique', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'famous', + 'celebrity' + ], + modifiable: true), + Emoji( + name: 'woman singer: dark skin tone', + char: '\u{1F469}\u{1F3FF}\u{200D}\u{1F3A4}', + shortName: 'woman_singer_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'actor', + 'dark skin tone', + 'entertainer', + 'rock', + 'singer', + 'star', + 'woman', + 'uc8', + 'instruments', + 'diversity', + 'job', + 'rock and roll', + 'fame', + 'artist', + 'disguise', + 'music', + 'instrument', + 'singing', + 'concert', + 'jaz', + 'listen', + 'singer', + 'song', + 'musique', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'famous', + 'celebrity' + ], + modifiable: true), + Emoji( + name: 'man singer', + char: '\u{1F468}\u{200D}\u{1F3A4}', + shortName: 'man_singer', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'actor', + 'entertainer', + 'man', + 'rock', + 'singer', + 'star', + 'uc6', + 'instruments', + 'diversity', + 'job', + 'rock and roll', + 'fame', + 'artist', + 'disguise', + 'music', + 'instrument', + 'singing', + 'concert', + 'jaz', + 'listen', + 'singer', + 'song', + 'musique', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'famous', + 'celebrity' + ]), + Emoji( + name: 'man singer: light skin tone', + char: '\u{1F468}\u{1F3FB}\u{200D}\u{1F3A4}', + shortName: 'man_singer_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'actor', + 'entertainer', + 'light skin tone', + 'man', + 'rock', + 'singer', + 'star', + 'uc8', + 'instruments', + 'diversity', + 'job', + 'rock and roll', + 'fame', + 'artist', + 'disguise', + 'music', + 'instrument', + 'singing', + 'concert', + 'jaz', + 'listen', + 'singer', + 'song', + 'musique', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'famous', + 'celebrity' + ], + modifiable: true), + Emoji( + name: 'man singer: medium-light skin tone', + char: '\u{1F468}\u{1F3FC}\u{200D}\u{1F3A4}', + shortName: 'man_singer_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'actor', + 'entertainer', + 'man', + 'medium-light skin tone', + 'rock', + 'singer', + 'star', + 'uc8', + 'instruments', + 'diversity', + 'job', + 'rock and roll', + 'fame', + 'artist', + 'disguise', + 'music', + 'instrument', + 'singing', + 'concert', + 'jaz', + 'listen', + 'singer', + 'song', + 'musique', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'famous', + 'celebrity' + ], + modifiable: true), + Emoji( + name: 'man singer: medium skin tone', + char: '\u{1F468}\u{1F3FD}\u{200D}\u{1F3A4}', + shortName: 'man_singer_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'actor', + 'entertainer', + 'man', + 'medium skin tone', + 'rock', + 'singer', + 'star', + 'uc8', + 'instruments', + 'diversity', + 'job', + 'rock and roll', + 'fame', + 'artist', + 'disguise', + 'music', + 'instrument', + 'singing', + 'concert', + 'jaz', + 'listen', + 'singer', + 'song', + 'musique', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'famous', + 'celebrity' + ], + modifiable: true), + Emoji( + name: 'man singer: medium-dark skin tone', + char: '\u{1F468}\u{1F3FE}\u{200D}\u{1F3A4}', + shortName: 'man_singer_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'actor', + 'entertainer', + 'man', + 'medium-dark skin tone', + 'rock', + 'singer', + 'star', + 'uc8', + 'instruments', + 'diversity', + 'job', + 'rock and roll', + 'fame', + 'artist', + 'disguise', + 'music', + 'instrument', + 'singing', + 'concert', + 'jaz', + 'listen', + 'singer', + 'song', + 'musique', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'famous', + 'celebrity' + ], + modifiable: true), + Emoji( + name: 'man singer: dark skin tone', + char: '\u{1F468}\u{1F3FF}\u{200D}\u{1F3A4}', + shortName: 'man_singer_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'actor', + 'dark skin tone', + 'entertainer', + 'man', + 'rock', + 'singer', + 'star', + 'uc8', + 'instruments', + 'diversity', + 'job', + 'rock and roll', + 'fame', + 'artist', + 'disguise', + 'music', + 'instrument', + 'singing', + 'concert', + 'jaz', + 'listen', + 'singer', + 'song', + 'musique', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'famous', + 'celebrity' + ], + modifiable: true), + Emoji( + name: 'teacher', + char: '\u{1F9D1}\u{200D}\u{1F3EB}', + shortName: 'teacher', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'classroom', + 'job', + 'nerd', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious' + ]), + Emoji( + name: 'teacher: light skin tone', + char: '\u{1F9D1}\u{1F3FB}\u{200D}\u{1F3EB}', + shortName: 'teacher_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'classroom', + 'job', + 'nerd', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'teacher: medium-light skin tone', + char: '\u{1F9D1}\u{1F3FC}\u{200D}\u{1F3EB}', + shortName: 'teacher_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'classroom', + 'job', + 'nerd', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'teacher: medium skin tone', + char: '\u{1F9D1}\u{1F3FD}\u{200D}\u{1F3EB}', + shortName: 'teacher_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'classroom', + 'job', + 'nerd', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'teacher: medium-dark skin tone', + char: '\u{1F9D1}\u{1F3FE}\u{200D}\u{1F3EB}', + shortName: 'teacher_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'classroom', + 'job', + 'nerd', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'teacher: dark skin tone', + char: '\u{1F9D1}\u{1F3FF}\u{200D}\u{1F3EB}', + shortName: 'teacher_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'classroom', + 'job', + 'nerd', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'woman teacher', + char: '\u{1F469}\u{200D}\u{1F3EB}', + shortName: 'woman_teacher', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'instructor', + 'professor', + 'teacher', + 'woman', + 'uc6', + 'diversity', + 'classroom', + 'job', + 'nerd', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious' + ]), + Emoji( + name: 'woman teacher: light skin tone', + char: '\u{1F469}\u{1F3FB}\u{200D}\u{1F3EB}', + shortName: 'woman_teacher_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'instructor', + 'light skin tone', + 'professor', + 'teacher', + 'woman', + 'uc8', + 'diversity', + 'classroom', + 'job', + 'nerd', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'woman teacher: medium-light skin tone', + char: '\u{1F469}\u{1F3FC}\u{200D}\u{1F3EB}', + shortName: 'woman_teacher_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'instructor', + 'medium-light skin tone', + 'professor', + 'teacher', + 'woman', + 'uc8', + 'diversity', + 'classroom', + 'job', + 'nerd', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'woman teacher: medium skin tone', + char: '\u{1F469}\u{1F3FD}\u{200D}\u{1F3EB}', + shortName: 'woman_teacher_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'instructor', + 'medium skin tone', + 'professor', + 'teacher', + 'woman', + 'uc8', + 'diversity', + 'classroom', + 'job', + 'nerd', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'woman teacher: medium-dark skin tone', + char: '\u{1F469}\u{1F3FE}\u{200D}\u{1F3EB}', + shortName: 'woman_teacher_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'instructor', + 'medium-dark skin tone', + 'professor', + 'teacher', + 'woman', + 'uc8', + 'diversity', + 'classroom', + 'job', + 'nerd', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'woman teacher: dark skin tone', + char: '\u{1F469}\u{1F3FF}\u{200D}\u{1F3EB}', + shortName: 'woman_teacher_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'dark skin tone', + 'instructor', + 'professor', + 'teacher', + 'woman', + 'uc8', + 'diversity', + 'classroom', + 'job', + 'nerd', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'man teacher', + char: '\u{1F468}\u{200D}\u{1F3EB}', + shortName: 'man_teacher', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'instructor', + 'man', + 'professor', + 'teacher', + 'uc6', + 'diversity', + 'classroom', + 'job', + 'nerd', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious' + ]), + Emoji( + name: 'man teacher: light skin tone', + char: '\u{1F468}\u{1F3FB}\u{200D}\u{1F3EB}', + shortName: 'man_teacher_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'instructor', + 'light skin tone', + 'man', + 'professor', + 'teacher', + 'uc8', + 'diversity', + 'classroom', + 'job', + 'nerd', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'man teacher: medium-light skin tone', + char: '\u{1F468}\u{1F3FC}\u{200D}\u{1F3EB}', + shortName: 'man_teacher_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'instructor', + 'man', + 'medium-light skin tone', + 'professor', + 'teacher', + 'uc8', + 'diversity', + 'classroom', + 'job', + 'nerd', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'man teacher: medium skin tone', + char: '\u{1F468}\u{1F3FD}\u{200D}\u{1F3EB}', + shortName: 'man_teacher_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'instructor', + 'man', + 'medium skin tone', + 'professor', + 'teacher', + 'uc8', + 'diversity', + 'classroom', + 'job', + 'nerd', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'man teacher: medium-dark skin tone', + char: '\u{1F468}\u{1F3FE}\u{200D}\u{1F3EB}', + shortName: 'man_teacher_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'instructor', + 'man', + 'medium-dark skin tone', + 'professor', + 'teacher', + 'uc8', + 'diversity', + 'classroom', + 'job', + 'nerd', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'man teacher: dark skin tone', + char: '\u{1F468}\u{1F3FF}\u{200D}\u{1F3EB}', + shortName: 'man_teacher_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'dark skin tone', + 'instructor', + 'man', + 'professor', + 'teacher', + 'uc8', + 'diversity', + 'classroom', + 'job', + 'nerd', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'factory worker', + char: '\u{1F9D1}\u{200D}\u{1F3ED}', + shortName: 'factory_worker', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'job', + 'mask', + 'build', + 'profession', + 'boss', + 'career' + ]), + Emoji( + name: 'factory worker: light skin tone', + char: '\u{1F9D1}\u{1F3FB}\u{200D}\u{1F3ED}', + shortName: 'factory_worker_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'job', + 'mask', + 'build', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'factory worker: medium-light skin tone', + char: '\u{1F9D1}\u{1F3FC}\u{200D}\u{1F3ED}', + shortName: 'factory_worker_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'job', + 'mask', + 'build', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'factory worker: medium skin tone', + char: '\u{1F9D1}\u{1F3FD}\u{200D}\u{1F3ED}', + shortName: 'factory_worker_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'job', + 'mask', + 'build', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'factory worker: medium-dark skin tone', + char: '\u{1F9D1}\u{1F3FE}\u{200D}\u{1F3ED}', + shortName: 'factory_worker_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'job', + 'mask', + 'build', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'factory worker: dark skin tone', + char: '\u{1F9D1}\u{1F3FF}\u{200D}\u{1F3ED}', + shortName: 'factory_worker_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'job', + 'mask', + 'build', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'woman factory worker', + char: '\u{1F469}\u{200D}\u{1F3ED}', + shortName: 'woman_factory_worker', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'assembly', + 'factory', + 'industrial', + 'woman', + 'worker', + 'uc6', + 'diversity', + 'job', + 'mask', + 'build', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career' + ]), + Emoji( + name: 'woman factory worker: light skin tone', + char: '\u{1F469}\u{1F3FB}\u{200D}\u{1F3ED}', + shortName: 'woman_factory_worker_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'assembly', + 'factory', + 'industrial', + 'light skin tone', + 'woman', + 'worker', + 'uc8', + 'diversity', + 'job', + 'mask', + 'build', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'woman factory worker: medium-light skin tone', + char: '\u{1F469}\u{1F3FC}\u{200D}\u{1F3ED}', + shortName: 'woman_factory_worker_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'assembly', + 'factory', + 'industrial', + 'medium-light skin tone', + 'woman', + 'worker', + 'uc8', + 'diversity', + 'job', + 'mask', + 'build', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'woman factory worker: medium skin tone', + char: '\u{1F469}\u{1F3FD}\u{200D}\u{1F3ED}', + shortName: 'woman_factory_worker_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'assembly', + 'factory', + 'industrial', + 'medium skin tone', + 'woman', + 'worker', + 'uc8', + 'diversity', + 'job', + 'mask', + 'build', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'woman factory worker: medium-dark skin tone', + char: '\u{1F469}\u{1F3FE}\u{200D}\u{1F3ED}', + shortName: 'woman_factory_worker_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'assembly', + 'factory', + 'industrial', + 'medium-dark skin tone', + 'woman', + 'worker', + 'uc8', + 'diversity', + 'job', + 'mask', + 'build', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'woman factory worker: dark skin tone', + char: '\u{1F469}\u{1F3FF}\u{200D}\u{1F3ED}', + shortName: 'woman_factory_worker_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'assembly', + 'dark skin tone', + 'factory', + 'industrial', + 'woman', + 'worker', + 'uc8', + 'diversity', + 'job', + 'mask', + 'build', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'man factory worker', + char: '\u{1F468}\u{200D}\u{1F3ED}', + shortName: 'man_factory_worker', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'assembly', + 'factory', + 'industrial', + 'man', + 'worker', + 'uc6', + 'diversity', + 'job', + 'mask', + 'build', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career' + ]), + Emoji( + name: 'man factory worker: light skin tone', + char: '\u{1F468}\u{1F3FB}\u{200D}\u{1F3ED}', + shortName: 'man_factory_worker_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'assembly', + 'factory', + 'industrial', + 'light skin tone', + 'man', + 'worker', + 'uc8', + 'diversity', + 'job', + 'mask', + 'build', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'man factory worker: medium-light skin tone', + char: '\u{1F468}\u{1F3FC}\u{200D}\u{1F3ED}', + shortName: 'man_factory_worker_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'assembly', + 'factory', + 'industrial', + 'man', + 'medium-light skin tone', + 'worker', + 'uc8', + 'diversity', + 'job', + 'mask', + 'build', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'man factory worker: medium skin tone', + char: '\u{1F468}\u{1F3FD}\u{200D}\u{1F3ED}', + shortName: 'man_factory_worker_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'assembly', + 'factory', + 'industrial', + 'man', + 'medium skin tone', + 'worker', + 'uc8', + 'diversity', + 'job', + 'mask', + 'build', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'man factory worker: medium-dark skin tone', + char: '\u{1F468}\u{1F3FE}\u{200D}\u{1F3ED}', + shortName: 'man_factory_worker_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'assembly', + 'factory', + 'industrial', + 'man', + 'medium-dark skin tone', + 'worker', + 'uc8', + 'diversity', + 'job', + 'mask', + 'build', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'man factory worker: dark skin tone', + char: '\u{1F468}\u{1F3FF}\u{200D}\u{1F3ED}', + shortName: 'man_factory_worker_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'assembly', + 'dark skin tone', + 'factory', + 'industrial', + 'man', + 'worker', + 'uc8', + 'diversity', + 'job', + 'mask', + 'build', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'technologist', + char: '\u{1F9D1}\u{200D}\u{1F4BB}', + shortName: 'technologist', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'classroom', + 'job', + 'business', + 'nerd', + 'work', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious', + 'office' + ]), + Emoji( + name: 'technologist: light skin tone', + char: '\u{1F9D1}\u{1F3FB}\u{200D}\u{1F4BB}', + shortName: 'technologist_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'classroom', + 'job', + 'business', + 'nerd', + 'work', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious', + 'office' + ], + modifiable: true), + Emoji( + name: 'technologist: medium-light skin tone', + char: '\u{1F9D1}\u{1F3FC}\u{200D}\u{1F4BB}', + shortName: 'technologist_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'classroom', + 'job', + 'business', + 'nerd', + 'work', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious', + 'office' + ], + modifiable: true), + Emoji( + name: 'technologist: medium skin tone', + char: '\u{1F9D1}\u{1F3FD}\u{200D}\u{1F4BB}', + shortName: 'technologist_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'classroom', + 'job', + 'business', + 'nerd', + 'work', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious', + 'office' + ], + modifiable: true), + Emoji( + name: 'technologist: medium-dark skin tone', + char: '\u{1F9D1}\u{1F3FE}\u{200D}\u{1F4BB}', + shortName: 'technologist_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'classroom', + 'job', + 'business', + 'nerd', + 'work', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious', + 'office' + ], + modifiable: true), + Emoji( + name: 'technologist: dark skin tone', + char: '\u{1F9D1}\u{1F3FF}\u{200D}\u{1F4BB}', + shortName: 'technologist_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'classroom', + 'job', + 'business', + 'nerd', + 'work', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious', + 'office' + ], + modifiable: true), + Emoji( + name: 'woman technologist', + char: '\u{1F469}\u{200D}\u{1F4BB}', + shortName: 'woman_technologist', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'coder', + 'developer', + 'inventor', + 'software', + 'technologist', + 'woman', + 'uc6', + 'diversity', + 'classroom', + 'job', + 'business', + 'nerd', + 'code', + 'work', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious', + 'coding', + 'office' + ]), + Emoji( + name: 'woman technologist: light skin tone', + char: '\u{1F469}\u{1F3FB}\u{200D}\u{1F4BB}', + shortName: 'woman_technologist_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'coder', + 'developer', + 'inventor', + 'light skin tone', + 'software', + 'technologist', + 'woman', + 'uc8', + 'diversity', + 'classroom', + 'job', + 'business', + 'nerd', + 'code', + 'work', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious', + 'coding', + 'office' + ], + modifiable: true), + Emoji( + name: 'woman technologist: medium-light skin tone', + char: '\u{1F469}\u{1F3FC}\u{200D}\u{1F4BB}', + shortName: 'woman_technologist_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'coder', + 'developer', + 'inventor', + 'medium-light skin tone', + 'software', + 'technologist', + 'woman', + 'uc8', + 'diversity', + 'classroom', + 'job', + 'business', + 'nerd', + 'code', + 'work', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious', + 'coding', + 'office' + ], + modifiable: true), + Emoji( + name: 'woman technologist: medium skin tone', + char: '\u{1F469}\u{1F3FD}\u{200D}\u{1F4BB}', + shortName: 'woman_technologist_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'coder', + 'developer', + 'inventor', + 'medium skin tone', + 'software', + 'technologist', + 'woman', + 'uc8', + 'diversity', + 'classroom', + 'job', + 'business', + 'nerd', + 'code', + 'work', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious', + 'coding', + 'office' + ], + modifiable: true), + Emoji( + name: 'woman technologist: medium-dark skin tone', + char: '\u{1F469}\u{1F3FE}\u{200D}\u{1F4BB}', + shortName: 'woman_technologist_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'coder', + 'developer', + 'inventor', + 'medium-dark skin tone', + 'software', + 'technologist', + 'woman', + 'uc8', + 'diversity', + 'classroom', + 'job', + 'business', + 'nerd', + 'code', + 'work', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious', + 'coding', + 'office' + ], + modifiable: true), + Emoji( + name: 'woman technologist: dark skin tone', + char: '\u{1F469}\u{1F3FF}\u{200D}\u{1F4BB}', + shortName: 'woman_technologist_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'coder', + 'dark skin tone', + 'developer', + 'inventor', + 'software', + 'technologist', + 'woman', + 'uc8', + 'diversity', + 'classroom', + 'job', + 'business', + 'nerd', + 'code', + 'work', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious', + 'coding', + 'office' + ], + modifiable: true), + Emoji( + name: 'man technologist', + char: '\u{1F468}\u{200D}\u{1F4BB}', + shortName: 'man_technologist', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'coder', + 'developer', + 'inventor', + 'man', + 'software', + 'technologist', + 'uc6', + 'diversity', + 'classroom', + 'job', + 'business', + 'nerd', + 'code', + 'work', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious', + 'coding', + 'office' + ]), + Emoji( + name: 'man technologist: light skin tone', + char: '\u{1F468}\u{1F3FB}\u{200D}\u{1F4BB}', + shortName: 'man_technologist_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'coder', + 'developer', + 'inventor', + 'light skin tone', + 'man', + 'software', + 'technologist', + 'uc8', + 'diversity', + 'classroom', + 'job', + 'business', + 'nerd', + 'code', + 'work', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious', + 'coding', + 'office' + ], + modifiable: true), + Emoji( + name: 'man technologist: medium-light skin tone', + char: '\u{1F468}\u{1F3FC}\u{200D}\u{1F4BB}', + shortName: 'man_technologist_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'coder', + 'developer', + 'inventor', + 'man', + 'medium-light skin tone', + 'software', + 'technologist', + 'uc8', + 'diversity', + 'classroom', + 'job', + 'business', + 'nerd', + 'code', + 'work', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious', + 'coding', + 'office' + ], + modifiable: true), + Emoji( + name: 'man technologist: medium skin tone', + char: '\u{1F468}\u{1F3FD}\u{200D}\u{1F4BB}', + shortName: 'man_technologist_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'coder', + 'developer', + 'inventor', + 'man', + 'medium skin tone', + 'software', + 'technologist', + 'uc8', + 'diversity', + 'classroom', + 'job', + 'business', + 'nerd', + 'code', + 'work', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious', + 'coding', + 'office' + ], + modifiable: true), + Emoji( + name: 'man technologist: medium-dark skin tone', + char: '\u{1F468}\u{1F3FE}\u{200D}\u{1F4BB}', + shortName: 'man_technologist_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'coder', + 'developer', + 'inventor', + 'man', + 'medium-dark skin tone', + 'software', + 'technologist', + 'uc8', + 'diversity', + 'classroom', + 'job', + 'business', + 'nerd', + 'code', + 'work', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious', + 'coding', + 'office' + ], + modifiable: true), + Emoji( + name: 'man technologist: dark skin tone', + char: '\u{1F468}\u{1F3FF}\u{200D}\u{1F4BB}', + shortName: 'man_technologist_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'coder', + 'dark skin tone', + 'developer', + 'inventor', + 'man', + 'software', + 'technologist', + 'uc8', + 'diversity', + 'classroom', + 'job', + 'business', + 'nerd', + 'code', + 'work', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious', + 'coding', + 'office' + ], + modifiable: true), + Emoji( + name: 'office worker', + char: '\u{1F9D1}\u{200D}\u{1F4BC}', + shortName: 'office_worker', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'men', + 'job', + 'business', + 'nerd', + 'work', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious', + 'office' + ]), + Emoji( + name: 'office worker: light skin tone', + char: '\u{1F9D1}\u{1F3FB}\u{200D}\u{1F4BC}', + shortName: 'office_worker_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'men', + 'job', + 'business', + 'nerd', + 'work', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious', + 'office' + ], + modifiable: true), + Emoji( + name: 'office worker: medium-light skin tone', + char: '\u{1F9D1}\u{1F3FC}\u{200D}\u{1F4BC}', + shortName: 'office_worker_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'men', + 'job', + 'business', + 'nerd', + 'work', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious', + 'office' + ], + modifiable: true), + Emoji( + name: 'office worker: medium skin tone', + char: '\u{1F9D1}\u{1F3FD}\u{200D}\u{1F4BC}', + shortName: 'office_worker_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'men', + 'job', + 'business', + 'nerd', + 'work', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious', + 'office' + ], + modifiable: true), + Emoji( + name: 'office worker: medium-dark skin tone', + char: '\u{1F9D1}\u{1F3FE}\u{200D}\u{1F4BC}', + shortName: 'office_worker_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'men', + 'job', + 'business', + 'nerd', + 'work', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious', + 'office' + ], + modifiable: true), + Emoji( + name: 'office worker: dark skin tone', + char: '\u{1F9D1}\u{1F3FF}\u{200D}\u{1F4BC}', + shortName: 'office_worker_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'men', + 'job', + 'business', + 'nerd', + 'work', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious', + 'office' + ], + modifiable: true), + Emoji( + name: 'woman office worker', + char: '\u{1F469}\u{200D}\u{1F4BC}', + shortName: 'woman_office_worker', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'architect', + 'business', + 'manager', + 'office', + 'white-collar', + 'woman', + 'uc6', + 'diversity', + 'women', + 'job', + 'business', + 'nerd', + 'costume', + 'work', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious', + 'office' + ]), + Emoji( + name: 'woman office worker: light skin tone', + char: '\u{1F469}\u{1F3FB}\u{200D}\u{1F4BC}', + shortName: 'woman_office_worker_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'architect', + 'business', + 'light skin tone', + 'manager', + 'office', + 'white-collar', + 'woman', + 'uc8', + 'diversity', + 'women', + 'job', + 'business', + 'nerd', + 'costume', + 'work', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious', + 'office' + ], + modifiable: true), + Emoji( + name: 'woman office worker: medium-light skin tone', + char: '\u{1F469}\u{1F3FC}\u{200D}\u{1F4BC}', + shortName: 'woman_office_worker_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'architect', + 'business', + 'manager', + 'medium-light skin tone', + 'office', + 'white-collar', + 'woman', + 'uc8', + 'diversity', + 'women', + 'job', + 'business', + 'nerd', + 'costume', + 'work', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious', + 'office' + ], + modifiable: true), + Emoji( + name: 'woman office worker: medium skin tone', + char: '\u{1F469}\u{1F3FD}\u{200D}\u{1F4BC}', + shortName: 'woman_office_worker_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'architect', + 'business', + 'manager', + 'medium skin tone', + 'office', + 'white-collar', + 'woman', + 'uc8', + 'diversity', + 'women', + 'job', + 'business', + 'nerd', + 'costume', + 'work', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious', + 'office' + ], + modifiable: true), + Emoji( + name: 'woman office worker: medium-dark skin tone', + char: '\u{1F469}\u{1F3FE}\u{200D}\u{1F4BC}', + shortName: 'woman_office_worker_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'architect', + 'business', + 'manager', + 'medium-dark skin tone', + 'office', + 'white-collar', + 'woman', + 'uc8', + 'diversity', + 'women', + 'job', + 'business', + 'nerd', + 'costume', + 'work', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious', + 'office' + ], + modifiable: true), + Emoji( + name: 'woman office worker: dark skin tone', + char: '\u{1F469}\u{1F3FF}\u{200D}\u{1F4BC}', + shortName: 'woman_office_worker_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'architect', + 'business', + 'dark skin tone', + 'manager', + 'office', + 'white-collar', + 'woman', + 'uc8', + 'diversity', + 'women', + 'job', + 'business', + 'nerd', + 'costume', + 'work', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious', + 'office' + ], + modifiable: true), + Emoji( + name: 'man office worker', + char: '\u{1F468}\u{200D}\u{1F4BC}', + shortName: 'man_office_worker', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'architect', + 'business', + 'man', + 'manager', + 'office', + 'white-collar', + 'uc6', + 'diversity', + 'men', + 'job', + 'business', + 'nerd', + 'work', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious', + 'office' + ]), + Emoji( + name: 'man office worker: light skin tone', + char: '\u{1F468}\u{1F3FB}\u{200D}\u{1F4BC}', + shortName: 'man_office_worker_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'architect', + 'business', + 'light skin tone', + 'man', + 'manager', + 'office', + 'white-collar', + 'uc8', + 'diversity', + 'men', + 'job', + 'business', + 'nerd', + 'work', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious', + 'office' + ], + modifiable: true), + Emoji( + name: 'man office worker: medium-light skin tone', + char: '\u{1F468}\u{1F3FC}\u{200D}\u{1F4BC}', + shortName: 'man_office_worker_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'architect', + 'business', + 'man', + 'manager', + 'medium-light skin tone', + 'office', + 'white-collar', + 'uc8', + 'diversity', + 'men', + 'job', + 'business', + 'nerd', + 'work', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious', + 'office' + ], + modifiable: true), + Emoji( + name: 'man office worker: medium skin tone', + char: '\u{1F468}\u{1F3FD}\u{200D}\u{1F4BC}', + shortName: 'man_office_worker_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'architect', + 'business', + 'man', + 'manager', + 'medium skin tone', + 'office', + 'white-collar', + 'uc8', + 'diversity', + 'men', + 'job', + 'business', + 'nerd', + 'work', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious', + 'office' + ], + modifiable: true), + Emoji( + name: 'man office worker: medium-dark skin tone', + char: '\u{1F468}\u{1F3FE}\u{200D}\u{1F4BC}', + shortName: 'man_office_worker_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'architect', + 'business', + 'man', + 'manager', + 'medium-dark skin tone', + 'office', + 'white-collar', + 'uc8', + 'diversity', + 'men', + 'job', + 'business', + 'nerd', + 'work', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious', + 'office' + ], + modifiable: true), + Emoji( + name: 'man office worker: dark skin tone', + char: '\u{1F468}\u{1F3FF}\u{200D}\u{1F4BC}', + shortName: 'man_office_worker_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'architect', + 'business', + 'dark skin tone', + 'man', + 'manager', + 'office', + 'white-collar', + 'uc8', + 'diversity', + 'men', + 'job', + 'business', + 'nerd', + 'work', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious', + 'office' + ], + modifiable: true), + Emoji( + name: 'mechanic', + char: '\u{1F9D1}\u{200D}\u{1F527}', + shortName: 'mechanic', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: ['uc12', 'job', 'profession', 'boss', 'career']), + Emoji( + name: 'mechanic: light skin tone', + char: '\u{1F9D1}\u{1F3FB}\u{200D}\u{1F527}', + shortName: 'mechanic_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: ['uc12', 'job', 'profession', 'boss', 'career'], + modifiable: true), + Emoji( + name: 'mechanic: medium-light skin tone', + char: '\u{1F9D1}\u{1F3FC}\u{200D}\u{1F527}', + shortName: 'mechanic_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: ['uc12', 'job', 'profession', 'boss', 'career'], + modifiable: true), + Emoji( + name: 'mechanic: medium skin tone', + char: '\u{1F9D1}\u{1F3FD}\u{200D}\u{1F527}', + shortName: 'mechanic_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: ['uc12', 'job', 'profession', 'boss', 'career'], + modifiable: true), + Emoji( + name: 'mechanic: medium-dark skin tone', + char: '\u{1F9D1}\u{1F3FE}\u{200D}\u{1F527}', + shortName: 'mechanic_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: ['uc12', 'job', 'profession', 'boss', 'career'], + modifiable: true), + Emoji( + name: 'mechanic: dark skin tone', + char: '\u{1F9D1}\u{1F3FF}\u{200D}\u{1F527}', + shortName: 'mechanic_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: ['uc12', 'job', 'profession', 'boss', 'career'], + modifiable: true), + Emoji( + name: 'woman mechanic', + char: '\u{1F469}\u{200D}\u{1F527}', + shortName: 'woman_mechanic', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'electrician', + 'mechanic', + 'plumber', + 'tradesperson', + 'woman', + 'uc6', + 'diversity', + 'job', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career' + ]), + Emoji( + name: 'woman mechanic: light skin tone', + char: '\u{1F469}\u{1F3FB}\u{200D}\u{1F527}', + shortName: 'woman_mechanic_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'electrician', + 'light skin tone', + 'mechanic', + 'plumber', + 'tradesperson', + 'woman', + 'uc8', + 'diversity', + 'job', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'woman mechanic: medium-light skin tone', + char: '\u{1F469}\u{1F3FC}\u{200D}\u{1F527}', + shortName: 'woman_mechanic_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'electrician', + 'mechanic', + 'medium-light skin tone', + 'plumber', + 'tradesperson', + 'woman', + 'uc8', + 'diversity', + 'job', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'woman mechanic: medium skin tone', + char: '\u{1F469}\u{1F3FD}\u{200D}\u{1F527}', + shortName: 'woman_mechanic_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'electrician', + 'mechanic', + 'medium skin tone', + 'plumber', + 'tradesperson', + 'woman', + 'uc8', + 'diversity', + 'job', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'woman mechanic: medium-dark skin tone', + char: '\u{1F469}\u{1F3FE}\u{200D}\u{1F527}', + shortName: 'woman_mechanic_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'electrician', + 'mechanic', + 'medium-dark skin tone', + 'plumber', + 'tradesperson', + 'woman', + 'uc8', + 'diversity', + 'job', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'woman mechanic: dark skin tone', + char: '\u{1F469}\u{1F3FF}\u{200D}\u{1F527}', + shortName: 'woman_mechanic_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'dark skin tone', + 'electrician', + 'mechanic', + 'plumber', + 'tradesperson', + 'woman', + 'uc8', + 'diversity', + 'job', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'man mechanic', + char: '\u{1F468}\u{200D}\u{1F527}', + shortName: 'man_mechanic', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'electrician', + 'man', + 'mechanic', + 'plumber', + 'tradesperson', + 'uc6', + 'diversity', + 'job', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career' + ]), + Emoji( + name: 'man mechanic: light skin tone', + char: '\u{1F468}\u{1F3FB}\u{200D}\u{1F527}', + shortName: 'man_mechanic_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'electrician', + 'light skin tone', + 'man', + 'mechanic', + 'plumber', + 'tradesperson', + 'uc8', + 'diversity', + 'job', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'man mechanic: medium-light skin tone', + char: '\u{1F468}\u{1F3FC}\u{200D}\u{1F527}', + shortName: 'man_mechanic_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'electrician', + 'man', + 'mechanic', + 'medium-light skin tone', + 'plumber', + 'tradesperson', + 'uc8', + 'diversity', + 'job', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'man mechanic: medium skin tone', + char: '\u{1F468}\u{1F3FD}\u{200D}\u{1F527}', + shortName: 'man_mechanic_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'electrician', + 'man', + 'mechanic', + 'medium skin tone', + 'plumber', + 'tradesperson', + 'uc8', + 'diversity', + 'job', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'man mechanic: medium-dark skin tone', + char: '\u{1F468}\u{1F3FE}\u{200D}\u{1F527}', + shortName: 'man_mechanic_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'electrician', + 'man', + 'mechanic', + 'medium-dark skin tone', + 'plumber', + 'tradesperson', + 'uc8', + 'diversity', + 'job', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'man mechanic: dark skin tone', + char: '\u{1F468}\u{1F3FF}\u{200D}\u{1F527}', + shortName: 'man_mechanic_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'dark skin tone', + 'electrician', + 'man', + 'mechanic', + 'plumber', + 'tradesperson', + 'uc8', + 'diversity', + 'job', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'scientist', + char: '\u{1F9D1}\u{200D}\u{1F52C}', + shortName: 'scientist', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'science', + 'job', + 'nerd', + 'lab', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious' + ]), + Emoji( + name: 'scientist: light skin tone', + char: '\u{1F9D1}\u{1F3FB}\u{200D}\u{1F52C}', + shortName: 'scientist_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'science', + 'job', + 'nerd', + 'lab', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'scientist: medium-light skin tone', + char: '\u{1F9D1}\u{1F3FC}\u{200D}\u{1F52C}', + shortName: 'scientist_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'science', + 'job', + 'nerd', + 'lab', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'scientist: medium skin tone', + char: '\u{1F9D1}\u{1F3FD}\u{200D}\u{1F52C}', + shortName: 'scientist_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'science', + 'job', + 'nerd', + 'lab', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'scientist: medium-dark skin tone', + char: '\u{1F9D1}\u{1F3FE}\u{200D}\u{1F52C}', + shortName: 'scientist_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'science', + 'job', + 'nerd', + 'lab', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'scientist: dark skin tone', + char: '\u{1F9D1}\u{1F3FF}\u{200D}\u{1F52C}', + shortName: 'scientist_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'science', + 'job', + 'nerd', + 'lab', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'woman scientist', + char: '\u{1F469}\u{200D}\u{1F52C}', + shortName: 'woman_scientist', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'biologist', + 'chemist', + 'engineer', + 'mathematician', + 'physicist', + 'scientist', + 'woman', + 'uc6', + 'diversity', + 'science', + 'job', + 'nerd', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'lab', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious' + ]), + Emoji( + name: 'woman scientist: light skin tone', + char: '\u{1F469}\u{1F3FB}\u{200D}\u{1F52C}', + shortName: 'woman_scientist_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'biologist', + 'chemist', + 'engineer', + 'light skin tone', + 'mathematician', + 'physicist', + 'scientist', + 'woman', + 'uc8', + 'diversity', + 'science', + 'job', + 'nerd', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'lab', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'woman scientist: medium-light skin tone', + char: '\u{1F469}\u{1F3FC}\u{200D}\u{1F52C}', + shortName: 'woman_scientist_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'biologist', + 'chemist', + 'engineer', + 'mathematician', + 'medium-light skin tone', + 'physicist', + 'scientist', + 'woman', + 'uc8', + 'diversity', + 'science', + 'job', + 'nerd', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'lab', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'woman scientist: medium skin tone', + char: '\u{1F469}\u{1F3FD}\u{200D}\u{1F52C}', + shortName: 'woman_scientist_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'biologist', + 'chemist', + 'engineer', + 'mathematician', + 'medium skin tone', + 'physicist', + 'scientist', + 'woman', + 'uc8', + 'diversity', + 'science', + 'job', + 'nerd', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'lab', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'woman scientist: medium-dark skin tone', + char: '\u{1F469}\u{1F3FE}\u{200D}\u{1F52C}', + shortName: 'woman_scientist_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'biologist', + 'chemist', + 'engineer', + 'mathematician', + 'medium-dark skin tone', + 'physicist', + 'scientist', + 'woman', + 'uc8', + 'diversity', + 'science', + 'job', + 'nerd', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'lab', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'woman scientist: dark skin tone', + char: '\u{1F469}\u{1F3FF}\u{200D}\u{1F52C}', + shortName: 'woman_scientist_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'biologist', + 'chemist', + 'dark skin tone', + 'engineer', + 'mathematician', + 'physicist', + 'scientist', + 'woman', + 'uc8', + 'diversity', + 'science', + 'job', + 'nerd', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'lab', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'man scientist', + char: '\u{1F468}\u{200D}\u{1F52C}', + shortName: 'man_scientist', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'biologist', + 'chemist', + 'engineer', + 'man', + 'mathematician', + 'physicist', + 'scientist', + 'uc6', + 'diversity', + 'science', + 'job', + 'nerd', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'lab', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious' + ]), + Emoji( + name: 'man scientist: light skin tone', + char: '\u{1F468}\u{1F3FB}\u{200D}\u{1F52C}', + shortName: 'man_scientist_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'biologist', + 'chemist', + 'engineer', + 'light skin tone', + 'man', + 'mathematician', + 'physicist', + 'scientist', + 'uc8', + 'diversity', + 'science', + 'job', + 'nerd', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'lab', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'man scientist: medium-light skin tone', + char: '\u{1F468}\u{1F3FC}\u{200D}\u{1F52C}', + shortName: 'man_scientist_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'biologist', + 'chemist', + 'engineer', + 'man', + 'mathematician', + 'medium-light skin tone', + 'physicist', + 'scientist', + 'uc8', + 'diversity', + 'science', + 'job', + 'nerd', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'lab', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'man scientist: medium skin tone', + char: '\u{1F468}\u{1F3FD}\u{200D}\u{1F52C}', + shortName: 'man_scientist_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'biologist', + 'chemist', + 'engineer', + 'man', + 'mathematician', + 'medium skin tone', + 'physicist', + 'scientist', + 'uc8', + 'diversity', + 'science', + 'job', + 'nerd', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'lab', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'man scientist: medium-dark skin tone', + char: '\u{1F468}\u{1F3FE}\u{200D}\u{1F52C}', + shortName: 'man_scientist_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'biologist', + 'chemist', + 'engineer', + 'man', + 'mathematician', + 'medium-dark skin tone', + 'physicist', + 'scientist', + 'uc8', + 'diversity', + 'science', + 'job', + 'nerd', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'lab', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'man scientist: dark skin tone', + char: '\u{1F468}\u{1F3FF}\u{200D}\u{1F52C}', + shortName: 'man_scientist_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'biologist', + 'chemist', + 'dark skin tone', + 'engineer', + 'man', + 'mathematician', + 'physicist', + 'scientist', + 'uc8', + 'diversity', + 'science', + 'job', + 'nerd', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'lab', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'artist', + char: '\u{1F9D1}\u{200D}\u{1F3A8}', + shortName: 'artist', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'job', + 'painting', + 'artist', + 'profession', + 'boss', + 'career', + 'painter', + 'arts' + ]), + Emoji( + name: 'artist: light skin tone', + char: '\u{1F9D1}\u{1F3FB}\u{200D}\u{1F3A8}', + shortName: 'artist_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'job', + 'painting', + 'artist', + 'profession', + 'boss', + 'career', + 'painter', + 'arts' + ], + modifiable: true), + Emoji( + name: 'artist: medium-light skin tone', + char: '\u{1F9D1}\u{1F3FC}\u{200D}\u{1F3A8}', + shortName: 'artist_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'job', + 'painting', + 'artist', + 'profession', + 'boss', + 'career', + 'painter', + 'arts' + ], + modifiable: true), + Emoji( + name: 'artist: medium skin tone', + char: '\u{1F9D1}\u{1F3FD}\u{200D}\u{1F3A8}', + shortName: 'artist_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'job', + 'painting', + 'artist', + 'profession', + 'boss', + 'career', + 'painter', + 'arts' + ], + modifiable: true), + Emoji( + name: 'artist: medium-dark skin tone', + char: '\u{1F9D1}\u{1F3FE}\u{200D}\u{1F3A8}', + shortName: 'artist_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'job', + 'painting', + 'artist', + 'profession', + 'boss', + 'career', + 'painter', + 'arts' + ], + modifiable: true), + Emoji( + name: 'artist: dark skin tone', + char: '\u{1F9D1}\u{1F3FF}\u{200D}\u{1F3A8}', + shortName: 'artist_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'job', + 'painting', + 'artist', + 'profession', + 'boss', + 'career', + 'painter', + 'arts' + ], + modifiable: true), + Emoji( + name: 'woman artist', + char: '\u{1F469}\u{200D}\u{1F3A8}', + shortName: 'woman_artist', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'artist', + 'palette', + 'woman', + 'uc6', + 'diversity', + 'job', + 'painting', + 'artist', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'painter', + 'arts' + ]), + Emoji( + name: 'woman artist: light skin tone', + char: '\u{1F469}\u{1F3FB}\u{200D}\u{1F3A8}', + shortName: 'woman_artist_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'artist', + 'light skin tone', + 'palette', + 'woman', + 'uc8', + 'diversity', + 'job', + 'painting', + 'artist', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'painter', + 'arts' + ], + modifiable: true), + Emoji( + name: 'woman artist: medium-light skin tone', + char: '\u{1F469}\u{1F3FC}\u{200D}\u{1F3A8}', + shortName: 'woman_artist_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'artist', + 'medium-light skin tone', + 'palette', + 'woman', + 'uc8', + 'diversity', + 'job', + 'painting', + 'artist', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'painter', + 'arts' + ], + modifiable: true), + Emoji( + name: 'woman artist: medium skin tone', + char: '\u{1F469}\u{1F3FD}\u{200D}\u{1F3A8}', + shortName: 'woman_artist_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'artist', + 'medium skin tone', + 'palette', + 'woman', + 'uc8', + 'diversity', + 'job', + 'painting', + 'artist', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'painter', + 'arts' + ], + modifiable: true), + Emoji( + name: 'woman artist: medium-dark skin tone', + char: '\u{1F469}\u{1F3FE}\u{200D}\u{1F3A8}', + shortName: 'woman_artist_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'artist', + 'medium-dark skin tone', + 'palette', + 'woman', + 'uc8', + 'diversity', + 'job', + 'painting', + 'artist', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'painter', + 'arts' + ], + modifiable: true), + Emoji( + name: 'woman artist: dark skin tone', + char: '\u{1F469}\u{1F3FF}\u{200D}\u{1F3A8}', + shortName: 'woman_artist_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'artist', + 'dark skin tone', + 'palette', + 'woman', + 'uc8', + 'diversity', + 'job', + 'painting', + 'artist', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'painter', + 'arts' + ], + modifiable: true), + Emoji( + name: 'man artist', + char: '\u{1F468}\u{200D}\u{1F3A8}', + shortName: 'man_artist', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'artist', + 'man', + 'palette', + 'uc6', + 'diversity', + 'job', + 'painting', + 'artist', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'painter', + 'arts' + ]), + Emoji( + name: 'man artist: light skin tone', + char: '\u{1F468}\u{1F3FB}\u{200D}\u{1F3A8}', + shortName: 'man_artist_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'artist', + 'light skin tone', + 'man', + 'palette', + 'uc8', + 'diversity', + 'job', + 'painting', + 'artist', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'painter', + 'arts' + ], + modifiable: true), + Emoji( + name: 'man artist: medium-light skin tone', + char: '\u{1F468}\u{1F3FC}\u{200D}\u{1F3A8}', + shortName: 'man_artist_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'artist', + 'man', + 'medium-light skin tone', + 'palette', + 'uc8', + 'diversity', + 'job', + 'painting', + 'artist', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'painter', + 'arts' + ], + modifiable: true), + Emoji( + name: 'man artist: medium skin tone', + char: '\u{1F468}\u{1F3FD}\u{200D}\u{1F3A8}', + shortName: 'man_artist_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'artist', + 'man', + 'medium skin tone', + 'palette', + 'uc8', + 'diversity', + 'job', + 'painting', + 'artist', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'painter', + 'arts' + ], + modifiable: true), + Emoji( + name: 'man artist: medium-dark skin tone', + char: '\u{1F468}\u{1F3FE}\u{200D}\u{1F3A8}', + shortName: 'man_artist_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'artist', + 'man', + 'medium-dark skin tone', + 'palette', + 'uc8', + 'diversity', + 'job', + 'painting', + 'artist', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'painter', + 'arts' + ], + modifiable: true), + Emoji( + name: 'man artist: dark skin tone', + char: '\u{1F468}\u{1F3FF}\u{200D}\u{1F3A8}', + shortName: 'man_artist_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'artist', + 'dark skin tone', + 'man', + 'palette', + 'uc8', + 'diversity', + 'job', + 'painting', + 'artist', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'painter', + 'arts' + ], + modifiable: true), + Emoji( + name: 'firefighter', + char: '\u{1F9D1}\u{200D}\u{1F692}', + shortName: 'firefighter', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'job', + '911', + 'help', + 'handsome', + 'fires', + 'helmet', + 'disguise', + 'profession', + 'boss', + 'career', + 'emergency', + 'injury', + 'stud' + ]), + Emoji( + name: 'firefighter: light skin tone', + char: '\u{1F9D1}\u{1F3FB}\u{200D}\u{1F692}', + shortName: 'firefighter_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'job', + '911', + 'help', + 'handsome', + 'fires', + 'helmet', + 'disguise', + 'profession', + 'boss', + 'career', + 'emergency', + 'injury', + 'stud' + ], + modifiable: true), + Emoji( + name: 'firefighter: medium-light skin tone', + char: '\u{1F9D1}\u{1F3FC}\u{200D}\u{1F692}', + shortName: 'firefighter_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'job', + '911', + 'help', + 'handsome', + 'fires', + 'helmet', + 'disguise', + 'profession', + 'boss', + 'career', + 'emergency', + 'injury', + 'stud' + ], + modifiable: true), + Emoji( + name: 'firefighter: medium skin tone', + char: '\u{1F9D1}\u{1F3FD}\u{200D}\u{1F692}', + shortName: 'firefighter_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'job', + '911', + 'help', + 'handsome', + 'fires', + 'helmet', + 'disguise', + 'profession', + 'boss', + 'career', + 'emergency', + 'injury', + 'stud' + ], + modifiable: true), + Emoji( + name: 'firefighter: medium-dark skin tone', + char: '\u{1F9D1}\u{1F3FE}\u{200D}\u{1F692}', + shortName: 'firefighter_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'job', + '911', + 'help', + 'handsome', + 'fires', + 'helmet', + 'disguise', + 'profession', + 'boss', + 'career', + 'emergency', + 'injury', + 'stud' + ], + modifiable: true), + Emoji( + name: 'firefighter: dark skin tone', + char: '\u{1F9D1}\u{1F3FF}\u{200D}\u{1F692}', + shortName: 'firefighter_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'job', + '911', + 'help', + 'handsome', + 'fires', + 'helmet', + 'disguise', + 'profession', + 'boss', + 'career', + 'emergency', + 'injury', + 'stud' + ], + modifiable: true), + Emoji( + name: 'woman firefighter', + char: '\u{1F469}\u{200D}\u{1F692}', + shortName: 'woman_firefighter', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'firefighter', + 'firetruck', + 'woman', + 'uc6', + 'diversity', + 'job', + '911', + 'help', + 'fires', + 'helmet', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'emergency', + 'injury' + ]), + Emoji( + name: 'woman firefighter: light skin tone', + char: '\u{1F469}\u{1F3FB}\u{200D}\u{1F692}', + shortName: 'woman_firefighter_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'firefighter', + 'firetruck', + 'light skin tone', + 'woman', + 'uc8', + 'diversity', + 'job', + '911', + 'help', + 'fires', + 'helmet', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'emergency', + 'injury' + ], + modifiable: true), + Emoji( + name: 'woman firefighter: medium-light skin tone', + char: '\u{1F469}\u{1F3FC}\u{200D}\u{1F692}', + shortName: 'woman_firefighter_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'firefighter', + 'firetruck', + 'medium-light skin tone', + 'woman', + 'uc8', + 'diversity', + 'job', + '911', + 'help', + 'fires', + 'helmet', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'emergency', + 'injury' + ], + modifiable: true), + Emoji( + name: 'woman firefighter: medium skin tone', + char: '\u{1F469}\u{1F3FD}\u{200D}\u{1F692}', + shortName: 'woman_firefighter_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'firefighter', + 'firetruck', + 'medium skin tone', + 'woman', + 'uc8', + 'diversity', + 'job', + '911', + 'help', + 'fires', + 'helmet', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'emergency', + 'injury' + ], + modifiable: true), + Emoji( + name: 'woman firefighter: medium-dark skin tone', + char: '\u{1F469}\u{1F3FE}\u{200D}\u{1F692}', + shortName: 'woman_firefighter_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'firefighter', + 'firetruck', + 'medium-dark skin tone', + 'woman', + 'uc8', + 'diversity', + 'job', + '911', + 'help', + 'fires', + 'helmet', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'emergency', + 'injury' + ], + modifiable: true), + Emoji( + name: 'woman firefighter: dark skin tone', + char: '\u{1F469}\u{1F3FF}\u{200D}\u{1F692}', + shortName: 'woman_firefighter_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'dark skin tone', + 'firefighter', + 'firetruck', + 'woman', + 'uc8', + 'diversity', + 'job', + '911', + 'help', + 'fires', + 'helmet', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'emergency', + 'injury' + ], + modifiable: true), + Emoji( + name: 'man firefighter', + char: '\u{1F468}\u{200D}\u{1F692}', + shortName: 'man_firefighter', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'firefighter', + 'firetruck', + 'man', + 'uc6', + 'diversity', + 'job', + '911', + 'help', + 'handsome', + 'fires', + 'helmet', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'emergency', + 'injury', + 'stud' + ]), + Emoji( + name: 'man firefighter: light skin tone', + char: '\u{1F468}\u{1F3FB}\u{200D}\u{1F692}', + shortName: 'man_firefighter_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'firefighter', + 'firetruck', + 'light skin tone', + 'man', + 'uc8', + 'diversity', + 'job', + '911', + 'help', + 'handsome', + 'fires', + 'helmet', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'emergency', + 'injury', + 'stud' + ], + modifiable: true), + Emoji( + name: 'man firefighter: medium-light skin tone', + char: '\u{1F468}\u{1F3FC}\u{200D}\u{1F692}', + shortName: 'man_firefighter_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'firefighter', + 'firetruck', + 'man', + 'medium-light skin tone', + 'uc8', + 'diversity', + 'job', + '911', + 'help', + 'handsome', + 'fires', + 'helmet', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'emergency', + 'injury', + 'stud' + ], + modifiable: true), + Emoji( + name: 'man firefighter: medium skin tone', + char: '\u{1F468}\u{1F3FD}\u{200D}\u{1F692}', + shortName: 'man_firefighter_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'firefighter', + 'firetruck', + 'man', + 'medium skin tone', + 'uc8', + 'diversity', + 'job', + '911', + 'help', + 'handsome', + 'fires', + 'helmet', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'emergency', + 'injury', + 'stud' + ], + modifiable: true), + Emoji( + name: 'man firefighter: medium-dark skin tone', + char: '\u{1F468}\u{1F3FE}\u{200D}\u{1F692}', + shortName: 'man_firefighter_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'firefighter', + 'firetruck', + 'man', + 'medium-dark skin tone', + 'uc8', + 'diversity', + 'job', + '911', + 'help', + 'handsome', + 'fires', + 'helmet', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'emergency', + 'injury', + 'stud' + ], + modifiable: true), + Emoji( + name: 'man firefighter: dark skin tone', + char: '\u{1F468}\u{1F3FF}\u{200D}\u{1F692}', + shortName: 'man_firefighter_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'dark skin tone', + 'firefighter', + 'firetruck', + 'man', + 'uc8', + 'diversity', + 'job', + '911', + 'help', + 'handsome', + 'fires', + 'helmet', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'emergency', + 'injury', + 'stud' + ], + modifiable: true), + Emoji( + name: 'pilot', + char: '\u{1F9D1}\u{200D}\u{2708}\u{FE0F}', + shortName: 'pilot', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'fly', + 'job', + 'airplane', + 'handsome', + 'disguise', + 'flight', + 'flying', + 'flights', + 'avion', + 'profession', + 'boss', + 'career', + 'airline', + 'aircraft', + 'airforce', + 'air force', + 'airport', + 'stud' + ]), + Emoji( + name: 'pilot: light skin tone', + char: '\u{1F9D1}\u{1F3FB}\u{200D}\u{2708}\u{FE0F}', + shortName: 'pilot_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'fly', + 'job', + 'airplane', + 'handsome', + 'disguise', + 'flight', + 'flying', + 'flights', + 'avion', + 'profession', + 'boss', + 'career', + 'airline', + 'aircraft', + 'airforce', + 'air force', + 'airport', + 'stud' + ], + modifiable: true), + Emoji( + name: 'pilot: medium-light skin tone', + char: '\u{1F9D1}\u{1F3FC}\u{200D}\u{2708}\u{FE0F}', + shortName: 'pilot_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'fly', + 'job', + 'airplane', + 'handsome', + 'disguise', + 'flight', + 'flying', + 'flights', + 'avion', + 'profession', + 'boss', + 'career', + 'airline', + 'aircraft', + 'airforce', + 'air force', + 'airport', + 'stud' + ], + modifiable: true), + Emoji( + name: 'pilot: medium skin tone', + char: '\u{1F9D1}\u{1F3FD}\u{200D}\u{2708}\u{FE0F}', + shortName: 'pilot_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'fly', + 'job', + 'airplane', + 'handsome', + 'disguise', + 'flight', + 'flying', + 'flights', + 'avion', + 'profession', + 'boss', + 'career', + 'airline', + 'aircraft', + 'airforce', + 'air force', + 'airport', + 'stud' + ], + modifiable: true), + Emoji( + name: 'pilot: medium-dark skin tone', + char: '\u{1F9D1}\u{1F3FE}\u{200D}\u{2708}\u{FE0F}', + shortName: 'pilot_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'fly', + 'job', + 'airplane', + 'handsome', + 'disguise', + 'flight', + 'flying', + 'flights', + 'avion', + 'profession', + 'boss', + 'career', + 'airline', + 'aircraft', + 'airforce', + 'air force', + 'airport', + 'stud' + ], + modifiable: true), + Emoji( + name: 'pilot: dark skin tone', + char: '\u{1F9D1}\u{1F3FF}\u{200D}\u{2708}\u{FE0F}', + shortName: 'pilot_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'fly', + 'job', + 'airplane', + 'handsome', + 'disguise', + 'flight', + 'flying', + 'flights', + 'avion', + 'profession', + 'boss', + 'career', + 'airline', + 'aircraft', + 'airforce', + 'air force', + 'airport', + 'stud' + ], + modifiable: true), + Emoji( + name: 'woman pilot', + char: '\u{1F469}\u{200D}\u{2708}\u{FE0F}', + shortName: 'woman_pilot', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'pilot', + 'plane', + 'woman', + 'uc6', + 'diversity', + 'fly', + 'job', + 'airplane', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'flight', + 'flying', + 'flights', + 'avion', + 'profession', + 'boss', + 'career', + 'airline', + 'aircraft', + 'airforce', + 'air force', + 'airport' + ]), + Emoji( + name: 'woman pilot: light skin tone', + char: '\u{1F469}\u{1F3FB}\u{200D}\u{2708}\u{FE0F}', + shortName: 'woman_pilot_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'light skin tone', + 'pilot', + 'plane', + 'woman', + 'uc8', + 'diversity', + 'fly', + 'job', + 'airplane', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'flight', + 'flying', + 'flights', + 'avion', + 'profession', + 'boss', + 'career', + 'airline', + 'aircraft', + 'airforce', + 'air force', + 'airport' + ], + modifiable: true), + Emoji( + name: 'woman pilot: medium-light skin tone', + char: '\u{1F469}\u{1F3FC}\u{200D}\u{2708}\u{FE0F}', + shortName: 'woman_pilot_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'medium-light skin tone', + 'pilot', + 'plane', + 'woman', + 'uc8', + 'diversity', + 'fly', + 'job', + 'airplane', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'flight', + 'flying', + 'flights', + 'avion', + 'profession', + 'boss', + 'career', + 'airline', + 'aircraft', + 'airforce', + 'air force', + 'airport' + ], + modifiable: true), + Emoji( + name: 'woman pilot: medium skin tone', + char: '\u{1F469}\u{1F3FD}\u{200D}\u{2708}\u{FE0F}', + shortName: 'woman_pilot_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'medium skin tone', + 'pilot', + 'plane', + 'woman', + 'uc8', + 'diversity', + 'fly', + 'job', + 'airplane', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'flight', + 'flying', + 'flights', + 'avion', + 'profession', + 'boss', + 'career', + 'airline', + 'aircraft', + 'airforce', + 'air force', + 'airport' + ], + modifiable: true), + Emoji( + name: 'woman pilot: medium-dark skin tone', + char: '\u{1F469}\u{1F3FE}\u{200D}\u{2708}\u{FE0F}', + shortName: 'woman_pilot_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'medium-dark skin tone', + 'pilot', + 'plane', + 'woman', + 'uc8', + 'diversity', + 'fly', + 'job', + 'airplane', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'flight', + 'flying', + 'flights', + 'avion', + 'profession', + 'boss', + 'career', + 'airline', + 'aircraft', + 'airforce', + 'air force', + 'airport' + ], + modifiable: true), + Emoji( + name: 'woman pilot: dark skin tone', + char: '\u{1F469}\u{1F3FF}\u{200D}\u{2708}\u{FE0F}', + shortName: 'woman_pilot_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'dark skin tone', + 'pilot', + 'plane', + 'woman', + 'uc8', + 'diversity', + 'fly', + 'job', + 'airplane', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'flight', + 'flying', + 'flights', + 'avion', + 'profession', + 'boss', + 'career', + 'airline', + 'aircraft', + 'airforce', + 'air force', + 'airport' + ], + modifiable: true), + Emoji( + name: 'man pilot', + char: '\u{1F468}\u{200D}\u{2708}\u{FE0F}', + shortName: 'man_pilot', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'man', + 'pilot', + 'plane', + 'uc6', + 'diversity', + 'fly', + 'job', + 'airplane', + 'handsome', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'flight', + 'flying', + 'flights', + 'avion', + 'profession', + 'boss', + 'career', + 'airline', + 'aircraft', + 'airforce', + 'air force', + 'airport', + 'stud' + ]), + Emoji( + name: 'man pilot: light skin tone', + char: '\u{1F468}\u{1F3FB}\u{200D}\u{2708}\u{FE0F}', + shortName: 'man_pilot_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'light skin tone', + 'man', + 'pilot', + 'plane', + 'uc8', + 'diversity', + 'fly', + 'job', + 'airplane', + 'handsome', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'flight', + 'flying', + 'flights', + 'avion', + 'profession', + 'boss', + 'career', + 'airline', + 'aircraft', + 'airforce', + 'air force', + 'airport', + 'stud' + ], + modifiable: true), + Emoji( + name: 'man pilot: medium-light skin tone', + char: '\u{1F468}\u{1F3FC}\u{200D}\u{2708}\u{FE0F}', + shortName: 'man_pilot_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'man', + 'medium-light skin tone', + 'pilot', + 'plane', + 'uc8', + 'diversity', + 'fly', + 'job', + 'airplane', + 'handsome', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'flight', + 'flying', + 'flights', + 'avion', + 'profession', + 'boss', + 'career', + 'airline', + 'aircraft', + 'airforce', + 'air force', + 'airport', + 'stud' + ], + modifiable: true), + Emoji( + name: 'man pilot: medium skin tone', + char: '\u{1F468}\u{1F3FD}\u{200D}\u{2708}\u{FE0F}', + shortName: 'man_pilot_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'man', + 'medium skin tone', + 'pilot', + 'plane', + 'uc8', + 'diversity', + 'fly', + 'job', + 'airplane', + 'handsome', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'flight', + 'flying', + 'flights', + 'avion', + 'profession', + 'boss', + 'career', + 'airline', + 'aircraft', + 'airforce', + 'air force', + 'airport', + 'stud' + ], + modifiable: true), + Emoji( + name: 'man pilot: medium-dark skin tone', + char: '\u{1F468}\u{1F3FE}\u{200D}\u{2708}\u{FE0F}', + shortName: 'man_pilot_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'man', + 'medium-dark skin tone', + 'pilot', + 'plane', + 'uc8', + 'diversity', + 'fly', + 'job', + 'airplane', + 'handsome', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'flight', + 'flying', + 'flights', + 'avion', + 'profession', + 'boss', + 'career', + 'airline', + 'aircraft', + 'airforce', + 'air force', + 'airport', + 'stud' + ], + modifiable: true), + Emoji( + name: 'man pilot: dark skin tone', + char: '\u{1F468}\u{1F3FF}\u{200D}\u{2708}\u{FE0F}', + shortName: 'man_pilot_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'dark skin tone', + 'man', + 'pilot', + 'plane', + 'uc8', + 'diversity', + 'fly', + 'job', + 'airplane', + 'handsome', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'flight', + 'flying', + 'flights', + 'avion', + 'profession', + 'boss', + 'career', + 'airline', + 'aircraft', + 'airforce', + 'air force', + 'airport', + 'stud' + ], + modifiable: true), + Emoji( + name: 'astronaut', + char: '\u{1F9D1}\u{200D}\u{1F680}', + shortName: 'astronaut', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'space', + 'job', + 'helmet', + 'disguise', + 'outer space', + 'galaxy', + 'universe', + 'nasa', + 'spaceship', + 'profession', + 'boss', + 'career' + ]), + Emoji( + name: 'astronaut: light skin tone', + char: '\u{1F9D1}\u{1F3FB}\u{200D}\u{1F680}', + shortName: 'astronaut_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'space', + 'job', + 'helmet', + 'disguise', + 'outer space', + 'galaxy', + 'universe', + 'nasa', + 'spaceship', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'astronaut: medium-light skin tone', + char: '\u{1F9D1}\u{1F3FC}\u{200D}\u{1F680}', + shortName: 'astronaut_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'space', + 'job', + 'helmet', + 'disguise', + 'outer space', + 'galaxy', + 'universe', + 'nasa', + 'spaceship', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'astronaut: medium skin tone', + char: '\u{1F9D1}\u{1F3FD}\u{200D}\u{1F680}', + shortName: 'astronaut_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'space', + 'job', + 'helmet', + 'disguise', + 'outer space', + 'galaxy', + 'universe', + 'nasa', + 'spaceship', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'astronaut: medium-dark skin tone', + char: '\u{1F9D1}\u{1F3FE}\u{200D}\u{1F680}', + shortName: 'astronaut_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'space', + 'job', + 'helmet', + 'disguise', + 'outer space', + 'galaxy', + 'universe', + 'nasa', + 'spaceship', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'astronaut: dark skin tone', + char: '\u{1F9D1}\u{1F3FF}\u{200D}\u{1F680}', + shortName: 'astronaut_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'space', + 'job', + 'helmet', + 'disguise', + 'outer space', + 'galaxy', + 'universe', + 'nasa', + 'spaceship', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'woman astronaut', + char: '\u{1F469}\u{200D}\u{1F680}', + shortName: 'woman_astronaut', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'astronaut', + 'rocket', + 'woman', + 'uc6', + 'diversity', + 'space', + 'job', + 'helmet', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'outer space', + 'galaxy', + 'universe', + 'nasa', + 'spaceship', + 'profession', + 'boss', + 'career' + ]), + Emoji( + name: 'woman astronaut: light skin tone', + char: '\u{1F469}\u{1F3FB}\u{200D}\u{1F680}', + shortName: 'woman_astronaut_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'astronaut', + 'light skin tone', + 'rocket', + 'woman', + 'uc8', + 'diversity', + 'space', + 'job', + 'helmet', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'outer space', + 'galaxy', + 'universe', + 'nasa', + 'spaceship', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'woman astronaut: medium-light skin tone', + char: '\u{1F469}\u{1F3FC}\u{200D}\u{1F680}', + shortName: 'woman_astronaut_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'astronaut', + 'medium-light skin tone', + 'rocket', + 'woman', + 'uc8', + 'diversity', + 'space', + 'job', + 'helmet', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'outer space', + 'galaxy', + 'universe', + 'nasa', + 'spaceship', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'woman astronaut: medium skin tone', + char: '\u{1F469}\u{1F3FD}\u{200D}\u{1F680}', + shortName: 'woman_astronaut_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'astronaut', + 'medium skin tone', + 'rocket', + 'woman', + 'uc8', + 'diversity', + 'space', + 'job', + 'helmet', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'outer space', + 'galaxy', + 'universe', + 'nasa', + 'spaceship', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'woman astronaut: medium-dark skin tone', + char: '\u{1F469}\u{1F3FE}\u{200D}\u{1F680}', + shortName: 'woman_astronaut_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'astronaut', + 'medium-dark skin tone', + 'rocket', + 'woman', + 'uc8', + 'diversity', + 'space', + 'job', + 'helmet', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'outer space', + 'galaxy', + 'universe', + 'nasa', + 'spaceship', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'woman astronaut: dark skin tone', + char: '\u{1F469}\u{1F3FF}\u{200D}\u{1F680}', + shortName: 'woman_astronaut_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'astronaut', + 'dark skin tone', + 'rocket', + 'woman', + 'uc8', + 'diversity', + 'space', + 'job', + 'helmet', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'outer space', + 'galaxy', + 'universe', + 'nasa', + 'spaceship', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'man astronaut', + char: '\u{1F468}\u{200D}\u{1F680}', + shortName: 'man_astronaut', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'astronaut', + 'man', + 'rocket', + 'uc6', + 'diversity', + 'space', + 'job', + 'helmet', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'outer space', + 'galaxy', + 'universe', + 'nasa', + 'spaceship', + 'profession', + 'boss', + 'career' + ]), + Emoji( + name: 'man astronaut: light skin tone', + char: '\u{1F468}\u{1F3FB}\u{200D}\u{1F680}', + shortName: 'man_astronaut_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'astronaut', + 'light skin tone', + 'man', + 'rocket', + 'uc8', + 'diversity', + 'space', + 'job', + 'helmet', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'outer space', + 'galaxy', + 'universe', + 'nasa', + 'spaceship', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'man astronaut: medium-light skin tone', + char: '\u{1F468}\u{1F3FC}\u{200D}\u{1F680}', + shortName: 'man_astronaut_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'astronaut', + 'man', + 'medium-light skin tone', + 'rocket', + 'uc8', + 'diversity', + 'space', + 'job', + 'helmet', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'outer space', + 'galaxy', + 'universe', + 'nasa', + 'spaceship', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'man astronaut: medium skin tone', + char: '\u{1F468}\u{1F3FD}\u{200D}\u{1F680}', + shortName: 'man_astronaut_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'astronaut', + 'man', + 'medium skin tone', + 'rocket', + 'uc8', + 'diversity', + 'space', + 'job', + 'helmet', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'outer space', + 'galaxy', + 'universe', + 'nasa', + 'spaceship', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'man astronaut: medium-dark skin tone', + char: '\u{1F468}\u{1F3FE}\u{200D}\u{1F680}', + shortName: 'man_astronaut_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'astronaut', + 'man', + 'medium-dark skin tone', + 'rocket', + 'uc8', + 'diversity', + 'space', + 'job', + 'helmet', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'outer space', + 'galaxy', + 'universe', + 'nasa', + 'spaceship', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'man astronaut: dark skin tone', + char: '\u{1F468}\u{1F3FF}\u{200D}\u{1F680}', + shortName: 'man_astronaut_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'astronaut', + 'dark skin tone', + 'man', + 'rocket', + 'uc8', + 'diversity', + 'space', + 'job', + 'helmet', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'outer space', + 'galaxy', + 'universe', + 'nasa', + 'spaceship', + 'profession', + 'boss', + 'career' + ], + modifiable: true), + Emoji( + name: 'judge', + char: '\u{1F9D1}\u{200D}\u{2696}\u{FE0F}', + shortName: 'judge', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'job', + 'nerd', + 'court', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious' + ]), + Emoji( + name: 'judge: light skin tone', + char: '\u{1F9D1}\u{1F3FB}\u{200D}\u{2696}\u{FE0F}', + shortName: 'judge_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'job', + 'nerd', + 'court', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'judge: medium-light skin tone', + char: '\u{1F9D1}\u{1F3FC}\u{200D}\u{2696}\u{FE0F}', + shortName: 'judge_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'job', + 'nerd', + 'court', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'judge: medium skin tone', + char: '\u{1F9D1}\u{1F3FD}\u{200D}\u{2696}\u{FE0F}', + shortName: 'judge_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'job', + 'nerd', + 'court', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'judge: medium-dark skin tone', + char: '\u{1F9D1}\u{1F3FE}\u{200D}\u{2696}\u{FE0F}', + shortName: 'judge_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'job', + 'nerd', + 'court', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'judge: dark skin tone', + char: '\u{1F9D1}\u{1F3FF}\u{200D}\u{2696}\u{FE0F}', + shortName: 'judge_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc12', + 'job', + 'nerd', + 'court', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'woman judge', + char: '\u{1F469}\u{200D}\u{2696}\u{FE0F}', + shortName: 'woman_judge', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'judge', + 'scales', + 'woman', + 'uc6', + 'diversity', + 'job', + 'nerd', + 'court', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious' + ]), + Emoji( + name: 'woman judge: light skin tone', + char: '\u{1F469}\u{1F3FB}\u{200D}\u{2696}\u{FE0F}', + shortName: 'woman_judge_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'judge', + 'light skin tone', + 'scales', + 'woman', + 'uc8', + 'diversity', + 'job', + 'nerd', + 'court', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'woman judge: medium-light skin tone', + char: '\u{1F469}\u{1F3FC}\u{200D}\u{2696}\u{FE0F}', + shortName: 'woman_judge_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'judge', + 'medium-light skin tone', + 'scales', + 'woman', + 'uc8', + 'diversity', + 'job', + 'nerd', + 'court', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'woman judge: medium skin tone', + char: '\u{1F469}\u{1F3FD}\u{200D}\u{2696}\u{FE0F}', + shortName: 'woman_judge_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'judge', + 'medium skin tone', + 'scales', + 'woman', + 'uc8', + 'diversity', + 'job', + 'nerd', + 'court', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'woman judge: medium-dark skin tone', + char: '\u{1F469}\u{1F3FE}\u{200D}\u{2696}\u{FE0F}', + shortName: 'woman_judge_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'judge', + 'medium-dark skin tone', + 'scales', + 'woman', + 'uc8', + 'diversity', + 'job', + 'nerd', + 'court', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'woman judge: dark skin tone', + char: '\u{1F469}\u{1F3FF}\u{200D}\u{2696}\u{FE0F}', + shortName: 'woman_judge_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'dark skin tone', + 'judge', + 'scales', + 'woman', + 'uc8', + 'diversity', + 'job', + 'nerd', + 'court', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'man judge', + char: '\u{1F468}\u{200D}\u{2696}\u{FE0F}', + shortName: 'man_judge', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'justice', + 'man', + 'scales', + 'uc6', + 'diversity', + 'job', + 'nerd', + 'court', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious' + ]), + Emoji( + name: 'man judge: light skin tone', + char: '\u{1F468}\u{1F3FB}\u{200D}\u{2696}\u{FE0F}', + shortName: 'man_judge_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'justice', + 'light skin tone', + 'man', + 'scales', + 'uc8', + 'diversity', + 'job', + 'nerd', + 'court', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'man judge: medium-light skin tone', + char: '\u{1F468}\u{1F3FC}\u{200D}\u{2696}\u{FE0F}', + shortName: 'man_judge_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'justice', + 'man', + 'medium-light skin tone', + 'scales', + 'uc8', + 'diversity', + 'job', + 'nerd', + 'court', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'man judge: medium skin tone', + char: '\u{1F468}\u{1F3FD}\u{200D}\u{2696}\u{FE0F}', + shortName: 'man_judge_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'justice', + 'man', + 'medium skin tone', + 'scales', + 'uc8', + 'diversity', + 'job', + 'nerd', + 'court', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'man judge: medium-dark skin tone', + char: '\u{1F468}\u{1F3FE}\u{200D}\u{2696}\u{FE0F}', + shortName: 'man_judge_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'justice', + 'man', + 'medium-dark skin tone', + 'scales', + 'uc8', + 'diversity', + 'job', + 'nerd', + 'court', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'man judge: dark skin tone', + char: '\u{1F468}\u{1F3FF}\u{200D}\u{2696}\u{FE0F}', + shortName: 'man_judge_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'dark skin tone', + 'justice', + 'man', + 'scales', + 'uc8', + 'diversity', + 'job', + 'nerd', + 'court', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'profession', + 'boss', + 'career', + 'smart', + 'geek', + 'serious' + ], + modifiable: true), + Emoji( + name: 'person with veil', + char: '\u{1F470}', + shortName: 'person_with_veil', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'person', + 'veil', + 'wedding', + 'uc6', + 'diversity', + 'wedding', + 'women', + 'beautiful', + 'las vegas', + 'wife', + 'dress', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'woman', + 'female', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'vegas' + ]), + Emoji( + name: 'person with veil: light skin tone', + char: '\u{1F470}\u{1F3FB}', + shortName: 'person_with_veil_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'person', + 'light skin tone', + 'veil', + 'wedding', + 'uc8', + 'diversity', + 'wedding', + 'women', + 'beautiful', + 'las vegas', + 'wife', + 'dress', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'woman', + 'female', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'vegas' + ], + modifiable: true), + Emoji( + name: 'person with veil: medium-light skin tone', + char: '\u{1F470}\u{1F3FC}', + shortName: 'person_with_veil_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'person', + 'medium-light skin tone', + 'veil', + 'wedding', + 'uc8', + 'diversity', + 'wedding', + 'women', + 'beautiful', + 'las vegas', + 'wife', + 'dress', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'woman', + 'female', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'vegas' + ], + modifiable: true), + Emoji( + name: 'person with veil: medium skin tone', + char: '\u{1F470}\u{1F3FD}', + shortName: 'person_with_veil_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'person', + 'medium skin tone', + 'veil', + 'wedding', + 'uc8', + 'diversity', + 'wedding', + 'women', + 'beautiful', + 'las vegas', + 'wife', + 'dress', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'woman', + 'female', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'vegas' + ], + modifiable: true), + Emoji( + name: 'person with veil: medium-dark skin tone', + char: '\u{1F470}\u{1F3FE}', + shortName: 'person_with_veil_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'person', + 'medium-dark skin tone', + 'veil', + 'wedding', + 'uc8', + 'diversity', + 'wedding', + 'women', + 'beautiful', + 'las vegas', + 'wife', + 'dress', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'woman', + 'female', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'vegas' + ], + modifiable: true), + Emoji( + name: 'person with veil: dark skin tone', + char: '\u{1F470}\u{1F3FF}', + shortName: 'person_with_veil_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'person', + 'dark skin tone', + 'veil', + 'wedding', + 'uc8', + 'diversity', + 'wedding', + 'women', + 'beautiful', + 'las vegas', + 'wife', + 'dress', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'woman', + 'female', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'vegas' + ], + modifiable: true), + Emoji( + name: 'woman with veil', + char: '\u{1F470}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_with_veil', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc13', + 'wedding', + 'beautiful', + 'wife', + 'dress', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely' + ]), + Emoji( + name: 'woman with veil: light skin tone', + char: '\u{1F470}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_with_veil_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc13', + 'wedding', + 'beautiful', + 'wife', + 'dress', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely' + ], + modifiable: true), + Emoji( + name: 'woman with veil: medium-light skin tone', + char: '\u{1F470}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_with_veil_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc13', + 'wedding', + 'beautiful', + 'wife', + 'dress', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely' + ], + modifiable: true), + Emoji( + name: 'woman with veil: medium skin tone', + char: '\u{1F470}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_with_veil_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc13', + 'wedding', + 'beautiful', + 'wife', + 'dress', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely' + ], + modifiable: true), + Emoji( + name: 'woman with veil: medium-dark skin tone', + char: '\u{1F470}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_with_veil_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc13', + 'wedding', + 'beautiful', + 'wife', + 'dress', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely' + ], + modifiable: true), + Emoji( + name: 'woman with veil: dark skin tone', + char: '\u{1F470}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_with_veil_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc13', + 'wedding', + 'beautiful', + 'wife', + 'dress', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely' + ], + modifiable: true), + Emoji( + name: 'man with veil', + char: '\u{1F470}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_with_veil', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc13', + 'wedding', + 'wife', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry' + ]), + Emoji( + name: 'man with veil: light skin tone', + char: '\u{1F470}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_with_veil_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc13', + 'wedding', + 'wife', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry' + ], + modifiable: true), + Emoji( + name: 'man with veil: medium-light skin tone', + char: '\u{1F470}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_with_veil_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc13', + 'wedding', + 'wife', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry' + ], + modifiable: true), + Emoji( + name: 'man with veil: medium skin tone', + char: '\u{1F470}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_with_veil_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc13', + 'wedding', + 'wife', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry' + ], + modifiable: true), + Emoji( + name: 'man with veil: medium-dark skin tone', + char: '\u{1F470}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_with_veil_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc13', + 'wedding', + 'wife', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry' + ], + modifiable: true), + Emoji( + name: 'man with veil: dark skin tone', + char: '\u{1F470}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_with_veil_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc13', + 'wedding', + 'wife', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry' + ], + modifiable: true), + Emoji( + name: 'person in tuxedo', + char: '\u{1F935}', + shortName: 'person_in_tuxedo', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'person', + 'tuxedo', + 'uc9', + 'diversity', + 'wedding', + 'men', + 'boys night', + 'donald trump', + 'fame', + 'vampire', + 'las vegas', + 'rich', + 'jacket', + 'handsome', + 'costume', + 'husband', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'guys night', + 'trump', + 'famous', + 'celebrity', + 'dracula', + 'vegas', + 'grand', + 'expensive', + 'fancy', + 'veste', + 'stud' + ]), + Emoji( + name: 'person in tuxedo: light skin tone', + char: '\u{1F935}\u{1F3FB}', + shortName: 'person_in_tuxedo_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'light skin tone', + 'person', + 'tuxedo', + 'uc9', + 'diversity', + 'wedding', + 'men', + 'boys night', + 'donald trump', + 'fame', + 'vampire', + 'las vegas', + 'rich', + 'jacket', + 'handsome', + 'costume', + 'husband', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'guys night', + 'trump', + 'famous', + 'celebrity', + 'dracula', + 'vegas', + 'grand', + 'expensive', + 'fancy', + 'veste', + 'stud' + ], + modifiable: true), + Emoji( + name: 'person in tuxedo: medium-light skin tone', + char: '\u{1F935}\u{1F3FC}', + shortName: 'person_in_tuxedo_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'person', + 'medium-light skin tone', + 'tuxedo', + 'uc9', + 'diversity', + 'wedding', + 'men', + 'boys night', + 'donald trump', + 'fame', + 'vampire', + 'las vegas', + 'rich', + 'jacket', + 'handsome', + 'costume', + 'husband', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'guys night', + 'trump', + 'famous', + 'celebrity', + 'dracula', + 'vegas', + 'grand', + 'expensive', + 'fancy', + 'veste', + 'stud' + ], + modifiable: true), + Emoji( + name: 'person in tuxedo: medium skin tone', + char: '\u{1F935}\u{1F3FD}', + shortName: 'person_in_tuxedo_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'person', + 'medium skin tone', + 'tuxedo', + 'uc9', + 'diversity', + 'wedding', + 'men', + 'boys night', + 'donald trump', + 'fame', + 'vampire', + 'las vegas', + 'rich', + 'jacket', + 'handsome', + 'costume', + 'husband', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'guys night', + 'trump', + 'famous', + 'celebrity', + 'dracula', + 'vegas', + 'grand', + 'expensive', + 'fancy', + 'veste', + 'stud' + ], + modifiable: true), + Emoji( + name: 'person in tuxedo: medium-dark skin tone', + char: '\u{1F935}\u{1F3FE}', + shortName: 'person_in_tuxedo_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'person', + 'medium-dark skin tone', + 'tuxedo', + 'uc9', + 'diversity', + 'wedding', + 'men', + 'boys night', + 'donald trump', + 'fame', + 'vampire', + 'las vegas', + 'rich', + 'jacket', + 'handsome', + 'costume', + 'husband', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'guys night', + 'trump', + 'famous', + 'celebrity', + 'dracula', + 'vegas', + 'grand', + 'expensive', + 'fancy', + 'veste', + 'stud' + ], + modifiable: true), + Emoji( + name: 'person in tuxedo: dark skin tone', + char: '\u{1F935}\u{1F3FF}', + shortName: 'person_in_tuxedo_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'dark skin tone', + 'person', + 'tuxedo', + 'uc9', + 'diversity', + 'wedding', + 'men', + 'boys night', + 'donald trump', + 'fame', + 'vampire', + 'las vegas', + 'rich', + 'jacket', + 'handsome', + 'costume', + 'husband', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'guys night', + 'trump', + 'famous', + 'celebrity', + 'dracula', + 'vegas', + 'grand', + 'expensive', + 'fancy', + 'veste', + 'stud' + ], + modifiable: true), + Emoji( + name: 'woman in tuxedo', + char: '\u{1F935}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_in_tuxedo', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc13', + 'wedding', + 'fame', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'famous', + 'celebrity' + ]), + Emoji( + name: 'woman in tuxedo: light skin tone', + char: '\u{1F935}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_in_tuxedo_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc13', + 'wedding', + 'fame', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'famous', + 'celebrity' + ], + modifiable: true), + Emoji( + name: 'woman in tuxedo: medium-light skin tone', + char: '\u{1F935}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_in_tuxedo_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc13', + 'wedding', + 'fame', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'famous', + 'celebrity' + ], + modifiable: true), + Emoji( + name: 'woman in tuxedo: medium skin tone', + char: '\u{1F935}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_in_tuxedo_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc13', + 'wedding', + 'fame', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'famous', + 'celebrity' + ], + modifiable: true), + Emoji( + name: 'woman in tuxedo: medium-dark skin tone', + char: '\u{1F935}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_in_tuxedo_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc13', + 'wedding', + 'fame', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'famous', + 'celebrity' + ], + modifiable: true), + Emoji( + name: 'woman in tuxedo: dark skin tone', + char: '\u{1F935}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_in_tuxedo_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc13', + 'wedding', + 'fame', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'famous', + 'celebrity' + ], + modifiable: true), + Emoji( + name: 'man in tuxedo', + char: '\u{1F935}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_in_tuxedo', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc13', + 'wedding', + 'men', + 'boys night', + 'fame', + 'jacket', + 'handsome', + 'costume', + 'husband', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'guys night', + 'famous', + 'celebrity', + 'veste', + 'stud' + ]), + Emoji( + name: 'man in tuxedo: light skin tone', + char: '\u{1F935}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_in_tuxedo_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc13', + 'wedding', + 'men', + 'boys night', + 'fame', + 'jacket', + 'handsome', + 'costume', + 'husband', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'guys night', + 'famous', + 'celebrity', + 'veste', + 'stud' + ], + modifiable: true), + Emoji( + name: 'man in tuxedo: medium-light skin tone', + char: '\u{1F935}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_in_tuxedo_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc13', + 'wedding', + 'men', + 'boys night', + 'fame', + 'jacket', + 'handsome', + 'costume', + 'husband', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'guys night', + 'famous', + 'celebrity', + 'veste', + 'stud' + ], + modifiable: true), + Emoji( + name: 'man in tuxedo: medium skin tone', + char: '\u{1F935}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_in_tuxedo_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc13', + 'wedding', + 'men', + 'boys night', + 'fame', + 'jacket', + 'handsome', + 'costume', + 'husband', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'guys night', + 'famous', + 'celebrity', + 'veste', + 'stud' + ], + modifiable: true), + Emoji( + name: 'man in tuxedo: medium-dark skin tone', + char: '\u{1F935}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_in_tuxedo_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc13', + 'wedding', + 'men', + 'boys night', + 'fame', + 'jacket', + 'handsome', + 'costume', + 'husband', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'guys night', + 'famous', + 'celebrity', + 'veste', + 'stud' + ], + modifiable: true), + Emoji( + name: 'man in tuxedo: dark skin tone', + char: '\u{1F935}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_in_tuxedo_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc13', + 'wedding', + 'men', + 'boys night', + 'fame', + 'jacket', + 'handsome', + 'costume', + 'husband', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'guys night', + 'famous', + 'celebrity', + 'veste', + 'stud' + ], + modifiable: true), + Emoji( + name: 'princess', + char: '\u{1F478}', + shortName: 'princess', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'fairy tale', + 'fantasy', + 'uc6', + 'diversity', + 'wedding', + 'women', + 'halloween', + 'beautiful', + 'girls night', + 'power', + 'queen', + 'disney', + 'bling', + 'fame', + 'crown', + 'rich', + 'dress', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'woman', + 'female', + 'samhain', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'ladies night', + 'girls only', + 'girlfriend', + 'king', + 'prince', + 'princess', + 'cartoon', + 'jewels', + 'gems', + 'jewel', + 'jewelry', + 'treasure', + 'famous', + 'celebrity', + 'tiara', + 'grand', + 'expensive', + 'fancy' + ]), + Emoji( + name: 'princess: light skin tone', + char: '\u{1F478}\u{1F3FB}', + shortName: 'princess_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'fairy tale', + 'fantasy', + 'light skin tone', + 'uc8', + 'diversity', + 'wedding', + 'women', + 'halloween', + 'beautiful', + 'girls night', + 'power', + 'queen', + 'disney', + 'bling', + 'fame', + 'crown', + 'rich', + 'dress', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'woman', + 'female', + 'samhain', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'ladies night', + 'girls only', + 'girlfriend', + 'king', + 'prince', + 'princess', + 'cartoon', + 'jewels', + 'gems', + 'jewel', + 'jewelry', + 'treasure', + 'famous', + 'celebrity', + 'tiara', + 'grand', + 'expensive', + 'fancy' + ], + modifiable: true), + Emoji( + name: 'princess: medium-light skin tone', + char: '\u{1F478}\u{1F3FC}', + shortName: 'princess_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'fairy tale', + 'fantasy', + 'medium-light skin tone', + 'uc8', + 'diversity', + 'wedding', + 'women', + 'halloween', + 'beautiful', + 'girls night', + 'power', + 'queen', + 'disney', + 'bling', + 'fame', + 'crown', + 'rich', + 'dress', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'woman', + 'female', + 'samhain', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'ladies night', + 'girls only', + 'girlfriend', + 'king', + 'prince', + 'princess', + 'cartoon', + 'jewels', + 'gems', + 'jewel', + 'jewelry', + 'treasure', + 'famous', + 'celebrity', + 'tiara', + 'grand', + 'expensive', + 'fancy' + ], + modifiable: true), + Emoji( + name: 'princess: medium skin tone', + char: '\u{1F478}\u{1F3FD}', + shortName: 'princess_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'fairy tale', + 'fantasy', + 'medium skin tone', + 'uc8', + 'diversity', + 'wedding', + 'women', + 'halloween', + 'beautiful', + 'girls night', + 'power', + 'queen', + 'disney', + 'bling', + 'fame', + 'crown', + 'rich', + 'dress', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'woman', + 'female', + 'samhain', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'ladies night', + 'girls only', + 'girlfriend', + 'king', + 'prince', + 'princess', + 'cartoon', + 'jewels', + 'gems', + 'jewel', + 'jewelry', + 'treasure', + 'famous', + 'celebrity', + 'tiara', + 'grand', + 'expensive', + 'fancy' + ], + modifiable: true), + Emoji( + name: 'princess: medium-dark skin tone', + char: '\u{1F478}\u{1F3FE}', + shortName: 'princess_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'fairy tale', + 'fantasy', + 'medium-dark skin tone', + 'uc8', + 'diversity', + 'wedding', + 'women', + 'halloween', + 'beautiful', + 'girls night', + 'power', + 'queen', + 'disney', + 'bling', + 'fame', + 'crown', + 'rich', + 'dress', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'woman', + 'female', + 'samhain', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'ladies night', + 'girls only', + 'girlfriend', + 'king', + 'prince', + 'princess', + 'cartoon', + 'jewels', + 'gems', + 'jewel', + 'jewelry', + 'treasure', + 'famous', + 'celebrity', + 'tiara', + 'grand', + 'expensive', + 'fancy' + ], + modifiable: true), + Emoji( + name: 'princess: dark skin tone', + char: '\u{1F478}\u{1F3FF}', + shortName: 'princess_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'dark skin tone', + 'fairy tale', + 'fantasy', + 'uc8', + 'diversity', + 'wedding', + 'women', + 'halloween', + 'beautiful', + 'girls night', + 'power', + 'queen', + 'disney', + 'bling', + 'fame', + 'crown', + 'rich', + 'dress', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'woman', + 'female', + 'samhain', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'ladies night', + 'girls only', + 'girlfriend', + 'king', + 'prince', + 'princess', + 'cartoon', + 'jewels', + 'gems', + 'jewel', + 'jewelry', + 'treasure', + 'famous', + 'celebrity', + 'tiara', + 'grand', + 'expensive', + 'fancy' + ], + modifiable: true), + Emoji( + name: 'prince', + char: '\u{1F934}', + shortName: 'prince', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'prince', + 'uc9', + 'diversity', + 'men', + 'power', + 'queen', + 'disney', + 'bling', + 'fame', + 'crown', + 'rich', + 'handsome', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'king', + 'prince', + 'princess', + 'cartoon', + 'jewels', + 'gems', + 'jewel', + 'jewelry', + 'treasure', + 'famous', + 'celebrity', + 'tiara', + 'grand', + 'expensive', + 'fancy', + 'stud' + ]), + Emoji( + name: 'prince: light skin tone', + char: '\u{1F934}\u{1F3FB}', + shortName: 'prince_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'light skin tone', + 'prince', + 'uc9', + 'diversity', + 'men', + 'power', + 'queen', + 'disney', + 'bling', + 'fame', + 'crown', + 'rich', + 'handsome', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'king', + 'prince', + 'princess', + 'cartoon', + 'jewels', + 'gems', + 'jewel', + 'jewelry', + 'treasure', + 'famous', + 'celebrity', + 'tiara', + 'grand', + 'expensive', + 'fancy', + 'stud' + ], + modifiable: true), + Emoji( + name: 'prince: medium-light skin tone', + char: '\u{1F934}\u{1F3FC}', + shortName: 'prince_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'medium-light skin tone', + 'prince', + 'uc9', + 'diversity', + 'men', + 'power', + 'queen', + 'disney', + 'bling', + 'fame', + 'crown', + 'rich', + 'handsome', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'king', + 'prince', + 'princess', + 'cartoon', + 'jewels', + 'gems', + 'jewel', + 'jewelry', + 'treasure', + 'famous', + 'celebrity', + 'tiara', + 'grand', + 'expensive', + 'fancy', + 'stud' + ], + modifiable: true), + Emoji( + name: 'prince: medium skin tone', + char: '\u{1F934}\u{1F3FD}', + shortName: 'prince_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'medium skin tone', + 'prince', + 'uc9', + 'diversity', + 'men', + 'power', + 'queen', + 'disney', + 'bling', + 'fame', + 'crown', + 'rich', + 'handsome', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'king', + 'prince', + 'princess', + 'cartoon', + 'jewels', + 'gems', + 'jewel', + 'jewelry', + 'treasure', + 'famous', + 'celebrity', + 'tiara', + 'grand', + 'expensive', + 'fancy', + 'stud' + ], + modifiable: true), + Emoji( + name: 'prince: medium-dark skin tone', + char: '\u{1F934}\u{1F3FE}', + shortName: 'prince_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'medium-dark skin tone', + 'prince', + 'uc9', + 'diversity', + 'men', + 'power', + 'queen', + 'disney', + 'bling', + 'fame', + 'crown', + 'rich', + 'handsome', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'king', + 'prince', + 'princess', + 'cartoon', + 'jewels', + 'gems', + 'jewel', + 'jewelry', + 'treasure', + 'famous', + 'celebrity', + 'tiara', + 'grand', + 'expensive', + 'fancy', + 'stud' + ], + modifiable: true), + Emoji( + name: 'prince: dark skin tone', + char: '\u{1F934}\u{1F3FF}', + shortName: 'prince_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'dark skin tone', + 'prince', + 'uc9', + 'diversity', + 'men', + 'power', + 'queen', + 'disney', + 'bling', + 'fame', + 'crown', + 'rich', + 'handsome', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'king', + 'prince', + 'princess', + 'cartoon', + 'jewels', + 'gems', + 'jewel', + 'jewelry', + 'treasure', + 'famous', + 'celebrity', + 'tiara', + 'grand', + 'expensive', + 'fancy', + 'stud' + ], + modifiable: true), + Emoji( + name: 'superhero', + char: '\u{1F9B8}', + shortName: 'superhero', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'uc11', + 'diversity', + 'peace', + 'halloween', + 'men', + '911', + 'power', + 'daddy', + 'fame', + 'help', + 'super hero', + 'costume', + 'mask', + 'fantasy', + 'proud', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'peace out', + 'peace sign', + 'samhain', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'emergency', + 'injury', + 'dad', + 'papa', + 'pere', + 'father', + 'famous', + 'celebrity', + 'superhero', + 'superman', + 'batman' + ]), + Emoji( + name: 'superhero: light skin tone', + char: '\u{1F9B8}\u{1F3FB}', + shortName: 'superhero_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'uc11', + 'diversity', + 'peace', + 'halloween', + 'men', + '911', + 'power', + 'daddy', + 'fame', + 'help', + 'super hero', + 'costume', + 'mask', + 'fantasy', + 'proud', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'peace out', + 'peace sign', + 'samhain', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'emergency', + 'injury', + 'dad', + 'papa', + 'pere', + 'father', + 'famous', + 'celebrity', + 'superhero', + 'superman', + 'batman' + ], + modifiable: true), + Emoji( + name: 'superhero: medium-light skin tone', + char: '\u{1F9B8}\u{1F3FC}', + shortName: 'superhero_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'uc11', + 'diversity', + 'peace', + 'halloween', + 'men', + '911', + 'power', + 'daddy', + 'fame', + 'help', + 'super hero', + 'costume', + 'mask', + 'fantasy', + 'proud', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'peace out', + 'peace sign', + 'samhain', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'emergency', + 'injury', + 'dad', + 'papa', + 'pere', + 'father', + 'famous', + 'celebrity', + 'superhero', + 'superman', + 'batman' + ], + modifiable: true), + Emoji( + name: 'superhero: medium skin tone', + char: '\u{1F9B8}\u{1F3FD}', + shortName: 'superhero_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'uc11', + 'diversity', + 'peace', + 'halloween', + 'men', + '911', + 'power', + 'daddy', + 'fame', + 'help', + 'super hero', + 'costume', + 'mask', + 'fantasy', + 'proud', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'peace out', + 'peace sign', + 'samhain', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'emergency', + 'injury', + 'dad', + 'papa', + 'pere', + 'father', + 'famous', + 'celebrity', + 'superhero', + 'superman', + 'batman' + ], + modifiable: true), + Emoji( + name: 'superhero: medium-dark skin tone', + char: '\u{1F9B8}\u{1F3FE}', + shortName: 'superhero_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'uc11', + 'diversity', + 'peace', + 'halloween', + 'men', + '911', + 'power', + 'daddy', + 'fame', + 'help', + 'super hero', + 'costume', + 'mask', + 'fantasy', + 'proud', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'peace out', + 'peace sign', + 'samhain', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'emergency', + 'injury', + 'dad', + 'papa', + 'pere', + 'father', + 'famous', + 'celebrity', + 'superhero', + 'superman', + 'batman' + ], + modifiable: true), + Emoji( + name: 'superhero: dark skin tone', + char: '\u{1F9B8}\u{1F3FF}', + shortName: 'superhero_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'uc11', + 'diversity', + 'peace', + 'halloween', + 'men', + '911', + 'power', + 'daddy', + 'fame', + 'help', + 'super hero', + 'costume', + 'mask', + 'fantasy', + 'proud', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'peace out', + 'peace sign', + 'samhain', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'emergency', + 'injury', + 'dad', + 'papa', + 'pere', + 'father', + 'famous', + 'celebrity', + 'superhero', + 'superman', + 'batman' + ], + modifiable: true), + Emoji( + name: 'woman superhero', + char: '\u{1F9B8}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_superhero', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'uc11', + 'diversity', + 'peace', + 'halloween', + '911', + 'power', + 'fame', + 'help', + 'super hero', + 'costume', + 'mask', + 'fantasy', + 'mom', + 'proud', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'peace out', + 'peace sign', + 'samhain', + 'emergency', + 'injury', + 'famous', + 'celebrity', + 'superhero', + 'superman', + 'batman', + 'maman', + 'mommy', + 'mama', + 'mother' + ]), + Emoji( + name: 'woman superhero: light skin tone', + char: '\u{1F9B8}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_superhero_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'uc11', + 'diversity', + 'peace', + 'halloween', + '911', + 'power', + 'fame', + 'help', + 'super hero', + 'costume', + 'mask', + 'fantasy', + 'mom', + 'proud', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'peace out', + 'peace sign', + 'samhain', + 'emergency', + 'injury', + 'famous', + 'celebrity', + 'superhero', + 'superman', + 'batman', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman superhero: medium-light skin tone', + char: '\u{1F9B8}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_superhero_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'uc11', + 'diversity', + 'peace', + 'halloween', + '911', + 'power', + 'fame', + 'help', + 'super hero', + 'costume', + 'mask', + 'fantasy', + 'mom', + 'proud', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'peace out', + 'peace sign', + 'samhain', + 'emergency', + 'injury', + 'famous', + 'celebrity', + 'superhero', + 'superman', + 'batman', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman superhero: medium skin tone', + char: '\u{1F9B8}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_superhero_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'uc11', + 'diversity', + 'peace', + 'halloween', + '911', + 'power', + 'fame', + 'help', + 'super hero', + 'costume', + 'mask', + 'fantasy', + 'mom', + 'proud', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'peace out', + 'peace sign', + 'samhain', + 'emergency', + 'injury', + 'famous', + 'celebrity', + 'superhero', + 'superman', + 'batman', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman superhero: medium-dark skin tone', + char: '\u{1F9B8}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_superhero_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'uc11', + 'diversity', + 'peace', + 'halloween', + '911', + 'power', + 'fame', + 'help', + 'super hero', + 'costume', + 'mask', + 'fantasy', + 'mom', + 'proud', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'peace out', + 'peace sign', + 'samhain', + 'emergency', + 'injury', + 'famous', + 'celebrity', + 'superhero', + 'superman', + 'batman', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman superhero: dark skin tone', + char: '\u{1F9B8}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_superhero_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'uc11', + 'diversity', + 'peace', + 'halloween', + '911', + 'power', + 'fame', + 'help', + 'super hero', + 'costume', + 'mask', + 'fantasy', + 'mom', + 'proud', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'peace out', + 'peace sign', + 'samhain', + 'emergency', + 'injury', + 'famous', + 'celebrity', + 'superhero', + 'superman', + 'batman', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'man superhero', + char: '\u{1F9B8}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_superhero', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'uc11', + 'diversity', + 'peace', + 'halloween', + 'men', + '911', + 'power', + 'daddy', + 'fame', + 'help', + 'super hero', + 'costume', + 'mask', + 'fantasy', + 'proud', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'peace out', + 'peace sign', + 'samhain', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'emergency', + 'injury', + 'dad', + 'papa', + 'pere', + 'father', + 'famous', + 'celebrity', + 'superhero', + 'superman', + 'batman' + ]), + Emoji( + name: 'man superhero: light skin tone', + char: '\u{1F9B8}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_superhero_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'uc11', + 'diversity', + 'peace', + 'halloween', + 'men', + '911', + 'power', + 'daddy', + 'fame', + 'help', + 'super hero', + 'costume', + 'mask', + 'fantasy', + 'proud', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'peace out', + 'peace sign', + 'samhain', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'emergency', + 'injury', + 'dad', + 'papa', + 'pere', + 'father', + 'famous', + 'celebrity', + 'superhero', + 'superman', + 'batman' + ], + modifiable: true), + Emoji( + name: 'man superhero: medium-light skin tone', + char: '\u{1F9B8}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_superhero_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'uc11', + 'diversity', + 'peace', + 'halloween', + 'men', + '911', + 'power', + 'daddy', + 'fame', + 'help', + 'super hero', + 'costume', + 'mask', + 'fantasy', + 'proud', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'peace out', + 'peace sign', + 'samhain', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'emergency', + 'injury', + 'dad', + 'papa', + 'pere', + 'father', + 'famous', + 'celebrity', + 'superhero', + 'superman', + 'batman' + ], + modifiable: true), + Emoji( + name: 'man superhero: medium skin tone', + char: '\u{1F9B8}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_superhero_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'uc11', + 'diversity', + 'peace', + 'halloween', + 'men', + '911', + 'power', + 'daddy', + 'fame', + 'help', + 'super hero', + 'costume', + 'mask', + 'fantasy', + 'proud', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'peace out', + 'peace sign', + 'samhain', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'emergency', + 'injury', + 'dad', + 'papa', + 'pere', + 'father', + 'famous', + 'celebrity', + 'superhero', + 'superman', + 'batman' + ], + modifiable: true), + Emoji( + name: 'man superhero: medium-dark skin tone', + char: '\u{1F9B8}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_superhero_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'uc11', + 'diversity', + 'peace', + 'halloween', + 'men', + '911', + 'power', + 'daddy', + 'fame', + 'help', + 'super hero', + 'costume', + 'mask', + 'fantasy', + 'proud', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'peace out', + 'peace sign', + 'samhain', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'emergency', + 'injury', + 'dad', + 'papa', + 'pere', + 'father', + 'famous', + 'celebrity', + 'superhero', + 'superman', + 'batman' + ], + modifiable: true), + Emoji( + name: 'man superhero: dark skin tone', + char: '\u{1F9B8}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_superhero_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'uc11', + 'diversity', + 'peace', + 'halloween', + 'men', + '911', + 'power', + 'daddy', + 'fame', + 'help', + 'super hero', + 'costume', + 'mask', + 'fantasy', + 'proud', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'peace out', + 'peace sign', + 'samhain', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'emergency', + 'injury', + 'dad', + 'papa', + 'pere', + 'father', + 'famous', + 'celebrity', + 'superhero', + 'superman', + 'batman' + ], + modifiable: true), + Emoji( + name: 'supervillain', + char: '\u{1F9B9}', + shortName: 'supervillain', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'uc11', + 'diversity', + 'halloween', + 'men', + 'power', + 'evil', + 'fame', + 'guilty', + 'super hero', + 'killer', + 'costume', + 'mask', + 'fantasy', + 'proud', + 'greed', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'imp', + 'demon', + 'devil', + 'naughty', + 'devilish', + 'diablo', + 'diable', + 'satan', + 'famous', + 'celebrity', + 'superhero', + 'superman', + 'batman', + 'savage', + 'scary clown', + 'selfish' + ]), + Emoji( + name: 'supervillain: light skin tone', + char: '\u{1F9B9}\u{1F3FB}', + shortName: 'supervillain_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'uc11', + 'diversity', + 'halloween', + 'men', + 'power', + 'evil', + 'fame', + 'guilty', + 'super hero', + 'killer', + 'costume', + 'mask', + 'fantasy', + 'proud', + 'greed', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'imp', + 'demon', + 'devil', + 'naughty', + 'devilish', + 'diablo', + 'diable', + 'satan', + 'famous', + 'celebrity', + 'superhero', + 'superman', + 'batman', + 'savage', + 'scary clown', + 'selfish' + ], + modifiable: true), + Emoji( + name: 'supervillain: medium-light skin tone', + char: '\u{1F9B9}\u{1F3FC}', + shortName: 'supervillain_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'uc11', + 'diversity', + 'halloween', + 'men', + 'power', + 'evil', + 'fame', + 'guilty', + 'super hero', + 'killer', + 'costume', + 'mask', + 'fantasy', + 'proud', + 'greed', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'imp', + 'demon', + 'devil', + 'naughty', + 'devilish', + 'diablo', + 'diable', + 'satan', + 'famous', + 'celebrity', + 'superhero', + 'superman', + 'batman', + 'savage', + 'scary clown', + 'selfish' + ], + modifiable: true), + Emoji( + name: 'supervillain: medium skin tone', + char: '\u{1F9B9}\u{1F3FD}', + shortName: 'supervillain_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'uc11', + 'diversity', + 'halloween', + 'men', + 'power', + 'evil', + 'fame', + 'guilty', + 'super hero', + 'killer', + 'costume', + 'mask', + 'fantasy', + 'proud', + 'greed', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'imp', + 'demon', + 'devil', + 'naughty', + 'devilish', + 'diablo', + 'diable', + 'satan', + 'famous', + 'celebrity', + 'superhero', + 'superman', + 'batman', + 'savage', + 'scary clown', + 'selfish' + ], + modifiable: true), + Emoji( + name: 'supervillain: medium-dark skin tone', + char: '\u{1F9B9}\u{1F3FE}', + shortName: 'supervillain_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'uc11', + 'diversity', + 'halloween', + 'men', + 'power', + 'evil', + 'fame', + 'guilty', + 'super hero', + 'killer', + 'costume', + 'mask', + 'fantasy', + 'proud', + 'greed', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'imp', + 'demon', + 'devil', + 'naughty', + 'devilish', + 'diablo', + 'diable', + 'satan', + 'famous', + 'celebrity', + 'superhero', + 'superman', + 'batman', + 'savage', + 'scary clown', + 'selfish' + ], + modifiable: true), + Emoji( + name: 'supervillain: dark skin tone', + char: '\u{1F9B9}\u{1F3FF}', + shortName: 'supervillain_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'uc11', + 'diversity', + 'halloween', + 'men', + 'power', + 'evil', + 'fame', + 'guilty', + 'super hero', + 'killer', + 'costume', + 'mask', + 'fantasy', + 'proud', + 'greed', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'imp', + 'demon', + 'devil', + 'naughty', + 'devilish', + 'diablo', + 'diable', + 'satan', + 'famous', + 'celebrity', + 'superhero', + 'superman', + 'batman', + 'savage', + 'scary clown', + 'selfish' + ], + modifiable: true), + Emoji( + name: 'woman supervillain: light skin tone', + char: '\u{1F9B9}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_supervillain_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'uc11', + 'diversity', + 'halloween', + 'power', + 'evil', + 'fame', + 'guilty', + 'super hero', + 'killer', + 'costume', + 'mask', + 'fantasy', + 'proud', + 'greed', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'imp', + 'demon', + 'devil', + 'naughty', + 'devilish', + 'diablo', + 'diable', + 'satan', + 'famous', + 'celebrity', + 'superhero', + 'superman', + 'batman', + 'savage', + 'scary clown', + 'selfish' + ], + modifiable: true), + Emoji( + name: 'woman supervillain', + char: '\u{1F9B9}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_supervillain', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'uc11', + 'diversity', + 'halloween', + 'power', + 'evil', + 'fame', + 'guilty', + 'super hero', + 'killer', + 'costume', + 'mask', + 'fantasy', + 'proud', + 'greed', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'imp', + 'demon', + 'devil', + 'naughty', + 'devilish', + 'diablo', + 'diable', + 'satan', + 'famous', + 'celebrity', + 'superhero', + 'superman', + 'batman', + 'savage', + 'scary clown', + 'selfish' + ]), + Emoji( + name: 'woman supervillain: medium-light skin tone', + char: '\u{1F9B9}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_supervillain_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'uc11', + 'diversity', + 'halloween', + 'power', + 'evil', + 'fame', + 'guilty', + 'super hero', + 'killer', + 'costume', + 'mask', + 'fantasy', + 'proud', + 'greed', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'imp', + 'demon', + 'devil', + 'naughty', + 'devilish', + 'diablo', + 'diable', + 'satan', + 'famous', + 'celebrity', + 'superhero', + 'superman', + 'batman', + 'savage', + 'scary clown', + 'selfish' + ], + modifiable: true), + Emoji( + name: 'woman supervillain: medium skin tone', + char: '\u{1F9B9}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_supervillain_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'uc11', + 'diversity', + 'halloween', + 'power', + 'evil', + 'fame', + 'guilty', + 'super hero', + 'killer', + 'costume', + 'mask', + 'fantasy', + 'proud', + 'greed', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'imp', + 'demon', + 'devil', + 'naughty', + 'devilish', + 'diablo', + 'diable', + 'satan', + 'famous', + 'celebrity', + 'superhero', + 'superman', + 'batman', + 'savage', + 'scary clown', + 'selfish' + ], + modifiable: true), + Emoji( + name: 'woman supervillain: medium-dark skin tone', + char: '\u{1F9B9}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_supervillain_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'uc11', + 'diversity', + 'halloween', + 'power', + 'evil', + 'fame', + 'guilty', + 'super hero', + 'killer', + 'costume', + 'mask', + 'fantasy', + 'proud', + 'greed', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'imp', + 'demon', + 'devil', + 'naughty', + 'devilish', + 'diablo', + 'diable', + 'satan', + 'famous', + 'celebrity', + 'superhero', + 'superman', + 'batman', + 'savage', + 'scary clown', + 'selfish' + ], + modifiable: true), + Emoji( + name: 'woman supervillain: dark skin tone', + char: '\u{1F9B9}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_supervillain_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'uc11', + 'diversity', + 'halloween', + 'power', + 'evil', + 'fame', + 'guilty', + 'super hero', + 'killer', + 'costume', + 'mask', + 'fantasy', + 'proud', + 'greed', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'imp', + 'demon', + 'devil', + 'naughty', + 'devilish', + 'diablo', + 'diable', + 'satan', + 'famous', + 'celebrity', + 'superhero', + 'superman', + 'batman', + 'savage', + 'scary clown', + 'selfish' + ], + modifiable: true), + Emoji( + name: 'man supervillain', + char: '\u{1F9B9}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_supervillain', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'uc11', + 'diversity', + 'halloween', + 'men', + 'power', + 'evil', + 'fame', + 'guilty', + 'super hero', + 'killer', + 'costume', + 'mask', + 'fantasy', + 'proud', + 'greed', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'imp', + 'demon', + 'devil', + 'naughty', + 'devilish', + 'diablo', + 'diable', + 'satan', + 'famous', + 'celebrity', + 'superhero', + 'superman', + 'batman', + 'savage', + 'scary clown', + 'selfish' + ]), + Emoji( + name: 'man supervillain: light skin tone', + char: '\u{1F9B9}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_supervillain_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'uc11', + 'diversity', + 'halloween', + 'men', + 'power', + 'evil', + 'fame', + 'guilty', + 'super hero', + 'killer', + 'costume', + 'mask', + 'fantasy', + 'proud', + 'greed', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'imp', + 'demon', + 'devil', + 'naughty', + 'devilish', + 'diablo', + 'diable', + 'satan', + 'famous', + 'celebrity', + 'superhero', + 'superman', + 'batman', + 'savage', + 'scary clown', + 'selfish' + ], + modifiable: true), + Emoji( + name: 'man supervillain: medium-light skin tone', + char: '\u{1F9B9}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_supervillain_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'uc11', + 'diversity', + 'halloween', + 'men', + 'power', + 'evil', + 'fame', + 'guilty', + 'super hero', + 'killer', + 'costume', + 'mask', + 'fantasy', + 'proud', + 'greed', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'imp', + 'demon', + 'devil', + 'naughty', + 'devilish', + 'diablo', + 'diable', + 'satan', + 'famous', + 'celebrity', + 'superhero', + 'superman', + 'batman', + 'savage', + 'scary clown', + 'selfish' + ], + modifiable: true), + Emoji( + name: 'man supervillain: medium skin tone', + char: '\u{1F9B9}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_supervillain_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'uc11', + 'diversity', + 'halloween', + 'men', + 'power', + 'evil', + 'fame', + 'guilty', + 'super hero', + 'killer', + 'costume', + 'mask', + 'fantasy', + 'proud', + 'greed', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'imp', + 'demon', + 'devil', + 'naughty', + 'devilish', + 'diablo', + 'diable', + 'satan', + 'famous', + 'celebrity', + 'superhero', + 'superman', + 'batman', + 'savage', + 'scary clown', + 'selfish' + ], + modifiable: true), + Emoji( + name: 'man supervillain: medium-dark skin tone', + char: '\u{1F9B9}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_supervillain_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'uc11', + 'diversity', + 'halloween', + 'men', + 'power', + 'evil', + 'fame', + 'guilty', + 'super hero', + 'killer', + 'costume', + 'mask', + 'fantasy', + 'proud', + 'greed', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'imp', + 'demon', + 'devil', + 'naughty', + 'devilish', + 'diablo', + 'diable', + 'satan', + 'famous', + 'celebrity', + 'superhero', + 'superman', + 'batman', + 'savage', + 'scary clown', + 'selfish' + ], + modifiable: true), + Emoji( + name: 'man supervillain: dark skin tone', + char: '\u{1F9B9}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_supervillain_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'uc11', + 'diversity', + 'halloween', + 'men', + 'power', + 'evil', + 'fame', + 'guilty', + 'super hero', + 'killer', + 'costume', + 'mask', + 'fantasy', + 'proud', + 'greed', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'imp', + 'demon', + 'devil', + 'naughty', + 'devilish', + 'diablo', + 'diable', + 'satan', + 'famous', + 'celebrity', + 'superhero', + 'superman', + 'batman', + 'savage', + 'scary clown', + 'selfish' + ], + modifiable: true), + Emoji( + name: 'ninja', + char: '\u{1F977}', + shortName: 'ninja', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc13', + 'japan', + 'evil', + 'chinese', + 'super hero', + 'killer', + 'mask', + 'disguise', + 'shinobi', + 'japanese', + 'ninja', + 'imp', + 'demon', + 'devil', + 'naughty', + 'devilish', + 'diablo', + 'diable', + 'satan', + 'chinois', + 'asian', + 'chine', + 'superhero', + 'superman', + 'batman', + 'savage', + 'scary clown', + 'samurai' + ]), + Emoji( + name: 'ninja: light skin tone', + char: '\u{1F977}\u{1F3FB}', + shortName: 'ninja_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc13', + 'japan', + 'evil', + 'chinese', + 'super hero', + 'killer', + 'mask', + 'disguise', + 'shinobi', + 'japanese', + 'ninja', + 'imp', + 'demon', + 'devil', + 'naughty', + 'devilish', + 'diablo', + 'diable', + 'satan', + 'chinois', + 'asian', + 'chine', + 'superhero', + 'superman', + 'batman', + 'savage', + 'scary clown', + 'samurai' + ], + modifiable: true), + Emoji( + name: 'ninja: medium-light skin tone', + char: '\u{1F977}\u{1F3FC}', + shortName: 'ninja_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc13', + 'japan', + 'evil', + 'chinese', + 'super hero', + 'killer', + 'mask', + 'disguise', + 'shinobi', + 'japanese', + 'ninja', + 'imp', + 'demon', + 'devil', + 'naughty', + 'devilish', + 'diablo', + 'diable', + 'satan', + 'chinois', + 'asian', + 'chine', + 'superhero', + 'superman', + 'batman', + 'savage', + 'scary clown', + 'samurai' + ], + modifiable: true), + Emoji( + name: 'ninja: medium skin tone', + char: '\u{1F977}\u{1F3FD}', + shortName: 'ninja_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc13', + 'japan', + 'evil', + 'chinese', + 'super hero', + 'killer', + 'mask', + 'disguise', + 'shinobi', + 'japanese', + 'ninja', + 'imp', + 'demon', + 'devil', + 'naughty', + 'devilish', + 'diablo', + 'diable', + 'satan', + 'chinois', + 'asian', + 'chine', + 'superhero', + 'superman', + 'batman', + 'savage', + 'scary clown', + 'samurai' + ], + modifiable: true), + Emoji( + name: 'ninja: medium-dark skin tone', + char: '\u{1F977}\u{1F3FE}', + shortName: 'ninja_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc13', + 'japan', + 'evil', + 'chinese', + 'super hero', + 'killer', + 'mask', + 'disguise', + 'shinobi', + 'japanese', + 'ninja', + 'imp', + 'demon', + 'devil', + 'naughty', + 'devilish', + 'diablo', + 'diable', + 'satan', + 'chinois', + 'asian', + 'chine', + 'superhero', + 'superman', + 'batman', + 'savage', + 'scary clown', + 'samurai' + ], + modifiable: true), + Emoji( + name: 'ninja: dark skin tone', + char: '\u{1F977}\u{1F3FF}', + shortName: 'ninja_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc13', + 'japan', + 'evil', + 'chinese', + 'super hero', + 'killer', + 'mask', + 'disguise', + 'shinobi', + 'japanese', + 'ninja', + 'imp', + 'demon', + 'devil', + 'naughty', + 'devilish', + 'diablo', + 'diable', + 'satan', + 'chinois', + 'asian', + 'chine', + 'superhero', + 'superman', + 'batman', + 'savage', + 'scary clown', + 'samurai' + ], + modifiable: true), + Emoji( + name: 'mx claus', + char: '\u{1F9D1}\u{200D}\u{1F384}', + shortName: 'mx_claus', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'uc13', + 'holidays', + 'winter', + 'christmas', + 'advent', + 'fantasy', + 'disguise', + 'holiday', + 'navidad', + 'xmas', + 'noel', + 'merry christmas' + ]), + Emoji( + name: 'mx claus: light skin tone', + char: '\u{1F9D1}\u{1F3FB}\u{200D}\u{1F384}', + shortName: 'mx_claus_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'uc13', + 'holidays', + 'winter', + 'christmas', + 'advent', + 'fantasy', + 'disguise', + 'holiday', + 'navidad', + 'xmas', + 'noel', + 'merry christmas' + ], + modifiable: true), + Emoji( + name: 'mx claus: medium-light skin tone', + char: '\u{1F9D1}\u{1F3FC}\u{200D}\u{1F384}', + shortName: 'mx_claus_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'uc13', + 'holidays', + 'winter', + 'christmas', + 'advent', + 'fantasy', + 'disguise', + 'holiday', + 'navidad', + 'xmas', + 'noel', + 'merry christmas' + ], + modifiable: true), + Emoji( + name: 'mx claus: medium skin tone', + char: '\u{1F9D1}\u{1F3FD}\u{200D}\u{1F384}', + shortName: 'mx_claus_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'uc13', + 'holidays', + 'winter', + 'christmas', + 'advent', + 'fantasy', + 'disguise', + 'holiday', + 'navidad', + 'xmas', + 'noel', + 'merry christmas' + ], + modifiable: true), + Emoji( + name: 'mx claus: medium-dark skin tone', + char: '\u{1F9D1}\u{1F3FE}\u{200D}\u{1F384}', + shortName: 'mx_claus_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'uc13', + 'holidays', + 'winter', + 'christmas', + 'advent', + 'fantasy', + 'disguise', + 'holiday', + 'navidad', + 'xmas', + 'noel', + 'merry christmas' + ], + modifiable: true), + Emoji( + name: 'mx claus: dark skin tone', + char: '\u{1F9D1}\u{1F3FF}\u{200D}\u{1F384}', + shortName: 'mx_claus_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'uc13', + 'holidays', + 'winter', + 'christmas', + 'advent', + 'fantasy', + 'disguise', + 'holiday', + 'navidad', + 'xmas', + 'noel', + 'merry christmas' + ], + modifiable: true), + Emoji( + name: 'Mrs. Claus', + char: '\u{1F936}', + shortName: 'mrs_claus', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'Christmas', + 'Mrs.', + 'celebration', + 'claus', + 'mother', + 'uc9', + 'holidays', + 'diversity', + 'winter', + 'christmas', + 'santa', + 'advent', + 'fantasy', + 'disguise', + 'holiday', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'navidad', + 'xmas', + 'noel', + 'merry christmas', + 'santa clause', + 'santa claus' + ]), + Emoji( + name: 'Mrs. Claus: light skin tone', + char: '\u{1F936}\u{1F3FB}', + shortName: 'mrs_claus_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'Christmas', + 'Mrs.', + 'celebration', + 'claus', + 'light skin tone', + 'mother', + 'uc9', + 'holidays', + 'diversity', + 'winter', + 'christmas', + 'santa', + 'advent', + 'fantasy', + 'disguise', + 'holiday', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'navidad', + 'xmas', + 'noel', + 'merry christmas', + 'santa clause', + 'santa claus' + ], + modifiable: true), + Emoji( + name: 'Mrs. Claus: medium-light skin tone', + char: '\u{1F936}\u{1F3FC}', + shortName: 'mrs_claus_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'Christmas', + 'Mrs.', + 'celebration', + 'claus', + 'medium-light skin tone', + 'mother', + 'uc9', + 'holidays', + 'diversity', + 'winter', + 'christmas', + 'santa', + 'advent', + 'fantasy', + 'disguise', + 'holiday', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'navidad', + 'xmas', + 'noel', + 'merry christmas', + 'santa clause', + 'santa claus' + ], + modifiable: true), + Emoji( + name: 'Mrs. Claus: medium skin tone', + char: '\u{1F936}\u{1F3FD}', + shortName: 'mrs_claus_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'Christmas', + 'Mrs.', + 'celebration', + 'claus', + 'medium skin tone', + 'mother', + 'uc9', + 'holidays', + 'diversity', + 'winter', + 'christmas', + 'santa', + 'advent', + 'fantasy', + 'disguise', + 'holiday', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'navidad', + 'xmas', + 'noel', + 'merry christmas', + 'santa clause', + 'santa claus' + ], + modifiable: true), + Emoji( + name: 'Mrs. Claus: medium-dark skin tone', + char: '\u{1F936}\u{1F3FE}', + shortName: 'mrs_claus_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'Christmas', + 'Mrs.', + 'celebration', + 'claus', + 'medium-dark skin tone', + 'mother', + 'uc9', + 'holidays', + 'diversity', + 'winter', + 'christmas', + 'santa', + 'advent', + 'fantasy', + 'disguise', + 'holiday', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'navidad', + 'xmas', + 'noel', + 'merry christmas', + 'santa clause', + 'santa claus' + ], + modifiable: true), + Emoji( + name: 'Mrs. Claus: dark skin tone', + char: '\u{1F936}\u{1F3FF}', + shortName: 'mrs_claus_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'Christmas', + 'Mrs.', + 'celebration', + 'claus', + 'dark skin tone', + 'mother', + 'uc9', + 'holidays', + 'diversity', + 'winter', + 'christmas', + 'santa', + 'advent', + 'fantasy', + 'disguise', + 'holiday', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'navidad', + 'xmas', + 'noel', + 'merry christmas', + 'santa clause', + 'santa claus' + ], + modifiable: true), + Emoji( + name: 'Santa Claus', + char: '\u{1F385}', + shortName: 'santa', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'Christmas', + 'celebration', + 'claus', + 'father', + 'santa', + 'uc6', + 'holidays', + 'diversity', + 'winter', + 'christmas', + 'santa', + 'mustache', + 'advent', + 'beard', + 'fantasy', + 'disguise', + 'holiday', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'navidad', + 'xmas', + 'noel', + 'merry christmas', + 'santa clause', + 'santa claus' + ]), + Emoji( + name: 'Santa Claus: light skin tone', + char: '\u{1F385}\u{1F3FB}', + shortName: 'santa_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'Christmas', + 'celebration', + 'claus', + 'father', + 'light skin tone', + 'santa', + 'uc8', + 'holidays', + 'diversity', + 'winter', + 'christmas', + 'santa', + 'mustache', + 'advent', + 'beard', + 'fantasy', + 'disguise', + 'holiday', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'navidad', + 'xmas', + 'noel', + 'merry christmas', + 'santa clause', + 'santa claus' + ], + modifiable: true), + Emoji( + name: 'Santa Claus: medium-light skin tone', + char: '\u{1F385}\u{1F3FC}', + shortName: 'santa_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'Christmas', + 'celebration', + 'claus', + 'father', + 'medium-light skin tone', + 'santa', + 'uc8', + 'holidays', + 'diversity', + 'winter', + 'christmas', + 'santa', + 'mustache', + 'advent', + 'beard', + 'fantasy', + 'disguise', + 'holiday', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'navidad', + 'xmas', + 'noel', + 'merry christmas', + 'santa clause', + 'santa claus' + ], + modifiable: true), + Emoji( + name: 'Santa Claus: medium skin tone', + char: '\u{1F385}\u{1F3FD}', + shortName: 'santa_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'Christmas', + 'celebration', + 'claus', + 'father', + 'medium skin tone', + 'santa', + 'uc8', + 'holidays', + 'diversity', + 'winter', + 'christmas', + 'santa', + 'mustache', + 'advent', + 'beard', + 'fantasy', + 'disguise', + 'holiday', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'navidad', + 'xmas', + 'noel', + 'merry christmas', + 'santa clause', + 'santa claus' + ], + modifiable: true), + Emoji( + name: 'Santa Claus: medium-dark skin tone', + char: '\u{1F385}\u{1F3FE}', + shortName: 'santa_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'Christmas', + 'celebration', + 'claus', + 'father', + 'medium-dark skin tone', + 'santa', + 'uc8', + 'holidays', + 'diversity', + 'winter', + 'christmas', + 'santa', + 'mustache', + 'advent', + 'beard', + 'fantasy', + 'disguise', + 'holiday', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'navidad', + 'xmas', + 'noel', + 'merry christmas', + 'santa clause', + 'santa claus' + ], + modifiable: true), + Emoji( + name: 'Santa Claus: dark skin tone', + char: '\u{1F385}\u{1F3FF}', + shortName: 'santa_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'Christmas', + 'celebration', + 'claus', + 'dark skin tone', + 'father', + 'santa', + 'uc8', + 'holidays', + 'diversity', + 'winter', + 'christmas', + 'santa', + 'mustache', + 'advent', + 'beard', + 'fantasy', + 'disguise', + 'holiday', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'navidad', + 'xmas', + 'noel', + 'merry christmas', + 'santa clause', + 'santa claus' + ], + modifiable: true), + Emoji( + name: 'mage', + char: '\u{1F9D9}', + shortName: 'mage', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'sorcerer', + 'sorceress', + 'witch', + 'wizard', + 'uc10', + 'diversity', + 'halloween', + 'magic', + 'wizard', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'spell', + 'genie', + 'magical', + 'Sorcerer', + 'Sorceress', + 'witch' + ]), + Emoji( + name: 'mage: light skin tone', + char: '\u{1F9D9}\u{1F3FB}', + shortName: 'mage_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'light skin tone', + 'sorcerer', + 'sorceress', + 'witch', + 'wizard', + 'uc10', + 'diversity', + 'halloween', + 'magic', + 'wizard', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'spell', + 'genie', + 'magical', + 'Sorcerer', + 'Sorceress', + 'witch' + ], + modifiable: true), + Emoji( + name: 'mage: medium-light skin tone', + char: '\u{1F9D9}\u{1F3FC}', + shortName: 'mage_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'medium-light skin tone', + 'sorcerer', + 'sorceress', + 'witch', + 'wizard', + 'uc10', + 'diversity', + 'halloween', + 'magic', + 'wizard', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'spell', + 'genie', + 'magical', + 'Sorcerer', + 'Sorceress', + 'witch' + ], + modifiable: true), + Emoji( + name: 'mage: medium skin tone', + char: '\u{1F9D9}\u{1F3FD}', + shortName: 'mage_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'medium skin tone', + 'sorcerer', + 'sorceress', + 'witch', + 'wizard', + 'uc10', + 'diversity', + 'halloween', + 'magic', + 'wizard', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'spell', + 'genie', + 'magical', + 'Sorcerer', + 'Sorceress', + 'witch' + ], + modifiable: true), + Emoji( + name: 'mage: medium-dark skin tone', + char: '\u{1F9D9}\u{1F3FE}', + shortName: 'mage_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'medium-dark skin tone', + 'sorcerer', + 'sorceress', + 'witch', + 'wizard', + 'uc10', + 'diversity', + 'halloween', + 'magic', + 'wizard', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'spell', + 'genie', + 'magical', + 'Sorcerer', + 'Sorceress', + 'witch' + ], + modifiable: true), + Emoji( + name: 'mage: dark skin tone', + char: '\u{1F9D9}\u{1F3FF}', + shortName: 'mage_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'dark skin tone', + 'sorcerer', + 'sorceress', + 'witch', + 'wizard', + 'uc10', + 'diversity', + 'halloween', + 'magic', + 'wizard', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'spell', + 'genie', + 'magical', + 'Sorcerer', + 'Sorceress', + 'witch' + ], + modifiable: true), + Emoji( + name: 'woman mage', + char: '\u{1F9D9}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_mage', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'sorceress', + 'witch', + 'uc10', + 'diversity', + 'halloween', + 'magic', + 'wizard', + 'fantasy', + 'disguise', + 'snow white', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'spell', + 'genie', + 'magical', + 'Sorcerer', + 'Sorceress', + 'witch' + ]), + Emoji( + name: 'woman mage: light skin tone', + char: '\u{1F9D9}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_mage_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'light skin tone', + 'sorceress', + 'witch', + 'uc10', + 'diversity', + 'halloween', + 'magic', + 'wizard', + 'fantasy', + 'disguise', + 'snow white', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'spell', + 'genie', + 'magical', + 'Sorcerer', + 'Sorceress', + 'witch' + ], + modifiable: true), + Emoji( + name: 'woman mage: medium-light skin tone', + char: '\u{1F9D9}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_mage_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'medium-light skin tone', + 'sorceress', + 'witch', + 'uc10', + 'diversity', + 'halloween', + 'magic', + 'wizard', + 'fantasy', + 'disguise', + 'snow white', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'spell', + 'genie', + 'magical', + 'Sorcerer', + 'Sorceress', + 'witch' + ], + modifiable: true), + Emoji( + name: 'woman mage: medium skin tone', + char: '\u{1F9D9}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_mage_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'medium skin tone', + 'sorceress', + 'witch', + 'uc10', + 'diversity', + 'halloween', + 'magic', + 'wizard', + 'fantasy', + 'disguise', + 'snow white', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'spell', + 'genie', + 'magical', + 'Sorcerer', + 'Sorceress', + 'witch' + ], + modifiable: true), + Emoji( + name: 'woman mage: medium-dark skin tone', + char: '\u{1F9D9}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_mage_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'medium-dark skin tone', + 'sorceress', + 'witch', + 'uc10', + 'diversity', + 'halloween', + 'magic', + 'wizard', + 'fantasy', + 'disguise', + 'snow white', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'spell', + 'genie', + 'magical', + 'Sorcerer', + 'Sorceress', + 'witch' + ], + modifiable: true), + Emoji( + name: 'woman mage: dark skin tone', + char: '\u{1F9D9}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_mage_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'dark skin tone', + 'sorceress', + 'witch', + 'uc10', + 'diversity', + 'halloween', + 'magic', + 'wizard', + 'fantasy', + 'disguise', + 'snow white', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'spell', + 'genie', + 'magical', + 'Sorcerer', + 'Sorceress', + 'witch' + ], + modifiable: true), + Emoji( + name: 'man mage', + char: '\u{1F9D9}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_mage', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'sorcerer', + 'wizard', + 'uc10', + 'diversity', + 'halloween', + 'magic', + 'beard', + 'wizard', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'spell', + 'genie', + 'magical', + 'Sorcerer', + 'Sorceress', + 'witch' + ]), + Emoji( + name: 'man mage: light skin tone', + char: '\u{1F9D9}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_mage_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'light skin tone', + 'sorcerer', + 'wizard', + 'uc10', + 'diversity', + 'halloween', + 'magic', + 'beard', + 'wizard', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'spell', + 'genie', + 'magical', + 'Sorcerer', + 'Sorceress', + 'witch' + ], + modifiable: true), + Emoji( + name: 'man mage: medium-light skin tone', + char: '\u{1F9D9}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_mage_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'medium-light skin tone', + 'sorcerer', + 'wizard', + 'uc10', + 'diversity', + 'halloween', + 'magic', + 'beard', + 'wizard', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'spell', + 'genie', + 'magical', + 'Sorcerer', + 'Sorceress', + 'witch' + ], + modifiable: true), + Emoji( + name: 'man mage: medium skin tone', + char: '\u{1F9D9}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_mage_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'medium skin tone', + 'sorcerer', + 'wizard', + 'uc10', + 'diversity', + 'halloween', + 'magic', + 'beard', + 'wizard', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'spell', + 'genie', + 'magical', + 'Sorcerer', + 'Sorceress', + 'witch' + ], + modifiable: true), + Emoji( + name: 'man mage: medium-dark skin tone', + char: '\u{1F9D9}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_mage_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'medium-dark skin tone', + 'sorcerer', + 'wizard', + 'uc10', + 'diversity', + 'halloween', + 'magic', + 'beard', + 'wizard', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'spell', + 'genie', + 'magical', + 'Sorcerer', + 'Sorceress', + 'witch' + ], + modifiable: true), + Emoji( + name: 'man mage: dark skin tone', + char: '\u{1F9D9}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_mage_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'dark skin tone', + 'sorcerer', + 'wizard', + 'uc10', + 'diversity', + 'halloween', + 'magic', + 'beard', + 'wizard', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'spell', + 'genie', + 'magical', + 'Sorcerer', + 'Sorceress', + 'witch' + ], + modifiable: true), + Emoji( + name: 'elf', + char: '\u{1F9DD}', + shortName: 'elf', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'uc10', + 'diversity', + 'halloween', + 'christmas', + 'magic', + 'legolas', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'navidad', + 'xmas', + 'noel', + 'merry christmas', + 'spell', + 'genie', + 'magical' + ]), + Emoji( + name: 'elf: light skin tone', + char: '\u{1F9DD}\u{1F3FB}', + shortName: 'elf_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'light skin tone', + 'magical', + 'uc10', + 'diversity', + 'halloween', + 'christmas', + 'magic', + 'legolas', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'navidad', + 'xmas', + 'noel', + 'merry christmas', + 'spell', + 'genie', + 'magical' + ], + modifiable: true), + Emoji( + name: 'elf: medium-light skin tone', + char: '\u{1F9DD}\u{1F3FC}', + shortName: 'elf_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'magical', + 'medium-light skin tone', + 'uc10', + 'diversity', + 'halloween', + 'christmas', + 'magic', + 'legolas', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'navidad', + 'xmas', + 'noel', + 'merry christmas', + 'spell', + 'genie', + 'magical' + ], + modifiable: true), + Emoji( + name: 'elf: medium skin tone', + char: '\u{1F9DD}\u{1F3FD}', + shortName: 'elf_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'magical', + 'medium skin tone', + 'uc10', + 'diversity', + 'halloween', + 'christmas', + 'magic', + 'legolas', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'navidad', + 'xmas', + 'noel', + 'merry christmas', + 'spell', + 'genie', + 'magical' + ], + modifiable: true), + Emoji( + name: 'elf: medium-dark skin tone', + char: '\u{1F9DD}\u{1F3FE}', + shortName: 'elf_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'magical', + 'medium-dark skin tone', + 'uc10', + 'diversity', + 'halloween', + 'christmas', + 'magic', + 'legolas', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'navidad', + 'xmas', + 'noel', + 'merry christmas', + 'spell', + 'genie', + 'magical' + ], + modifiable: true), + Emoji( + name: 'elf: dark skin tone', + char: '\u{1F9DD}\u{1F3FF}', + shortName: 'elf_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'dark skin tone', + 'magical', + 'uc10', + 'diversity', + 'halloween', + 'christmas', + 'magic', + 'legolas', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'navidad', + 'xmas', + 'noel', + 'merry christmas', + 'spell', + 'genie', + 'magical' + ], + modifiable: true), + Emoji( + name: 'woman elf', + char: '\u{1F9DD}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_elf', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'magical', + 'uc10', + 'diversity', + 'halloween', + 'christmas', + 'magic', + 'legolas', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'navidad', + 'xmas', + 'noel', + 'merry christmas', + 'spell', + 'genie', + 'magical' + ]), + Emoji( + name: 'woman elf: light skin tone', + char: '\u{1F9DD}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_elf_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'light skin tone', + 'magical', + 'uc10', + 'diversity', + 'halloween', + 'christmas', + 'magic', + 'legolas', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'navidad', + 'xmas', + 'noel', + 'merry christmas', + 'spell', + 'genie', + 'magical' + ], + modifiable: true), + Emoji( + name: 'woman elf: medium-light skin tone', + char: '\u{1F9DD}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_elf_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'magical', + 'medium-light skin tone', + 'uc10', + 'diversity', + 'halloween', + 'christmas', + 'magic', + 'legolas', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'navidad', + 'xmas', + 'noel', + 'merry christmas', + 'spell', + 'genie', + 'magical' + ], + modifiable: true), + Emoji( + name: 'woman elf: medium skin tone', + char: '\u{1F9DD}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_elf_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'magical', + 'medium skin tone', + 'uc10', + 'diversity', + 'halloween', + 'christmas', + 'magic', + 'legolas', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'navidad', + 'xmas', + 'noel', + 'merry christmas', + 'spell', + 'genie', + 'magical' + ], + modifiable: true), + Emoji( + name: 'woman elf: medium-dark skin tone', + char: '\u{1F9DD}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_elf_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'magical', + 'medium-dark skin tone', + 'uc10', + 'diversity', + 'halloween', + 'christmas', + 'magic', + 'legolas', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'navidad', + 'xmas', + 'noel', + 'merry christmas', + 'spell', + 'genie', + 'magical' + ], + modifiable: true), + Emoji( + name: 'woman elf: dark skin tone', + char: '\u{1F9DD}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_elf_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'dark skin tone', + 'magical', + 'uc10', + 'diversity', + 'halloween', + 'christmas', + 'magic', + 'legolas', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'navidad', + 'xmas', + 'noel', + 'merry christmas', + 'spell', + 'genie', + 'magical' + ], + modifiable: true), + Emoji( + name: 'man elf', + char: '\u{1F9DD}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_elf', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'magical', + 'uc10', + 'diversity', + 'halloween', + 'christmas', + 'magic', + 'legolas', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'navidad', + 'xmas', + 'noel', + 'merry christmas', + 'spell', + 'genie', + 'magical' + ]), + Emoji( + name: 'man elf: light skin tone', + char: '\u{1F9DD}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_elf_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'light skin tone', + 'magical', + 'uc10', + 'diversity', + 'halloween', + 'christmas', + 'magic', + 'legolas', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'navidad', + 'xmas', + 'noel', + 'merry christmas', + 'spell', + 'genie', + 'magical' + ], + modifiable: true), + Emoji( + name: 'man elf: medium-light skin tone', + char: '\u{1F9DD}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_elf_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'magical', + 'medium-light skin tone', + 'uc10', + 'diversity', + 'halloween', + 'christmas', + 'magic', + 'legolas', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'navidad', + 'xmas', + 'noel', + 'merry christmas', + 'spell', + 'genie', + 'magical' + ], + modifiable: true), + Emoji( + name: 'man elf: medium skin tone', + char: '\u{1F9DD}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_elf_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'magical', + 'medium skin tone', + 'uc10', + 'diversity', + 'halloween', + 'christmas', + 'magic', + 'legolas', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'navidad', + 'xmas', + 'noel', + 'merry christmas', + 'spell', + 'genie', + 'magical' + ], + modifiable: true), + Emoji( + name: 'man elf: medium-dark skin tone', + char: '\u{1F9DD}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_elf_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'magical', + 'medium-dark skin tone', + 'uc10', + 'diversity', + 'halloween', + 'christmas', + 'magic', + 'legolas', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'navidad', + 'xmas', + 'noel', + 'merry christmas', + 'spell', + 'genie', + 'magical' + ], + modifiable: true), + Emoji( + name: 'man elf: dark skin tone', + char: '\u{1F9DD}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_elf_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'dark skin tone', + 'magical', + 'uc10', + 'diversity', + 'halloween', + 'christmas', + 'magic', + 'legolas', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'navidad', + 'xmas', + 'noel', + 'merry christmas', + 'spell', + 'genie', + 'magical' + ], + modifiable: true), + Emoji( + name: 'vampire', + char: '\u{1F9DB}', + shortName: 'vampire', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'Dracula', + 'undead', + 'uc10', + 'diversity', + 'halloween', + 'vampire', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'dracula' + ]), + Emoji( + name: 'vampire: light skin tone', + char: '\u{1F9DB}\u{1F3FB}', + shortName: 'vampire_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'Dracula', + 'light skin tone', + 'undead', + 'uc10', + 'diversity', + 'halloween', + 'vampire', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'dracula' + ], + modifiable: true), + Emoji( + name: 'vampire: medium-light skin tone', + char: '\u{1F9DB}\u{1F3FC}', + shortName: 'vampire_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'Dracula', + 'medium-light skin tone', + 'undead', + 'uc10', + 'diversity', + 'halloween', + 'vampire', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'dracula' + ], + modifiable: true), + Emoji( + name: 'vampire: medium skin tone', + char: '\u{1F9DB}\u{1F3FD}', + shortName: 'vampire_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'Dracula', + 'medium skin tone', + 'undead', + 'uc10', + 'diversity', + 'halloween', + 'vampire', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'dracula' + ], + modifiable: true), + Emoji( + name: 'vampire: medium-dark skin tone', + char: '\u{1F9DB}\u{1F3FE}', + shortName: 'vampire_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'Dracula', + 'medium-dark skin tone', + 'undead', + 'uc10', + 'diversity', + 'halloween', + 'vampire', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'dracula' + ], + modifiable: true), + Emoji( + name: 'vampire: dark skin tone', + char: '\u{1F9DB}\u{1F3FF}', + shortName: 'vampire_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'Dracula', + 'dark skin tone', + 'undead', + 'uc10', + 'diversity', + 'halloween', + 'vampire', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'dracula' + ], + modifiable: true), + Emoji( + name: 'woman vampire', + char: '\u{1F9DB}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_vampire', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'undead', + 'uc10', + 'diversity', + 'halloween', + 'vampire', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'dracula' + ]), + Emoji( + name: 'woman vampire: light skin tone', + char: '\u{1F9DB}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_vampire_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'light skin tone', + 'undead', + 'uc10', + 'diversity', + 'halloween', + 'vampire', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'dracula' + ], + modifiable: true), + Emoji( + name: 'woman vampire: medium-light skin tone', + char: '\u{1F9DB}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_vampire_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'medium-light skin tone', + 'undead', + 'uc10', + 'diversity', + 'halloween', + 'vampire', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'dracula' + ], + modifiable: true), + Emoji( + name: 'woman vampire: medium skin tone', + char: '\u{1F9DB}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_vampire_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'medium skin tone', + 'undead', + 'uc10', + 'diversity', + 'halloween', + 'vampire', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'dracula' + ], + modifiable: true), + Emoji( + name: 'woman vampire: medium-dark skin tone', + char: '\u{1F9DB}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_vampire_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'medium-dark skin tone', + 'undead', + 'uc10', + 'diversity', + 'halloween', + 'vampire', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'dracula' + ], + modifiable: true), + Emoji( + name: 'woman vampire: dark skin tone', + char: '\u{1F9DB}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_vampire_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'dark skin tone', + 'undead', + 'uc10', + 'diversity', + 'halloween', + 'vampire', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'dracula' + ], + modifiable: true), + Emoji( + name: 'man vampire', + char: '\u{1F9DB}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_vampire', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'Dracula', + 'undead', + 'uc10', + 'diversity', + 'halloween', + 'vampire', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'dracula' + ]), + Emoji( + name: 'man vampire: light skin tone', + char: '\u{1F9DB}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_vampire_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'Dracula', + 'light skin tone', + 'undead', + 'uc10', + 'diversity', + 'halloween', + 'vampire', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'dracula' + ], + modifiable: true), + Emoji( + name: 'man vampire: medium-light skin tone', + char: '\u{1F9DB}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_vampire_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'Dracula', + 'medium-light skin tone', + 'undead', + 'uc10', + 'diversity', + 'halloween', + 'vampire', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'dracula' + ], + modifiable: true), + Emoji( + name: 'man vampire: medium skin tone', + char: '\u{1F9DB}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_vampire_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'Dracula', + 'medium skin tone', + 'undead', + 'uc10', + 'diversity', + 'halloween', + 'vampire', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'dracula' + ], + modifiable: true), + Emoji( + name: 'man vampire: medium-dark skin tone', + char: '\u{1F9DB}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_vampire_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'Dracula', + 'medium-dark skin tone', + 'undead', + 'uc10', + 'diversity', + 'halloween', + 'vampire', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'dracula' + ], + modifiable: true), + Emoji( + name: 'man vampire: dark skin tone', + char: '\u{1F9DB}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_vampire_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'Dracula', + 'dark skin tone', + 'undead', + 'uc10', + 'diversity', + 'halloween', + 'vampire', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'dracula' + ], + modifiable: true), + Emoji( + name: 'zombie', + char: '\u{1F9DF}', + shortName: 'zombie', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'uc10', + 'halloween', + 'monster', + 'disguise', + 'samhain', + 'monsters', + 'beast' + ]), + Emoji( + name: 'woman zombie', + char: '\u{1F9DF}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_zombie', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'undead', + 'walking dead', + 'uc10', + 'halloween', + 'monster', + 'fantasy', + 'disguise', + 'samhain', + 'monsters', + 'beast' + ]), + Emoji( + name: 'man zombie', + char: '\u{1F9DF}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_zombie', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'undead', + 'walking dead', + 'uc10', + 'halloween', + 'monster', + 'fantasy', + 'disguise', + 'samhain', + 'monsters', + 'beast' + ]), + Emoji( + name: 'genie', + char: '\u{1F9DE}', + shortName: 'genie', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'uc10', + 'halloween', + 'magic', + 'djinni', + 'fantasy', + 'disguise', + 'samhain', + 'spell', + 'genie', + 'magical', + 'jinni' + ]), + Emoji( + name: 'woman genie', + char: '\u{1F9DE}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_genie', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'djinn', + 'uc10', + 'halloween', + 'magic', + 'djinni', + 'fantasy', + 'disguise', + 'samhain', + 'spell', + 'genie', + 'magical', + 'jinni' + ]), + Emoji( + name: 'man genie', + char: '\u{1F9DE}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_genie', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'djinn', + 'uc10', + 'halloween', + 'magic', + 'disney', + 'djinni', + 'fantasy', + 'disguise', + 'samhain', + 'spell', + 'genie', + 'magical', + 'cartoon', + 'jinni' + ]), + Emoji( + name: 'merperson', + char: '\u{1F9DC}', + shortName: 'merperson', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'mermaid', + 'merman', + 'merwoman', + 'uc10', + 'diversity', + 'halloween', + 'mermaid', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'merboy', + 'mergirl', + 'merman', + 'merperson', + 'selkie', + 'undine', + 'atargatis', + 'siren' + ]), + Emoji( + name: 'merperson: light skin tone', + char: '\u{1F9DC}\u{1F3FB}', + shortName: 'merperson_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'light skin tone', + 'mermaid', + 'merman', + 'merwoman', + 'uc10', + 'diversity', + 'halloween', + 'mermaid', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'merboy', + 'mergirl', + 'merman', + 'merperson', + 'selkie', + 'undine', + 'atargatis', + 'siren' + ], + modifiable: true), + Emoji( + name: 'merperson: medium-light skin tone', + char: '\u{1F9DC}\u{1F3FC}', + shortName: 'merperson_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'medium-light skin tone', + 'mermaid', + 'merman', + 'merwoman', + 'uc10', + 'diversity', + 'halloween', + 'mermaid', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'merboy', + 'mergirl', + 'merman', + 'merperson', + 'selkie', + 'undine', + 'atargatis', + 'siren' + ], + modifiable: true), + Emoji( + name: 'merperson: medium skin tone', + char: '\u{1F9DC}\u{1F3FD}', + shortName: 'merperson_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'medium skin tone', + 'mermaid', + 'merman', + 'merwoman', + 'uc10', + 'diversity', + 'halloween', + 'mermaid', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'merboy', + 'mergirl', + 'merman', + 'merperson', + 'selkie', + 'undine', + 'atargatis', + 'siren' + ], + modifiable: true), + Emoji( + name: 'merperson: medium-dark skin tone', + char: '\u{1F9DC}\u{1F3FE}', + shortName: 'merperson_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'medium-dark skin tone', + 'mermaid', + 'merman', + 'merwoman', + 'uc10', + 'diversity', + 'halloween', + 'mermaid', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'merboy', + 'mergirl', + 'merman', + 'merperson', + 'selkie', + 'undine', + 'atargatis', + 'siren' + ], + modifiable: true), + Emoji( + name: 'merperson: dark skin tone', + char: '\u{1F9DC}\u{1F3FF}', + shortName: 'merperson_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'dark skin tone', + 'mermaid', + 'merman', + 'merwoman', + 'uc10', + 'diversity', + 'halloween', + 'mermaid', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'merboy', + 'mergirl', + 'merman', + 'merperson', + 'selkie', + 'undine', + 'atargatis', + 'siren' + ], + modifiable: true), + Emoji( + name: 'mermaid', + char: '\u{1F9DC}\u{200D}\u{2640}\u{FE0F}', + shortName: 'mermaid', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'merwoman', + 'uc10', + 'diversity', + 'halloween', + 'disney', + 'mermaid', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'cartoon', + 'merboy', + 'mergirl', + 'merman', + 'merperson', + 'selkie', + 'undine', + 'atargatis', + 'siren' + ]), + Emoji( + name: 'mermaid: light skin tone', + char: '\u{1F9DC}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}', + shortName: 'mermaid_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'light skin tone', + 'merwoman', + 'uc10', + 'diversity', + 'halloween', + 'disney', + 'mermaid', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'cartoon', + 'merboy', + 'mergirl', + 'merman', + 'merperson', + 'selkie', + 'undine', + 'atargatis', + 'siren' + ], + modifiable: true), + Emoji( + name: 'mermaid: medium-light skin tone', + char: '\u{1F9DC}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}', + shortName: 'mermaid_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'medium-light skin tone', + 'merwoman', + 'uc10', + 'diversity', + 'halloween', + 'disney', + 'mermaid', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'cartoon', + 'merboy', + 'mergirl', + 'merman', + 'merperson', + 'selkie', + 'undine', + 'atargatis', + 'siren' + ], + modifiable: true), + Emoji( + name: 'mermaid: medium skin tone', + char: '\u{1F9DC}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}', + shortName: 'mermaid_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'medium skin tone', + 'merwoman', + 'uc10', + 'diversity', + 'halloween', + 'disney', + 'mermaid', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'cartoon', + 'merboy', + 'mergirl', + 'merman', + 'merperson', + 'selkie', + 'undine', + 'atargatis', + 'siren' + ], + modifiable: true), + Emoji( + name: 'mermaid: medium-dark skin tone', + char: '\u{1F9DC}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}', + shortName: 'mermaid_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'medium-dark skin tone', + 'merwoman', + 'uc10', + 'diversity', + 'halloween', + 'disney', + 'mermaid', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'cartoon', + 'merboy', + 'mergirl', + 'merman', + 'merperson', + 'selkie', + 'undine', + 'atargatis', + 'siren' + ], + modifiable: true), + Emoji( + name: 'mermaid: dark skin tone', + char: '\u{1F9DC}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}', + shortName: 'mermaid_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'dark skin tone', + 'merwoman', + 'uc10', + 'diversity', + 'halloween', + 'disney', + 'mermaid', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'cartoon', + 'merboy', + 'mergirl', + 'merman', + 'merperson', + 'selkie', + 'undine', + 'atargatis', + 'siren' + ], + modifiable: true), + Emoji( + name: 'merman', + char: '\u{1F9DC}\u{200D}\u{2642}\u{FE0F}', + shortName: 'merman', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'Triton', + 'uc10', + 'diversity', + 'halloween', + 'mermaid', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'merboy', + 'mergirl', + 'merman', + 'merperson', + 'selkie', + 'undine', + 'atargatis', + 'siren' + ]), + Emoji( + name: 'merman: light skin tone', + char: '\u{1F9DC}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}', + shortName: 'merman_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'Triton', + 'light skin tone', + 'uc10', + 'diversity', + 'halloween', + 'mermaid', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'merboy', + 'mergirl', + 'merman', + 'merperson', + 'selkie', + 'undine', + 'atargatis', + 'siren' + ], + modifiable: true), + Emoji( + name: 'merman: medium-light skin tone', + char: '\u{1F9DC}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}', + shortName: 'merman_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'Triton', + 'medium-light skin tone', + 'uc10', + 'diversity', + 'halloween', + 'mermaid', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'merboy', + 'mergirl', + 'merman', + 'merperson', + 'selkie', + 'undine', + 'atargatis', + 'siren' + ], + modifiable: true), + Emoji( + name: 'merman: medium skin tone', + char: '\u{1F9DC}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}', + shortName: 'merman_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'Triton', + 'medium skin tone', + 'uc10', + 'diversity', + 'halloween', + 'mermaid', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'merboy', + 'mergirl', + 'merman', + 'merperson', + 'selkie', + 'undine', + 'atargatis', + 'siren' + ], + modifiable: true), + Emoji( + name: 'merman: medium-dark skin tone', + char: '\u{1F9DC}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}', + shortName: 'merman_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'Triton', + 'medium-dark skin tone', + 'uc10', + 'diversity', + 'halloween', + 'mermaid', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'merboy', + 'mergirl', + 'merman', + 'merperson', + 'selkie', + 'undine', + 'atargatis', + 'siren' + ], + modifiable: true), + Emoji( + name: 'merman: dark skin tone', + char: '\u{1F9DC}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}', + shortName: 'merman_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'Triton', + 'dark skin tone', + 'uc10', + 'diversity', + 'halloween', + 'mermaid', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'merboy', + 'mergirl', + 'merman', + 'merperson', + 'selkie', + 'undine', + 'atargatis', + 'siren' + ], + modifiable: true), + Emoji( + name: 'fairy', + char: '\u{1F9DA}', + shortName: 'fairy', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'Oberon', + 'Puck', + 'Titania', + 'uc10', + 'diversity', + 'halloween', + 'magic', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'spell', + 'genie', + 'magical' + ]), + Emoji( + name: 'fairy: light skin tone', + char: '\u{1F9DA}\u{1F3FB}', + shortName: 'fairy_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'Oberon', + 'Puck', + 'Titania', + 'light skin tone', + 'uc10', + 'diversity', + 'halloween', + 'magic', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'spell', + 'genie', + 'magical' + ], + modifiable: true), + Emoji( + name: 'fairy: medium-light skin tone', + char: '\u{1F9DA}\u{1F3FC}', + shortName: 'fairy_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'Oberon', + 'Puck', + 'Titania', + 'medium-light skin tone', + 'uc10', + 'diversity', + 'halloween', + 'magic', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'spell', + 'genie', + 'magical' + ], + modifiable: true), + Emoji( + name: 'fairy: medium skin tone', + char: '\u{1F9DA}\u{1F3FD}', + shortName: 'fairy_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'Oberon', + 'Puck', + 'Titania', + 'medium skin tone', + 'uc10', + 'diversity', + 'halloween', + 'magic', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'spell', + 'genie', + 'magical' + ], + modifiable: true), + Emoji( + name: 'fairy: medium-dark skin tone', + char: '\u{1F9DA}\u{1F3FE}', + shortName: 'fairy_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'Oberon', + 'Puck', + 'Titania', + 'medium-dark skin tone', + 'uc10', + 'diversity', + 'halloween', + 'magic', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'spell', + 'genie', + 'magical' + ], + modifiable: true), + Emoji( + name: 'fairy: dark skin tone', + char: '\u{1F9DA}\u{1F3FF}', + shortName: 'fairy_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'Oberon', + 'Puck', + 'Titania', + 'dark skin tone', + 'uc10', + 'diversity', + 'halloween', + 'magic', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'spell', + 'genie', + 'magical' + ], + modifiable: true), + Emoji( + name: 'woman fairy', + char: '\u{1F9DA}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_fairy', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'Titania', + 'uc10', + 'diversity', + 'halloween', + 'beautiful', + 'magic', + 'disney', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'spell', + 'genie', + 'magical', + 'cartoon' + ]), + Emoji( + name: 'woman fairy: light skin tone', + char: '\u{1F9DA}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_fairy_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'Titania', + 'light skin tone', + 'uc10', + 'diversity', + 'halloween', + 'beautiful', + 'magic', + 'disney', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'spell', + 'genie', + 'magical', + 'cartoon' + ], + modifiable: true), + Emoji( + name: 'woman fairy: medium-light skin tone', + char: '\u{1F9DA}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_fairy_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'Titania', + 'medium-light skin tone', + 'uc10', + 'diversity', + 'halloween', + 'beautiful', + 'magic', + 'disney', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'spell', + 'genie', + 'magical', + 'cartoon' + ], + modifiable: true), + Emoji( + name: 'woman fairy: medium skin tone', + char: '\u{1F9DA}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_fairy_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'Titania', + 'medium skin tone', + 'uc10', + 'diversity', + 'halloween', + 'beautiful', + 'magic', + 'disney', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'spell', + 'genie', + 'magical', + 'cartoon' + ], + modifiable: true), + Emoji( + name: 'woman fairy: medium-dark skin tone', + char: '\u{1F9DA}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_fairy_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'Titania', + 'medium-dark skin tone', + 'uc10', + 'diversity', + 'halloween', + 'beautiful', + 'magic', + 'disney', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'spell', + 'genie', + 'magical', + 'cartoon' + ], + modifiable: true), + Emoji( + name: 'woman fairy: dark skin tone', + char: '\u{1F9DA}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_fairy_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'Titania', + 'dark skin tone', + 'uc10', + 'diversity', + 'halloween', + 'beautiful', + 'magic', + 'disney', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'spell', + 'genie', + 'magical', + 'cartoon' + ], + modifiable: true), + Emoji( + name: 'man fairy', + char: '\u{1F9DA}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_fairy', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'Oberon', + 'Puck', + 'uc10', + 'diversity', + 'halloween', + 'beautiful', + 'magic', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'spell', + 'genie', + 'magical' + ]), + Emoji( + name: 'man fairy: light skin tone', + char: '\u{1F9DA}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_fairy_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'Oberon', + 'Puck', + 'light skin tone', + 'uc10', + 'diversity', + 'halloween', + 'beautiful', + 'magic', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'spell', + 'genie', + 'magical' + ], + modifiable: true), + Emoji( + name: 'man fairy: medium-light skin tone', + char: '\u{1F9DA}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_fairy_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'Oberon', + 'Puck', + 'medium-light skin tone', + 'uc10', + 'diversity', + 'halloween', + 'beautiful', + 'magic', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'spell', + 'genie', + 'magical' + ], + modifiable: true), + Emoji( + name: 'man fairy: medium skin tone', + char: '\u{1F9DA}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_fairy_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'Oberon', + 'Puck', + 'medium skin tone', + 'uc10', + 'diversity', + 'halloween', + 'beautiful', + 'magic', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'spell', + 'genie', + 'magical' + ], + modifiable: true), + Emoji( + name: 'man fairy: medium-dark skin tone', + char: '\u{1F9DA}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_fairy_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'Oberon', + 'Puck', + 'medium-dark skin tone', + 'uc10', + 'diversity', + 'halloween', + 'beautiful', + 'magic', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'spell', + 'genie', + 'magical' + ], + modifiable: true), + Emoji( + name: 'man fairy: dark skin tone', + char: '\u{1F9DA}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_fairy_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'Oberon', + 'Puck', + 'dark skin tone', + 'uc10', + 'diversity', + 'halloween', + 'beautiful', + 'magic', + 'fantasy', + 'disguise', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'spell', + 'genie', + 'magical' + ], + modifiable: true), + Emoji( + name: 'baby angel', + char: '\u{1F47C}', + shortName: 'angel', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'angel', + 'baby', + 'face', + 'fairy tale', + 'fantasy', + 'uc6', + 'diversity', + 'halloween', + 'baby', + 'christmas', + 'pray', + 'omg', + 'jesus', + 'fantasy', + 'child', + 'soul', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'kid', + 'babies', + 'infant', + 'infants', + 'crying kid', + 'bebe', + 'little', + 'petite', + 'bambino', + 'navidad', + 'xmas', + 'noel', + 'merry christmas', + 'prayer', + 'praying', + 'prayers', + 'grateful', + 'sorry', + 'heaven', + 'bless', + 'faith', + 'holy', + 'spirit', + 'hopeful', + 'blessed', + 'preach', + 'offering', + 'omfg', + 'oh my god', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant' + ]), + Emoji( + name: 'baby angel: light skin tone', + char: '\u{1F47C}\u{1F3FB}', + shortName: 'angel_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'angel', + 'baby', + 'face', + 'fairy tale', + 'fantasy', + 'light skin tone', + 'uc8', + 'diversity', + 'halloween', + 'baby', + 'christmas', + 'pray', + 'omg', + 'jesus', + 'fantasy', + 'child', + 'soul', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'kid', + 'babies', + 'infant', + 'infants', + 'crying kid', + 'bebe', + 'little', + 'petite', + 'bambino', + 'navidad', + 'xmas', + 'noel', + 'merry christmas', + 'prayer', + 'praying', + 'prayers', + 'grateful', + 'sorry', + 'heaven', + 'bless', + 'faith', + 'holy', + 'spirit', + 'hopeful', + 'blessed', + 'preach', + 'offering', + 'omfg', + 'oh my god', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant' + ], + modifiable: true), + Emoji( + name: 'baby angel: medium-light skin tone', + char: '\u{1F47C}\u{1F3FC}', + shortName: 'angel_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'angel', + 'baby', + 'face', + 'fairy tale', + 'fantasy', + 'medium-light skin tone', + 'uc8', + 'diversity', + 'halloween', + 'baby', + 'christmas', + 'pray', + 'omg', + 'jesus', + 'fantasy', + 'child', + 'soul', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'kid', + 'babies', + 'infant', + 'infants', + 'crying kid', + 'bebe', + 'little', + 'petite', + 'bambino', + 'navidad', + 'xmas', + 'noel', + 'merry christmas', + 'prayer', + 'praying', + 'prayers', + 'grateful', + 'sorry', + 'heaven', + 'bless', + 'faith', + 'holy', + 'spirit', + 'hopeful', + 'blessed', + 'preach', + 'offering', + 'omfg', + 'oh my god', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant' + ], + modifiable: true), + Emoji( + name: 'baby angel: medium skin tone', + char: '\u{1F47C}\u{1F3FD}', + shortName: 'angel_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'angel', + 'baby', + 'face', + 'fairy tale', + 'fantasy', + 'medium skin tone', + 'uc8', + 'diversity', + 'halloween', + 'baby', + 'christmas', + 'pray', + 'omg', + 'jesus', + 'fantasy', + 'child', + 'soul', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'kid', + 'babies', + 'infant', + 'infants', + 'crying kid', + 'bebe', + 'little', + 'petite', + 'bambino', + 'navidad', + 'xmas', + 'noel', + 'merry christmas', + 'prayer', + 'praying', + 'prayers', + 'grateful', + 'sorry', + 'heaven', + 'bless', + 'faith', + 'holy', + 'spirit', + 'hopeful', + 'blessed', + 'preach', + 'offering', + 'omfg', + 'oh my god', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant' + ], + modifiable: true), + Emoji( + name: 'baby angel: medium-dark skin tone', + char: '\u{1F47C}\u{1F3FE}', + shortName: 'angel_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'angel', + 'baby', + 'face', + 'fairy tale', + 'fantasy', + 'medium-dark skin tone', + 'uc8', + 'diversity', + 'halloween', + 'baby', + 'christmas', + 'pray', + 'omg', + 'jesus', + 'fantasy', + 'child', + 'soul', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'kid', + 'babies', + 'infant', + 'infants', + 'crying kid', + 'bebe', + 'little', + 'petite', + 'bambino', + 'navidad', + 'xmas', + 'noel', + 'merry christmas', + 'prayer', + 'praying', + 'prayers', + 'grateful', + 'sorry', + 'heaven', + 'bless', + 'faith', + 'holy', + 'spirit', + 'hopeful', + 'blessed', + 'preach', + 'offering', + 'omfg', + 'oh my god', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant' + ], + modifiable: true), + Emoji( + name: 'baby angel: dark skin tone', + char: '\u{1F47C}\u{1F3FF}', + shortName: 'angel_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personFantasy, + keywords: [ + 'angel', + 'baby', + 'dark skin tone', + 'face', + 'fairy tale', + 'fantasy', + 'uc8', + 'diversity', + 'halloween', + 'baby', + 'christmas', + 'pray', + 'omg', + 'jesus', + 'fantasy', + 'child', + 'soul', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'samhain', + 'kid', + 'babies', + 'infant', + 'infants', + 'crying kid', + 'bebe', + 'little', + 'petite', + 'bambino', + 'navidad', + 'xmas', + 'noel', + 'merry christmas', + 'prayer', + 'praying', + 'prayers', + 'grateful', + 'sorry', + 'heaven', + 'bless', + 'faith', + 'holy', + 'spirit', + 'hopeful', + 'blessed', + 'preach', + 'offering', + 'omfg', + 'oh my god', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant' + ], + modifiable: true), + Emoji( + name: 'pregnant woman', + char: '\u{1F930}', + shortName: 'pregnant_woman', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'pregnant', + 'woman', + 'uc9', + 'diversity', + 'women', + 'baby', + 'parent', + 'wife', + 'child', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'kid', + 'babies', + 'infant', + 'infants', + 'crying kid', + 'bebe', + 'little', + 'petite', + 'bambino', + 'parents', + 'adult', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant', + 'maman', + 'mommy', + 'mama', + 'mother' + ]), + Emoji( + name: 'pregnant woman: light skin tone', + char: '\u{1F930}\u{1F3FB}', + shortName: 'pregnant_woman_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'light skin tone', + 'pregnant', + 'woman', + 'uc9', + 'diversity', + 'women', + 'baby', + 'parent', + 'wife', + 'child', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'kid', + 'babies', + 'infant', + 'infants', + 'crying kid', + 'bebe', + 'little', + 'petite', + 'bambino', + 'parents', + 'adult', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'pregnant woman: medium-light skin tone', + char: '\u{1F930}\u{1F3FC}', + shortName: 'pregnant_woman_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'medium-light skin tone', + 'pregnant', + 'woman', + 'uc9', + 'diversity', + 'women', + 'baby', + 'parent', + 'wife', + 'child', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'kid', + 'babies', + 'infant', + 'infants', + 'crying kid', + 'bebe', + 'little', + 'petite', + 'bambino', + 'parents', + 'adult', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'pregnant woman: medium skin tone', + char: '\u{1F930}\u{1F3FD}', + shortName: 'pregnant_woman_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'medium skin tone', + 'pregnant', + 'woman', + 'uc9', + 'diversity', + 'women', + 'baby', + 'parent', + 'wife', + 'child', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'kid', + 'babies', + 'infant', + 'infants', + 'crying kid', + 'bebe', + 'little', + 'petite', + 'bambino', + 'parents', + 'adult', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'pregnant woman: medium-dark skin tone', + char: '\u{1F930}\u{1F3FE}', + shortName: 'pregnant_woman_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'medium-dark skin tone', + 'pregnant', + 'woman', + 'uc9', + 'diversity', + 'women', + 'baby', + 'parent', + 'wife', + 'child', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'kid', + 'babies', + 'infant', + 'infants', + 'crying kid', + 'bebe', + 'little', + 'petite', + 'bambino', + 'parents', + 'adult', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'pregnant woman: dark skin tone', + char: '\u{1F930}\u{1F3FF}', + shortName: 'pregnant_woman_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'dark skin tone', + 'pregnant', + 'woman', + 'uc9', + 'diversity', + 'women', + 'baby', + 'parent', + 'wife', + 'child', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'kid', + 'babies', + 'infant', + 'infants', + 'crying kid', + 'bebe', + 'little', + 'petite', + 'bambino', + 'parents', + 'adult', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'breast-feeding', + char: '\u{1F931}', + shortName: 'breast_feeding', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'baby', + 'breast', + 'nursing', + 'uc10', + 'food', + 'diversity', + 'boobs', + 'women', + 'baby', + 'parent', + 'wife', + 'child', + 'mom', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'boob', + 'tits', + 'tit', + 'breast', + 'woman', + 'female', + 'kid', + 'babies', + 'infant', + 'infants', + 'crying kid', + 'bebe', + 'little', + 'petite', + 'bambino', + 'parents', + 'adult', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant', + 'maman', + 'mommy', + 'mama', + 'mother' + ]), + Emoji( + name: 'breast-feeding: light skin tone', + char: '\u{1F931}\u{1F3FB}', + shortName: 'breast_feeding_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'baby', + 'breast', + 'light skin tone', + 'nursing', + 'uc10', + 'food', + 'diversity', + 'boobs', + 'women', + 'baby', + 'parent', + 'wife', + 'child', + 'mom', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'boob', + 'tits', + 'tit', + 'breast', + 'woman', + 'female', + 'kid', + 'babies', + 'infant', + 'infants', + 'crying kid', + 'bebe', + 'little', + 'petite', + 'bambino', + 'parents', + 'adult', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'breast-feeding: medium-light skin tone', + char: '\u{1F931}\u{1F3FC}', + shortName: 'breast_feeding_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'baby', + 'breast', + 'medium-light skin tone', + 'nursing', + 'uc10', + 'food', + 'diversity', + 'boobs', + 'women', + 'baby', + 'parent', + 'wife', + 'child', + 'mom', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'boob', + 'tits', + 'tit', + 'breast', + 'woman', + 'female', + 'kid', + 'babies', + 'infant', + 'infants', + 'crying kid', + 'bebe', + 'little', + 'petite', + 'bambino', + 'parents', + 'adult', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'breast-feeding: medium skin tone', + char: '\u{1F931}\u{1F3FD}', + shortName: 'breast_feeding_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'baby', + 'breast', + 'medium skin tone', + 'nursing', + 'uc10', + 'food', + 'diversity', + 'boobs', + 'women', + 'baby', + 'parent', + 'wife', + 'child', + 'mom', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'boob', + 'tits', + 'tit', + 'breast', + 'woman', + 'female', + 'kid', + 'babies', + 'infant', + 'infants', + 'crying kid', + 'bebe', + 'little', + 'petite', + 'bambino', + 'parents', + 'adult', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'breast-feeding: medium-dark skin tone', + char: '\u{1F931}\u{1F3FE}', + shortName: 'breast_feeding_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'baby', + 'breast', + 'medium-dark skin tone', + 'nursing', + 'uc10', + 'food', + 'diversity', + 'boobs', + 'women', + 'baby', + 'parent', + 'wife', + 'child', + 'mom', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'boob', + 'tits', + 'tit', + 'breast', + 'woman', + 'female', + 'kid', + 'babies', + 'infant', + 'infants', + 'crying kid', + 'bebe', + 'little', + 'petite', + 'bambino', + 'parents', + 'adult', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'breast-feeding: dark skin tone', + char: '\u{1F931}\u{1F3FF}', + shortName: 'breast_feeding_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'baby', + 'breast', + 'dark skin tone', + 'nursing', + 'uc10', + 'food', + 'diversity', + 'boobs', + 'women', + 'baby', + 'parent', + 'wife', + 'child', + 'mom', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'boob', + 'tits', + 'tit', + 'breast', + 'woman', + 'female', + 'kid', + 'babies', + 'infant', + 'infants', + 'crying kid', + 'bebe', + 'little', + 'petite', + 'bambino', + 'parents', + 'adult', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'person feeding baby', + char: '\u{1F9D1}\u{200D}\u{1F37C}', + shortName: 'person_feeding_baby', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc13', + 'food', + 'baby', + 'daddy', + 'parent', + 'wife', + 'child', + 'formula', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'kid', + 'babies', + 'infant', + 'infants', + 'crying kid', + 'bebe', + 'little', + 'petite', + 'bambino', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant' + ]), + Emoji( + name: 'person feeding baby: light skin tone', + char: '\u{1F9D1}\u{1F3FB}\u{200D}\u{1F37C}', + shortName: 'person_feeding_baby_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc13', + 'food', + 'baby', + 'daddy', + 'parent', + 'wife', + 'child', + 'formula', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'kid', + 'babies', + 'infant', + 'infants', + 'crying kid', + 'bebe', + 'little', + 'petite', + 'bambino', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant' + ], + modifiable: true), + Emoji( + name: 'person feeding baby: medium-light skin tone', + char: '\u{1F9D1}\u{1F3FC}\u{200D}\u{1F37C}', + shortName: 'person_feeding_baby_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc13', + 'food', + 'baby', + 'daddy', + 'parent', + 'wife', + 'child', + 'formula', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'kid', + 'babies', + 'infant', + 'infants', + 'crying kid', + 'bebe', + 'little', + 'petite', + 'bambino', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant' + ], + modifiable: true), + Emoji( + name: 'person feeding baby: medium skin tone', + char: '\u{1F9D1}\u{1F3FD}\u{200D}\u{1F37C}', + shortName: 'person_feeding_baby_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc13', + 'food', + 'baby', + 'daddy', + 'parent', + 'wife', + 'child', + 'formula', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'kid', + 'babies', + 'infant', + 'infants', + 'crying kid', + 'bebe', + 'little', + 'petite', + 'bambino', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant' + ], + modifiable: true), + Emoji( + name: 'person feeding baby: medium-dark skin tone', + char: '\u{1F9D1}\u{1F3FE}\u{200D}\u{1F37C}', + shortName: 'person_feeding_baby_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc13', + 'food', + 'baby', + 'daddy', + 'parent', + 'wife', + 'child', + 'formula', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'kid', + 'babies', + 'infant', + 'infants', + 'crying kid', + 'bebe', + 'little', + 'petite', + 'bambino', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant' + ], + modifiable: true), + Emoji( + name: 'person feeding baby: dark skin tone', + char: '\u{1F9D1}\u{1F3FF}\u{200D}\u{1F37C}', + shortName: 'person_feeding_baby_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc13', + 'food', + 'baby', + 'daddy', + 'parent', + 'wife', + 'child', + 'formula', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'kid', + 'babies', + 'infant', + 'infants', + 'crying kid', + 'bebe', + 'little', + 'petite', + 'bambino', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant' + ], + modifiable: true), + Emoji( + name: 'woman feeding baby', + char: '\u{1F469}\u{200D}\u{1F37C}', + shortName: 'woman_feeding_baby', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc13', + 'food', + 'baby', + 'parent', + 'wife', + 'child', + 'mom', + 'formula', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'kid', + 'babies', + 'infant', + 'infants', + 'crying kid', + 'bebe', + 'little', + 'petite', + 'bambino', + 'parents', + 'adult', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant', + 'maman', + 'mommy', + 'mama', + 'mother' + ]), + Emoji( + name: 'woman feeding baby: light skin tone', + char: '\u{1F469}\u{1F3FB}\u{200D}\u{1F37C}', + shortName: 'woman_feeding_baby_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc13', + 'food', + 'baby', + 'parent', + 'wife', + 'child', + 'mom', + 'formula', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'kid', + 'babies', + 'infant', + 'infants', + 'crying kid', + 'bebe', + 'little', + 'petite', + 'bambino', + 'parents', + 'adult', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman feeding baby: medium-light skin tone', + char: '\u{1F469}\u{1F3FC}\u{200D}\u{1F37C}', + shortName: 'woman_feeding_baby_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc13', + 'food', + 'baby', + 'parent', + 'wife', + 'child', + 'mom', + 'formula', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'kid', + 'babies', + 'infant', + 'infants', + 'crying kid', + 'bebe', + 'little', + 'petite', + 'bambino', + 'parents', + 'adult', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman feeding baby: medium skin tone', + char: '\u{1F469}\u{1F3FD}\u{200D}\u{1F37C}', + shortName: 'woman_feeding_baby_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc13', + 'food', + 'baby', + 'parent', + 'wife', + 'child', + 'mom', + 'formula', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'kid', + 'babies', + 'infant', + 'infants', + 'crying kid', + 'bebe', + 'little', + 'petite', + 'bambino', + 'parents', + 'adult', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman feeding baby: medium-dark skin tone', + char: '\u{1F469}\u{1F3FE}\u{200D}\u{1F37C}', + shortName: 'woman_feeding_baby_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc13', + 'food', + 'baby', + 'parent', + 'wife', + 'child', + 'mom', + 'formula', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'kid', + 'babies', + 'infant', + 'infants', + 'crying kid', + 'bebe', + 'little', + 'petite', + 'bambino', + 'parents', + 'adult', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman feeding baby: dark skin tone', + char: '\u{1F469}\u{1F3FF}\u{200D}\u{1F37C}', + shortName: 'woman_feeding_baby_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc13', + 'food', + 'baby', + 'parent', + 'wife', + 'child', + 'mom', + 'formula', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'kid', + 'babies', + 'infant', + 'infants', + 'crying kid', + 'bebe', + 'little', + 'petite', + 'bambino', + 'parents', + 'adult', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'man feeding baby', + char: '\u{1F468}\u{200D}\u{1F37C}', + shortName: 'man_feeding_baby', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc13', + 'food', + 'baby', + 'daddy', + 'parent', + 'child', + 'formula', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'kid', + 'babies', + 'infant', + 'infants', + 'crying kid', + 'bebe', + 'little', + 'petite', + 'bambino', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant' + ]), + Emoji( + name: 'man feeding baby: light skin tone', + char: '\u{1F468}\u{1F3FB}\u{200D}\u{1F37C}', + shortName: 'man_feeding_baby_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc13', + 'food', + 'baby', + 'daddy', + 'parent', + 'child', + 'formula', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'kid', + 'babies', + 'infant', + 'infants', + 'crying kid', + 'bebe', + 'little', + 'petite', + 'bambino', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant' + ], + modifiable: true), + Emoji( + name: 'man feeding baby: medium-light skin tone', + char: '\u{1F468}\u{1F3FC}\u{200D}\u{1F37C}', + shortName: 'man_feeding_baby_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc13', + 'food', + 'baby', + 'daddy', + 'parent', + 'child', + 'formula', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'kid', + 'babies', + 'infant', + 'infants', + 'crying kid', + 'bebe', + 'little', + 'petite', + 'bambino', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant' + ], + modifiable: true), + Emoji( + name: 'man feeding baby: medium skin tone', + char: '\u{1F468}\u{1F3FD}\u{200D}\u{1F37C}', + shortName: 'man_feeding_baby_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc13', + 'food', + 'baby', + 'daddy', + 'parent', + 'child', + 'formula', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'kid', + 'babies', + 'infant', + 'infants', + 'crying kid', + 'bebe', + 'little', + 'petite', + 'bambino', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant' + ], + modifiable: true), + Emoji( + name: 'man feeding baby: medium-dark skin tone', + char: '\u{1F468}\u{1F3FE}\u{200D}\u{1F37C}', + shortName: 'man_feeding_baby_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc13', + 'food', + 'baby', + 'daddy', + 'parent', + 'child', + 'formula', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'kid', + 'babies', + 'infant', + 'infants', + 'crying kid', + 'bebe', + 'little', + 'petite', + 'bambino', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant' + ], + modifiable: true), + Emoji( + name: 'man feeding baby: dark skin tone', + char: '\u{1F468}\u{1F3FF}\u{200D}\u{1F37C}', + shortName: 'man_feeding_baby_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personRole, + keywords: [ + 'uc13', + 'food', + 'baby', + 'daddy', + 'parent', + 'child', + 'formula', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'kid', + 'babies', + 'infant', + 'infants', + 'crying kid', + 'bebe', + 'little', + 'petite', + 'bambino', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant' + ], + modifiable: true), + Emoji( + name: 'person bowing', + char: '\u{1F647}', + shortName: 'person_bowing', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'apology', + 'bow', + 'gesture', + 'sorry', + 'uc6', + 'diversity', + 'thank you', + 'pray', + 'jesus', + 'yoga', + 'begging', + 'fame', + 'idea', + 'hope', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'prayer', + 'praying', + 'prayers', + 'grateful', + 'sorry', + 'heaven', + 'bless', + 'faith', + 'holy', + 'spirit', + 'hopeful', + 'blessed', + 'preach', + 'offering', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'famous', + 'celebrity', + 'swear', + 'promise' + ]), + Emoji( + name: 'person bowing: light skin tone', + char: '\u{1F647}\u{1F3FB}', + shortName: 'person_bowing_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'apology', + 'bow', + 'gesture', + 'light skin tone', + 'sorry', + 'uc8', + 'diversity', + 'thank you', + 'pray', + 'jesus', + 'yoga', + 'begging', + 'fame', + 'idea', + 'hope', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'prayer', + 'praying', + 'prayers', + 'grateful', + 'sorry', + 'heaven', + 'bless', + 'faith', + 'holy', + 'spirit', + 'hopeful', + 'blessed', + 'preach', + 'offering', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'famous', + 'celebrity', + 'swear', + 'promise' + ], + modifiable: true), + Emoji( + name: 'person bowing: medium-light skin tone', + char: '\u{1F647}\u{1F3FC}', + shortName: 'person_bowing_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'apology', + 'bow', + 'gesture', + 'medium-light skin tone', + 'sorry', + 'uc8', + 'diversity', + 'thank you', + 'pray', + 'jesus', + 'yoga', + 'begging', + 'fame', + 'idea', + 'hope', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'prayer', + 'praying', + 'prayers', + 'grateful', + 'sorry', + 'heaven', + 'bless', + 'faith', + 'holy', + 'spirit', + 'hopeful', + 'blessed', + 'preach', + 'offering', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'famous', + 'celebrity', + 'swear', + 'promise' + ], + modifiable: true), + Emoji( + name: 'person bowing: medium skin tone', + char: '\u{1F647}\u{1F3FD}', + shortName: 'person_bowing_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'apology', + 'bow', + 'gesture', + 'medium skin tone', + 'sorry', + 'uc8', + 'diversity', + 'thank you', + 'pray', + 'jesus', + 'yoga', + 'begging', + 'fame', + 'idea', + 'hope', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'prayer', + 'praying', + 'prayers', + 'grateful', + 'sorry', + 'heaven', + 'bless', + 'faith', + 'holy', + 'spirit', + 'hopeful', + 'blessed', + 'preach', + 'offering', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'famous', + 'celebrity', + 'swear', + 'promise' + ], + modifiable: true), + Emoji( + name: 'person bowing: medium-dark skin tone', + char: '\u{1F647}\u{1F3FE}', + shortName: 'person_bowing_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'apology', + 'bow', + 'gesture', + 'medium-dark skin tone', + 'sorry', + 'uc8', + 'diversity', + 'thank you', + 'pray', + 'jesus', + 'yoga', + 'begging', + 'fame', + 'idea', + 'hope', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'prayer', + 'praying', + 'prayers', + 'grateful', + 'sorry', + 'heaven', + 'bless', + 'faith', + 'holy', + 'spirit', + 'hopeful', + 'blessed', + 'preach', + 'offering', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'famous', + 'celebrity', + 'swear', + 'promise' + ], + modifiable: true), + Emoji( + name: 'person bowing: dark skin tone', + char: '\u{1F647}\u{1F3FF}', + shortName: 'person_bowing_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'apology', + 'bow', + 'dark skin tone', + 'gesture', + 'sorry', + 'uc8', + 'diversity', + 'thank you', + 'pray', + 'jesus', + 'yoga', + 'begging', + 'fame', + 'idea', + 'hope', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'prayer', + 'praying', + 'prayers', + 'grateful', + 'sorry', + 'heaven', + 'bless', + 'faith', + 'holy', + 'spirit', + 'hopeful', + 'blessed', + 'preach', + 'offering', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'famous', + 'celebrity', + 'swear', + 'promise' + ], + modifiable: true), + Emoji( + name: 'woman bowing', + char: '\u{1F647}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_bowing', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'apology', + 'bowing', + 'favor', + 'gesture', + 'sorry', + 'woman', + 'uc6', + 'diversity', + 'women', + 'thank you', + 'pray', + 'jesus', + 'yoga', + 'fame', + 'idea', + 'hope', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'prayer', + 'praying', + 'prayers', + 'grateful', + 'sorry', + 'heaven', + 'bless', + 'faith', + 'holy', + 'spirit', + 'hopeful', + 'blessed', + 'preach', + 'offering', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'famous', + 'celebrity', + 'swear', + 'promise' + ]), + Emoji( + name: 'woman bowing: light skin tone', + char: '\u{1F647}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_bowing_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'apology', + 'bowing', + 'favor', + 'gesture', + 'light skin tone', + 'sorry', + 'woman', + 'uc8', + 'diversity', + 'women', + 'thank you', + 'pray', + 'jesus', + 'yoga', + 'fame', + 'idea', + 'hope', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'prayer', + 'praying', + 'prayers', + 'grateful', + 'sorry', + 'heaven', + 'bless', + 'faith', + 'holy', + 'spirit', + 'hopeful', + 'blessed', + 'preach', + 'offering', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'famous', + 'celebrity', + 'swear', + 'promise' + ], + modifiable: true), + Emoji( + name: 'woman bowing: medium-light skin tone', + char: '\u{1F647}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_bowing_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'apology', + 'bowing', + 'favor', + 'gesture', + 'medium-light skin tone', + 'sorry', + 'woman', + 'uc8', + 'diversity', + 'women', + 'thank you', + 'pray', + 'jesus', + 'yoga', + 'fame', + 'idea', + 'hope', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'prayer', + 'praying', + 'prayers', + 'grateful', + 'sorry', + 'heaven', + 'bless', + 'faith', + 'holy', + 'spirit', + 'hopeful', + 'blessed', + 'preach', + 'offering', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'famous', + 'celebrity', + 'swear', + 'promise' + ], + modifiable: true), + Emoji( + name: 'woman bowing: medium skin tone', + char: '\u{1F647}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_bowing_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'apology', + 'bowing', + 'favor', + 'gesture', + 'medium skin tone', + 'sorry', + 'woman', + 'uc8', + 'diversity', + 'women', + 'thank you', + 'pray', + 'jesus', + 'yoga', + 'fame', + 'idea', + 'hope', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'prayer', + 'praying', + 'prayers', + 'grateful', + 'sorry', + 'heaven', + 'bless', + 'faith', + 'holy', + 'spirit', + 'hopeful', + 'blessed', + 'preach', + 'offering', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'famous', + 'celebrity', + 'swear', + 'promise' + ], + modifiable: true), + Emoji( + name: 'woman bowing: medium-dark skin tone', + char: '\u{1F647}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_bowing_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'apology', + 'bowing', + 'favor', + 'gesture', + 'medium-dark skin tone', + 'sorry', + 'woman', + 'uc8', + 'diversity', + 'women', + 'thank you', + 'pray', + 'jesus', + 'yoga', + 'fame', + 'idea', + 'hope', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'prayer', + 'praying', + 'prayers', + 'grateful', + 'sorry', + 'heaven', + 'bless', + 'faith', + 'holy', + 'spirit', + 'hopeful', + 'blessed', + 'preach', + 'offering', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'famous', + 'celebrity', + 'swear', + 'promise' + ], + modifiable: true), + Emoji( + name: 'woman bowing: dark skin tone', + char: '\u{1F647}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_bowing_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'apology', + 'bowing', + 'dark skin tone', + 'favor', + 'gesture', + 'sorry', + 'woman', + 'uc8', + 'diversity', + 'women', + 'thank you', + 'pray', + 'jesus', + 'yoga', + 'fame', + 'idea', + 'hope', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'prayer', + 'praying', + 'prayers', + 'grateful', + 'sorry', + 'heaven', + 'bless', + 'faith', + 'holy', + 'spirit', + 'hopeful', + 'blessed', + 'preach', + 'offering', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'famous', + 'celebrity', + 'swear', + 'promise' + ], + modifiable: true), + Emoji( + name: 'man bowing', + char: '\u{1F647}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_bowing', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'apology', + 'bowing', + 'favor', + 'gesture', + 'man', + 'sorry', + 'uc6', + 'diversity', + 'thank you', + 'pray', + 'jesus', + 'yoga', + 'fame', + 'idea', + 'hope', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'prayer', + 'praying', + 'prayers', + 'grateful', + 'sorry', + 'heaven', + 'bless', + 'faith', + 'holy', + 'spirit', + 'hopeful', + 'blessed', + 'preach', + 'offering', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'famous', + 'celebrity', + 'swear', + 'promise' + ]), + Emoji( + name: 'man bowing: light skin tone', + char: '\u{1F647}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_bowing_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'apology', + 'bowing', + 'favor', + 'gesture', + 'light skin tone', + 'man', + 'sorry', + 'uc8', + 'diversity', + 'thank you', + 'pray', + 'jesus', + 'yoga', + 'fame', + 'idea', + 'hope', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'prayer', + 'praying', + 'prayers', + 'grateful', + 'sorry', + 'heaven', + 'bless', + 'faith', + 'holy', + 'spirit', + 'hopeful', + 'blessed', + 'preach', + 'offering', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'famous', + 'celebrity', + 'swear', + 'promise' + ], + modifiable: true), + Emoji( + name: 'man bowing: medium-light skin tone', + char: '\u{1F647}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_bowing_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'apology', + 'bowing', + 'favor', + 'gesture', + 'man', + 'medium-light skin tone', + 'sorry', + 'uc8', + 'diversity', + 'thank you', + 'pray', + 'jesus', + 'yoga', + 'fame', + 'idea', + 'hope', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'prayer', + 'praying', + 'prayers', + 'grateful', + 'sorry', + 'heaven', + 'bless', + 'faith', + 'holy', + 'spirit', + 'hopeful', + 'blessed', + 'preach', + 'offering', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'famous', + 'celebrity', + 'swear', + 'promise' + ], + modifiable: true), + Emoji( + name: 'man bowing: medium skin tone', + char: '\u{1F647}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_bowing_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'apology', + 'bowing', + 'favor', + 'gesture', + 'man', + 'medium skin tone', + 'sorry', + 'uc8', + 'diversity', + 'thank you', + 'pray', + 'jesus', + 'yoga', + 'fame', + 'idea', + 'hope', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'prayer', + 'praying', + 'prayers', + 'grateful', + 'sorry', + 'heaven', + 'bless', + 'faith', + 'holy', + 'spirit', + 'hopeful', + 'blessed', + 'preach', + 'offering', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'famous', + 'celebrity', + 'swear', + 'promise' + ], + modifiable: true), + Emoji( + name: 'man bowing: medium-dark skin tone', + char: '\u{1F647}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_bowing_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'apology', + 'bowing', + 'favor', + 'gesture', + 'man', + 'medium-dark skin tone', + 'sorry', + 'uc8', + 'diversity', + 'thank you', + 'pray', + 'jesus', + 'yoga', + 'fame', + 'idea', + 'hope', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'prayer', + 'praying', + 'prayers', + 'grateful', + 'sorry', + 'heaven', + 'bless', + 'faith', + 'holy', + 'spirit', + 'hopeful', + 'blessed', + 'preach', + 'offering', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'famous', + 'celebrity', + 'swear', + 'promise' + ], + modifiable: true), + Emoji( + name: 'man bowing: dark skin tone', + char: '\u{1F647}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_bowing_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'apology', + 'bowing', + 'dark skin tone', + 'favor', + 'gesture', + 'man', + 'sorry', + 'uc8', + 'diversity', + 'thank you', + 'pray', + 'jesus', + 'yoga', + 'fame', + 'idea', + 'hope', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'prayer', + 'praying', + 'prayers', + 'grateful', + 'sorry', + 'heaven', + 'bless', + 'faith', + 'holy', + 'spirit', + 'hopeful', + 'blessed', + 'preach', + 'offering', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'famous', + 'celebrity', + 'swear', + 'promise' + ], + modifiable: true), + Emoji( + name: 'person tipping hand', + char: '\u{1F481}', + shortName: 'person_tipping_hand', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'hand', + 'help', + 'information', + 'sassy', + 'tipping', + 'uc6', + 'diversity', + 'men', + 'lipstick', + 'help', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'mouth', + 'mouths', + 'makeup', + 'lip', + 'lips', + 'maman', + 'mommy', + 'mama', + 'mother' + ]), + Emoji( + name: 'person tipping hand: light skin tone', + char: '\u{1F481}\u{1F3FB}', + shortName: 'person_tipping_hand_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'hand', + 'help', + 'information', + 'light skin tone', + 'sassy', + 'tipping', + 'uc8', + 'diversity', + 'men', + 'lipstick', + 'help', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'mouth', + 'mouths', + 'makeup', + 'lip', + 'lips', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'person tipping hand: medium-light skin tone', + char: '\u{1F481}\u{1F3FC}', + shortName: 'person_tipping_hand_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'hand', + 'help', + 'information', + 'medium-light skin tone', + 'sassy', + 'tipping', + 'uc8', + 'diversity', + 'men', + 'lipstick', + 'help', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'mouth', + 'mouths', + 'makeup', + 'lip', + 'lips', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'person tipping hand: medium skin tone', + char: '\u{1F481}\u{1F3FD}', + shortName: 'person_tipping_hand_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'hand', + 'help', + 'information', + 'medium skin tone', + 'sassy', + 'tipping', + 'uc8', + 'diversity', + 'men', + 'lipstick', + 'help', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'mouth', + 'mouths', + 'makeup', + 'lip', + 'lips', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'person tipping hand: medium-dark skin tone', + char: '\u{1F481}\u{1F3FE}', + shortName: 'person_tipping_hand_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'hand', + 'help', + 'information', + 'medium-dark skin tone', + 'sassy', + 'tipping', + 'uc8', + 'diversity', + 'men', + 'lipstick', + 'help', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'mouth', + 'mouths', + 'makeup', + 'lip', + 'lips', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'person tipping hand: dark skin tone', + char: '\u{1F481}\u{1F3FF}', + shortName: 'person_tipping_hand_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'dark skin tone', + 'hand', + 'help', + 'information', + 'sassy', + 'tipping', + 'uc8', + 'diversity', + 'men', + 'lipstick', + 'help', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'mouth', + 'mouths', + 'makeup', + 'lip', + 'lips', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman tipping hand', + char: '\u{1F481}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_tipping_hand', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'sassy', + 'tipping hand', + 'woman', + 'uc6', + 'diversity', + 'women', + 'help', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'maman', + 'mommy', + 'mama', + 'mother' + ]), + Emoji( + name: 'woman tipping hand: light skin tone', + char: '\u{1F481}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_tipping_hand_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'light skin tone', + 'sassy', + 'tipping hand', + 'woman', + 'uc8', + 'diversity', + 'women', + 'help', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman tipping hand: medium-light skin tone', + char: '\u{1F481}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_tipping_hand_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'medium-light skin tone', + 'sassy', + 'tipping hand', + 'woman', + 'uc8', + 'diversity', + 'women', + 'help', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman tipping hand: medium skin tone', + char: '\u{1F481}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_tipping_hand_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'medium skin tone', + 'sassy', + 'tipping hand', + 'woman', + 'uc8', + 'diversity', + 'women', + 'help', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman tipping hand: medium-dark skin tone', + char: '\u{1F481}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_tipping_hand_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'medium-dark skin tone', + 'sassy', + 'tipping hand', + 'woman', + 'uc8', + 'diversity', + 'women', + 'help', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman tipping hand: dark skin tone', + char: '\u{1F481}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_tipping_hand_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'dark skin tone', + 'sassy', + 'tipping hand', + 'woman', + 'uc8', + 'diversity', + 'women', + 'help', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'man tipping hand', + char: '\u{1F481}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_tipping_hand', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'man', + 'sassy', + 'tipping hand', + 'uc6', + 'diversity', + 'men', + 'help', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male' + ]), + Emoji( + name: 'man tipping hand: light skin tone', + char: '\u{1F481}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_tipping_hand_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'light skin tone', + 'man', + 'sassy', + 'tipping hand', + 'uc8', + 'diversity', + 'men', + 'help', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male' + ], + modifiable: true), + Emoji( + name: 'man tipping hand: medium-light skin tone', + char: '\u{1F481}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_tipping_hand_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'man', + 'medium-light skin tone', + 'sassy', + 'tipping hand', + 'uc8', + 'diversity', + 'men', + 'help', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male' + ], + modifiable: true), + Emoji( + name: 'man tipping hand: medium skin tone', + char: '\u{1F481}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_tipping_hand_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'man', + 'medium skin tone', + 'sassy', + 'tipping hand', + 'uc8', + 'diversity', + 'men', + 'help', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male' + ], + modifiable: true), + Emoji( + name: 'man tipping hand: medium-dark skin tone', + char: '\u{1F481}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_tipping_hand_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'man', + 'medium-dark skin tone', + 'sassy', + 'tipping hand', + 'uc8', + 'diversity', + 'men', + 'help', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male' + ], + modifiable: true), + Emoji( + name: 'man tipping hand: dark skin tone', + char: '\u{1F481}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_tipping_hand_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'dark skin tone', + 'man', + 'sassy', + 'tipping hand', + 'uc8', + 'diversity', + 'men', + 'help', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male' + ], + modifiable: true), + Emoji( + name: 'person gesturing NO', + char: '\u{1F645}', + shortName: 'person_gesturing_no', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'forbidden', + 'gesture', + 'hand', + 'no', + 'not', + 'prohibited', + 'uc6', + 'diversity', + 'men', + 'angry', + 'girls night', + 'hate', + 'danger', + 'bitch', + 'daddy', + 'crazy', + 'private', + 'mom', + 'never', + 'wrong', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad', + 'ladies night', + 'girls only', + 'girlfriend', + 'i hate', + 'disgust', + 'stump', + 'shout', + 'dislike', + 'rude', + 'annoy', + 'grinch', + 'gross', + 'grumpy', + 'mean', + 'problem', + 'suck', + 'jerk', + 'asshole', + 'no', + 'warn', + 'attention', + 'caution', + 'alert', + 'error', + 'panic', + 'restricted', + "don't", + 'dont', + 'dangerous', + 'puta', + 'pute', + 'dad', + 'papa', + 'pere', + 'father', + 'weird', + 'awkward', + 'insane', + 'wild', + 'прив', + 'privé', + 'privado', + 'reserved', + 'maman', + 'mommy', + 'mama', + 'mother' + ]), + Emoji( + name: 'person gesturing NO: light skin tone', + char: '\u{1F645}\u{1F3FB}', + shortName: 'person_gesturing_no_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'forbidden', + 'gesture', + 'hand', + 'light skin tone', + 'no', + 'not', + 'prohibited', + 'uc8', + 'diversity', + 'men', + 'angry', + 'girls night', + 'hate', + 'danger', + 'bitch', + 'daddy', + 'crazy', + 'private', + 'mom', + 'never', + 'wrong', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad', + 'ladies night', + 'girls only', + 'girlfriend', + 'i hate', + 'disgust', + 'stump', + 'shout', + 'dislike', + 'rude', + 'annoy', + 'grinch', + 'gross', + 'grumpy', + 'mean', + 'problem', + 'suck', + 'jerk', + 'asshole', + 'no', + 'warn', + 'attention', + 'caution', + 'alert', + 'error', + 'panic', + 'restricted', + "don't", + 'dont', + 'dangerous', + 'puta', + 'pute', + 'dad', + 'papa', + 'pere', + 'father', + 'weird', + 'awkward', + 'insane', + 'wild', + 'прив', + 'privé', + 'privado', + 'reserved', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'person gesturing NO: medium-light skin tone', + char: '\u{1F645}\u{1F3FC}', + shortName: 'person_gesturing_no_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'forbidden', + 'gesture', + 'hand', + 'medium-light skin tone', + 'no', + 'not', + 'prohibited', + 'uc8', + 'diversity', + 'men', + 'angry', + 'girls night', + 'hate', + 'danger', + 'bitch', + 'daddy', + 'crazy', + 'private', + 'mom', + 'never', + 'wrong', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad', + 'ladies night', + 'girls only', + 'girlfriend', + 'i hate', + 'disgust', + 'stump', + 'shout', + 'dislike', + 'rude', + 'annoy', + 'grinch', + 'gross', + 'grumpy', + 'mean', + 'problem', + 'suck', + 'jerk', + 'asshole', + 'no', + 'warn', + 'attention', + 'caution', + 'alert', + 'error', + 'panic', + 'restricted', + "don't", + 'dont', + 'dangerous', + 'puta', + 'pute', + 'dad', + 'papa', + 'pere', + 'father', + 'weird', + 'awkward', + 'insane', + 'wild', + 'прив', + 'privé', + 'privado', + 'reserved', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'person gesturing NO: medium skin tone', + char: '\u{1F645}\u{1F3FD}', + shortName: 'person_gesturing_no_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'forbidden', + 'gesture', + 'hand', + 'medium skin tone', + 'no', + 'not', + 'prohibited', + 'uc8', + 'diversity', + 'men', + 'angry', + 'girls night', + 'hate', + 'danger', + 'bitch', + 'daddy', + 'crazy', + 'private', + 'mom', + 'never', + 'wrong', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad', + 'ladies night', + 'girls only', + 'girlfriend', + 'i hate', + 'disgust', + 'stump', + 'shout', + 'dislike', + 'rude', + 'annoy', + 'grinch', + 'gross', + 'grumpy', + 'mean', + 'problem', + 'suck', + 'jerk', + 'asshole', + 'no', + 'warn', + 'attention', + 'caution', + 'alert', + 'error', + 'panic', + 'restricted', + "don't", + 'dont', + 'dangerous', + 'puta', + 'pute', + 'dad', + 'papa', + 'pere', + 'father', + 'weird', + 'awkward', + 'insane', + 'wild', + 'прив', + 'privé', + 'privado', + 'reserved', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'person gesturing NO: medium-dark skin tone', + char: '\u{1F645}\u{1F3FE}', + shortName: 'person_gesturing_no_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'forbidden', + 'gesture', + 'hand', + 'medium-dark skin tone', + 'no', + 'not', + 'prohibited', + 'uc8', + 'diversity', + 'men', + 'angry', + 'girls night', + 'hate', + 'danger', + 'bitch', + 'daddy', + 'crazy', + 'private', + 'mom', + 'never', + 'wrong', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad', + 'ladies night', + 'girls only', + 'girlfriend', + 'i hate', + 'disgust', + 'stump', + 'shout', + 'dislike', + 'rude', + 'annoy', + 'grinch', + 'gross', + 'grumpy', + 'mean', + 'problem', + 'suck', + 'jerk', + 'asshole', + 'no', + 'warn', + 'attention', + 'caution', + 'alert', + 'error', + 'panic', + 'restricted', + "don't", + 'dont', + 'dangerous', + 'puta', + 'pute', + 'dad', + 'papa', + 'pere', + 'father', + 'weird', + 'awkward', + 'insane', + 'wild', + 'прив', + 'privé', + 'privado', + 'reserved', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'person gesturing NO: dark skin tone', + char: '\u{1F645}\u{1F3FF}', + shortName: 'person_gesturing_no_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'dark skin tone', + 'forbidden', + 'gesture', + 'hand', + 'no', + 'not', + 'prohibited', + 'uc8', + 'diversity', + 'men', + 'angry', + 'girls night', + 'hate', + 'danger', + 'bitch', + 'daddy', + 'crazy', + 'private', + 'mom', + 'never', + 'wrong', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad', + 'ladies night', + 'girls only', + 'girlfriend', + 'i hate', + 'disgust', + 'stump', + 'shout', + 'dislike', + 'rude', + 'annoy', + 'grinch', + 'gross', + 'grumpy', + 'mean', + 'problem', + 'suck', + 'jerk', + 'asshole', + 'no', + 'warn', + 'attention', + 'caution', + 'alert', + 'error', + 'panic', + 'restricted', + "don't", + 'dont', + 'dangerous', + 'puta', + 'pute', + 'dad', + 'papa', + 'pere', + 'father', + 'weird', + 'awkward', + 'insane', + 'wild', + 'прив', + 'privé', + 'privado', + 'reserved', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman gesturing NO', + char: '\u{1F645}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_gesturing_no', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'forbidden', + 'gesture', + 'hand', + 'no', + 'prohibited', + 'woman', + 'uc6', + 'diversity', + 'women', + 'girls night', + 'danger', + 'bitch', + 'crazy', + 'private', + 'mom', + 'never', + 'wrong', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'ladies night', + 'girls only', + 'girlfriend', + 'warn', + 'attention', + 'caution', + 'alert', + 'error', + 'panic', + 'restricted', + "don't", + 'dont', + 'dangerous', + 'puta', + 'pute', + 'weird', + 'awkward', + 'insane', + 'wild', + 'прив', + 'privé', + 'privado', + 'reserved', + 'maman', + 'mommy', + 'mama', + 'mother' + ]), + Emoji( + name: 'woman gesturing NO: light skin tone', + char: '\u{1F645}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_gesturing_no_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'forbidden', + 'gesture', + 'hand', + 'light skin tone', + 'no', + 'prohibited', + 'woman', + 'uc8', + 'diversity', + 'women', + 'girls night', + 'danger', + 'bitch', + 'crazy', + 'private', + 'mom', + 'never', + 'wrong', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'ladies night', + 'girls only', + 'girlfriend', + 'warn', + 'attention', + 'caution', + 'alert', + 'error', + 'panic', + 'restricted', + "don't", + 'dont', + 'dangerous', + 'puta', + 'pute', + 'weird', + 'awkward', + 'insane', + 'wild', + 'прив', + 'privé', + 'privado', + 'reserved', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman gesturing NO: medium-light skin tone', + char: '\u{1F645}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_gesturing_no_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'forbidden', + 'gesture', + 'hand', + 'medium-light skin tone', + 'no', + 'prohibited', + 'woman', + 'uc8', + 'diversity', + 'women', + 'girls night', + 'danger', + 'bitch', + 'crazy', + 'private', + 'mom', + 'never', + 'wrong', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'ladies night', + 'girls only', + 'girlfriend', + 'warn', + 'attention', + 'caution', + 'alert', + 'error', + 'panic', + 'restricted', + "don't", + 'dont', + 'dangerous', + 'puta', + 'pute', + 'weird', + 'awkward', + 'insane', + 'wild', + 'прив', + 'privé', + 'privado', + 'reserved', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman gesturing NO: medium skin tone', + char: '\u{1F645}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_gesturing_no_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'forbidden', + 'gesture', + 'hand', + 'medium skin tone', + 'no', + 'prohibited', + 'woman', + 'uc8', + 'diversity', + 'women', + 'girls night', + 'danger', + 'bitch', + 'crazy', + 'private', + 'mom', + 'never', + 'wrong', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'ladies night', + 'girls only', + 'girlfriend', + 'warn', + 'attention', + 'caution', + 'alert', + 'error', + 'panic', + 'restricted', + "don't", + 'dont', + 'dangerous', + 'puta', + 'pute', + 'weird', + 'awkward', + 'insane', + 'wild', + 'прив', + 'privé', + 'privado', + 'reserved', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman gesturing NO: medium-dark skin tone', + char: '\u{1F645}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_gesturing_no_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'forbidden', + 'gesture', + 'hand', + 'medium-dark skin tone', + 'no', + 'prohibited', + 'woman', + 'uc8', + 'diversity', + 'women', + 'girls night', + 'danger', + 'bitch', + 'crazy', + 'private', + 'mom', + 'never', + 'wrong', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'ladies night', + 'girls only', + 'girlfriend', + 'warn', + 'attention', + 'caution', + 'alert', + 'error', + 'panic', + 'restricted', + "don't", + 'dont', + 'dangerous', + 'puta', + 'pute', + 'weird', + 'awkward', + 'insane', + 'wild', + 'прив', + 'privé', + 'privado', + 'reserved', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman gesturing NO: dark skin tone', + char: '\u{1F645}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_gesturing_no_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'dark skin tone', + 'forbidden', + 'gesture', + 'hand', + 'no', + 'prohibited', + 'woman', + 'uc8', + 'diversity', + 'women', + 'girls night', + 'danger', + 'bitch', + 'crazy', + 'private', + 'mom', + 'never', + 'wrong', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'ladies night', + 'girls only', + 'girlfriend', + 'warn', + 'attention', + 'caution', + 'alert', + 'error', + 'panic', + 'restricted', + "don't", + 'dont', + 'dangerous', + 'puta', + 'pute', + 'weird', + 'awkward', + 'insane', + 'wild', + 'прив', + 'privé', + 'privado', + 'reserved', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'man gesturing NO', + char: '\u{1F645}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_gesturing_no', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'forbidden', + 'gesture', + 'hand', + 'man', + 'no', + 'prohibited', + 'uc6', + 'diversity', + 'men', + 'angry', + 'hate', + 'danger', + 'daddy', + 'crazy', + 'private', + 'never', + 'wrong', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad', + 'i hate', + 'disgust', + 'stump', + 'shout', + 'dislike', + 'rude', + 'annoy', + 'grinch', + 'gross', + 'grumpy', + 'mean', + 'problem', + 'suck', + 'jerk', + 'asshole', + 'no', + 'warn', + 'attention', + 'caution', + 'alert', + 'error', + 'panic', + 'restricted', + "don't", + 'dont', + 'dangerous', + 'dad', + 'papa', + 'pere', + 'father', + 'weird', + 'awkward', + 'insane', + 'wild', + 'прив', + 'privé', + 'privado', + 'reserved' + ]), + Emoji( + name: 'man gesturing NO: light skin tone', + char: '\u{1F645}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_gesturing_no_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'forbidden', + 'gesture', + 'hand', + 'light skin tone', + 'man', + 'no', + 'prohibited', + 'uc8', + 'diversity', + 'men', + 'angry', + 'hate', + 'danger', + 'daddy', + 'crazy', + 'private', + 'never', + 'wrong', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad', + 'i hate', + 'disgust', + 'stump', + 'shout', + 'dislike', + 'rude', + 'annoy', + 'grinch', + 'gross', + 'grumpy', + 'mean', + 'problem', + 'suck', + 'jerk', + 'asshole', + 'no', + 'warn', + 'attention', + 'caution', + 'alert', + 'error', + 'panic', + 'restricted', + "don't", + 'dont', + 'dangerous', + 'dad', + 'papa', + 'pere', + 'father', + 'weird', + 'awkward', + 'insane', + 'wild', + 'прив', + 'privé', + 'privado', + 'reserved' + ], + modifiable: true), + Emoji( + name: 'man gesturing NO: medium-light skin tone', + char: '\u{1F645}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_gesturing_no_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'forbidden', + 'gesture', + 'hand', + 'man', + 'medium-light skin tone', + 'no', + 'prohibited', + 'uc8', + 'diversity', + 'men', + 'angry', + 'hate', + 'danger', + 'daddy', + 'crazy', + 'private', + 'never', + 'wrong', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad', + 'i hate', + 'disgust', + 'stump', + 'shout', + 'dislike', + 'rude', + 'annoy', + 'grinch', + 'gross', + 'grumpy', + 'mean', + 'problem', + 'suck', + 'jerk', + 'asshole', + 'no', + 'warn', + 'attention', + 'caution', + 'alert', + 'error', + 'panic', + 'restricted', + "don't", + 'dont', + 'dangerous', + 'dad', + 'papa', + 'pere', + 'father', + 'weird', + 'awkward', + 'insane', + 'wild', + 'прив', + 'privé', + 'privado', + 'reserved' + ], + modifiable: true), + Emoji( + name: 'man gesturing NO: medium skin tone', + char: '\u{1F645}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_gesturing_no_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'forbidden', + 'gesture', + 'hand', + 'man', + 'medium skin tone', + 'no', + 'prohibited', + 'uc8', + 'diversity', + 'men', + 'angry', + 'hate', + 'danger', + 'daddy', + 'crazy', + 'private', + 'never', + 'wrong', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad', + 'i hate', + 'disgust', + 'stump', + 'shout', + 'dislike', + 'rude', + 'annoy', + 'grinch', + 'gross', + 'grumpy', + 'mean', + 'problem', + 'suck', + 'jerk', + 'asshole', + 'no', + 'warn', + 'attention', + 'caution', + 'alert', + 'error', + 'panic', + 'restricted', + "don't", + 'dont', + 'dangerous', + 'dad', + 'papa', + 'pere', + 'father', + 'weird', + 'awkward', + 'insane', + 'wild', + 'прив', + 'privé', + 'privado', + 'reserved' + ], + modifiable: true), + Emoji( + name: 'man gesturing NO: medium-dark skin tone', + char: '\u{1F645}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_gesturing_no_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'forbidden', + 'gesture', + 'hand', + 'man', + 'medium-dark skin tone', + 'no', + 'prohibited', + 'uc8', + 'diversity', + 'men', + 'angry', + 'hate', + 'danger', + 'daddy', + 'crazy', + 'private', + 'never', + 'wrong', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad', + 'i hate', + 'disgust', + 'stump', + 'shout', + 'dislike', + 'rude', + 'annoy', + 'grinch', + 'gross', + 'grumpy', + 'mean', + 'problem', + 'suck', + 'jerk', + 'asshole', + 'no', + 'warn', + 'attention', + 'caution', + 'alert', + 'error', + 'panic', + 'restricted', + "don't", + 'dont', + 'dangerous', + 'dad', + 'papa', + 'pere', + 'father', + 'weird', + 'awkward', + 'insane', + 'wild', + 'прив', + 'privé', + 'privado', + 'reserved' + ], + modifiable: true), + Emoji( + name: 'man gesturing NO: dark skin tone', + char: '\u{1F645}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_gesturing_no_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'dark skin tone', + 'forbidden', + 'gesture', + 'hand', + 'man', + 'no', + 'prohibited', + 'uc8', + 'diversity', + 'men', + 'angry', + 'hate', + 'danger', + 'daddy', + 'crazy', + 'private', + 'never', + 'wrong', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad', + 'i hate', + 'disgust', + 'stump', + 'shout', + 'dislike', + 'rude', + 'annoy', + 'grinch', + 'gross', + 'grumpy', + 'mean', + 'problem', + 'suck', + 'jerk', + 'asshole', + 'no', + 'warn', + 'attention', + 'caution', + 'alert', + 'error', + 'panic', + 'restricted', + "don't", + 'dont', + 'dangerous', + 'dad', + 'papa', + 'pere', + 'father', + 'weird', + 'awkward', + 'insane', + 'wild', + 'прив', + 'privé', + 'privado', + 'reserved' + ], + modifiable: true), + Emoji( + name: 'person gesturing OK', + char: '\u{1F646}', + shortName: 'person_gesturing_ok', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'OK', + 'gesture', + 'hand', + 'uc6', + 'diversity', + 'men', + 'thank you', + 'awesome', + 'yoga', + 'crazy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'okay', + 'got it', + 'cool', + 'ok', + 'will do', + 'like', + 'bien', + 'yep', + 'yup', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'weird', + 'awkward', + 'insane', + 'wild', + '*\\0/*', + '\\0/', + '*\\O/*', + '\\O/' + ]), + Emoji( + name: 'person gesturing OK: light skin tone', + char: '\u{1F646}\u{1F3FB}', + shortName: 'person_gesturing_ok_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'OK', + 'gesture', + 'hand', + 'light skin tone', + 'uc8', + 'diversity', + 'men', + 'thank you', + 'awesome', + 'yoga', + 'crazy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'okay', + 'got it', + 'cool', + 'ok', + 'will do', + 'like', + 'bien', + 'yep', + 'yup', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'weird', + 'awkward', + 'insane', + 'wild' + ], + modifiable: true), + Emoji( + name: 'person gesturing OK: medium-light skin tone', + char: '\u{1F646}\u{1F3FC}', + shortName: 'person_gesturing_ok_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'OK', + 'gesture', + 'hand', + 'medium-light skin tone', + 'uc8', + 'diversity', + 'men', + 'thank you', + 'awesome', + 'yoga', + 'crazy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'okay', + 'got it', + 'cool', + 'ok', + 'will do', + 'like', + 'bien', + 'yep', + 'yup', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'weird', + 'awkward', + 'insane', + 'wild' + ], + modifiable: true), + Emoji( + name: 'person gesturing OK: medium skin tone', + char: '\u{1F646}\u{1F3FD}', + shortName: 'person_gesturing_ok_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'OK', + 'gesture', + 'hand', + 'medium skin tone', + 'uc8', + 'diversity', + 'men', + 'thank you', + 'awesome', + 'yoga', + 'crazy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'okay', + 'got it', + 'cool', + 'ok', + 'will do', + 'like', + 'bien', + 'yep', + 'yup', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'weird', + 'awkward', + 'insane', + 'wild' + ], + modifiable: true), + Emoji( + name: 'person gesturing OK: medium-dark skin tone', + char: '\u{1F646}\u{1F3FE}', + shortName: 'person_gesturing_ok_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'OK', + 'gesture', + 'hand', + 'medium-dark skin tone', + 'uc8', + 'diversity', + 'men', + 'thank you', + 'awesome', + 'yoga', + 'crazy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'okay', + 'got it', + 'cool', + 'ok', + 'will do', + 'like', + 'bien', + 'yep', + 'yup', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'weird', + 'awkward', + 'insane', + 'wild' + ], + modifiable: true), + Emoji( + name: 'person gesturing OK: dark skin tone', + char: '\u{1F646}\u{1F3FF}', + shortName: 'person_gesturing_ok_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'OK', + 'dark skin tone', + 'gesture', + 'hand', + 'uc8', + 'diversity', + 'men', + 'thank you', + 'awesome', + 'yoga', + 'crazy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'okay', + 'got it', + 'cool', + 'ok', + 'will do', + 'like', + 'bien', + 'yep', + 'yup', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'weird', + 'awkward', + 'insane', + 'wild' + ], + modifiable: true), + Emoji( + name: 'woman gesturing OK', + char: '\u{1F646}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_gesturing_ok', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'OK', + 'gesture', + 'hand', + 'woman', + 'uc6', + 'diversity', + 'women', + 'thank you', + 'awesome', + 'yoga', + 'crazy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'okay', + 'got it', + 'cool', + 'ok', + 'will do', + 'like', + 'bien', + 'yep', + 'yup', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'weird', + 'awkward', + 'insane', + 'wild' + ]), + Emoji( + name: 'woman gesturing OK: light skin tone', + char: '\u{1F646}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_gesturing_ok_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'OK', + 'gesture', + 'hand', + 'light skin tone', + 'woman', + 'uc8', + 'diversity', + 'women', + 'thank you', + 'awesome', + 'yoga', + 'crazy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'okay', + 'got it', + 'cool', + 'ok', + 'will do', + 'like', + 'bien', + 'yep', + 'yup', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'weird', + 'awkward', + 'insane', + 'wild' + ], + modifiable: true), + Emoji( + name: 'woman gesturing OK: medium-light skin tone', + char: '\u{1F646}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_gesturing_ok_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'OK', + 'gesture', + 'hand', + 'medium-light skin tone', + 'woman', + 'uc8', + 'diversity', + 'women', + 'thank you', + 'awesome', + 'yoga', + 'crazy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'okay', + 'got it', + 'cool', + 'ok', + 'will do', + 'like', + 'bien', + 'yep', + 'yup', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'weird', + 'awkward', + 'insane', + 'wild' + ], + modifiable: true), + Emoji( + name: 'woman gesturing OK: medium skin tone', + char: '\u{1F646}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_gesturing_ok_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'OK', + 'gesture', + 'hand', + 'medium skin tone', + 'woman', + 'uc8', + 'diversity', + 'women', + 'thank you', + 'awesome', + 'yoga', + 'crazy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'okay', + 'got it', + 'cool', + 'ok', + 'will do', + 'like', + 'bien', + 'yep', + 'yup', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'weird', + 'awkward', + 'insane', + 'wild' + ], + modifiable: true), + Emoji( + name: 'woman gesturing OK: medium-dark skin tone', + char: '\u{1F646}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_gesturing_ok_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'OK', + 'gesture', + 'hand', + 'medium-dark skin tone', + 'woman', + 'uc8', + 'diversity', + 'women', + 'thank you', + 'awesome', + 'yoga', + 'crazy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'okay', + 'got it', + 'cool', + 'ok', + 'will do', + 'like', + 'bien', + 'yep', + 'yup', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'weird', + 'awkward', + 'insane', + 'wild' + ], + modifiable: true), + Emoji( + name: 'woman gesturing OK: dark skin tone', + char: '\u{1F646}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_gesturing_ok_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'OK', + 'dark skin tone', + 'gesture', + 'hand', + 'woman', + 'uc8', + 'diversity', + 'women', + 'thank you', + 'awesome', + 'yoga', + 'crazy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'okay', + 'got it', + 'cool', + 'ok', + 'will do', + 'like', + 'bien', + 'yep', + 'yup', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'weird', + 'awkward', + 'insane', + 'wild' + ], + modifiable: true), + Emoji( + name: 'man gesturing OK', + char: '\u{1F646}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_gesturing_ok', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'OK', + 'gesture', + 'hand', + 'man', + 'uc6', + 'diversity', + 'men', + 'thank you', + 'awesome', + 'yoga', + 'crazy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'okay', + 'got it', + 'cool', + 'ok', + 'will do', + 'like', + 'bien', + 'yep', + 'yup', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'weird', + 'awkward', + 'insane', + 'wild' + ]), + Emoji( + name: 'man gesturing OK: light skin tone', + char: '\u{1F646}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_gesturing_ok_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'OK', + 'gesture', + 'hand', + 'light skin tone', + 'man', + 'uc8', + 'diversity', + 'men', + 'thank you', + 'awesome', + 'yoga', + 'crazy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'okay', + 'got it', + 'cool', + 'ok', + 'will do', + 'like', + 'bien', + 'yep', + 'yup', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'weird', + 'awkward', + 'insane', + 'wild' + ], + modifiable: true), + Emoji( + name: 'man gesturing OK: medium-light skin tone', + char: '\u{1F646}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_gesturing_ok_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'OK', + 'gesture', + 'hand', + 'man', + 'medium-light skin tone', + 'uc8', + 'diversity', + 'men', + 'thank you', + 'awesome', + 'yoga', + 'crazy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'okay', + 'got it', + 'cool', + 'ok', + 'will do', + 'like', + 'bien', + 'yep', + 'yup', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'weird', + 'awkward', + 'insane', + 'wild' + ], + modifiable: true), + Emoji( + name: 'man gesturing OK: medium skin tone', + char: '\u{1F646}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_gesturing_ok_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'OK', + 'gesture', + 'hand', + 'man', + 'medium skin tone', + 'uc8', + 'diversity', + 'men', + 'thank you', + 'awesome', + 'yoga', + 'crazy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'okay', + 'got it', + 'cool', + 'ok', + 'will do', + 'like', + 'bien', + 'yep', + 'yup', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'weird', + 'awkward', + 'insane', + 'wild' + ], + modifiable: true), + Emoji( + name: 'man gesturing OK: medium-dark skin tone', + char: '\u{1F646}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_gesturing_ok_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'OK', + 'gesture', + 'hand', + 'man', + 'medium-dark skin tone', + 'uc8', + 'diversity', + 'men', + 'thank you', + 'awesome', + 'yoga', + 'crazy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'okay', + 'got it', + 'cool', + 'ok', + 'will do', + 'like', + 'bien', + 'yep', + 'yup', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'weird', + 'awkward', + 'insane', + 'wild' + ], + modifiable: true), + Emoji( + name: 'man gesturing OK: dark skin tone', + char: '\u{1F646}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_gesturing_ok_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'OK', + 'dark skin tone', + 'gesture', + 'hand', + 'man', + 'uc8', + 'diversity', + 'men', + 'thank you', + 'awesome', + 'yoga', + 'crazy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'okay', + 'got it', + 'cool', + 'ok', + 'will do', + 'like', + 'bien', + 'yep', + 'yup', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'weird', + 'awkward', + 'insane', + 'wild' + ], + modifiable: true), + Emoji( + name: 'person raising hand', + char: '\u{1F64B}', + shortName: 'person_raising_hand', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'gesture', + 'hand', + 'happy', + 'raised', + 'uc6', + 'diversity', + 'men', + 'hi', + 'girls night', + 'boys night', + 'celebrate', + 'daddy', + 'help', + 'crazy', + 'mom', + 'proud', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'ladies night', + 'girls only', + 'girlfriend', + 'guys night', + 'celebration', + 'event', + 'celebrating', + 'festa', + 'parties', + 'events', + 'new years', + 'new year', + 'fiesta', + 'fete', + 'newyear', + 'party', + 'festive', + 'festival', + 'yolo', + 'festejar', + 'dad', + 'papa', + 'pere', + 'father', + 'weird', + 'awkward', + 'insane', + 'wild', + 'maman', + 'mommy', + 'mama', + 'mother' + ]), + Emoji( + name: 'person raising hand: light skin tone', + char: '\u{1F64B}\u{1F3FB}', + shortName: 'person_raising_hand_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'gesture', + 'hand', + 'happy', + 'light skin tone', + 'raised', + 'uc8', + 'diversity', + 'men', + 'hi', + 'girls night', + 'boys night', + 'celebrate', + 'daddy', + 'help', + 'crazy', + 'mom', + 'proud', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'ladies night', + 'girls only', + 'girlfriend', + 'guys night', + 'celebration', + 'event', + 'celebrating', + 'festa', + 'parties', + 'events', + 'new years', + 'new year', + 'fiesta', + 'fete', + 'newyear', + 'party', + 'festive', + 'festival', + 'yolo', + 'festejar', + 'dad', + 'papa', + 'pere', + 'father', + 'weird', + 'awkward', + 'insane', + 'wild', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'person raising hand: medium-light skin tone', + char: '\u{1F64B}\u{1F3FC}', + shortName: 'person_raising_hand_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'gesture', + 'hand', + 'happy', + 'medium-light skin tone', + 'raised', + 'uc8', + 'diversity', + 'men', + 'hi', + 'girls night', + 'boys night', + 'celebrate', + 'daddy', + 'help', + 'crazy', + 'mom', + 'proud', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'ladies night', + 'girls only', + 'girlfriend', + 'guys night', + 'celebration', + 'event', + 'celebrating', + 'festa', + 'parties', + 'events', + 'new years', + 'new year', + 'fiesta', + 'fete', + 'newyear', + 'party', + 'festive', + 'festival', + 'yolo', + 'festejar', + 'dad', + 'papa', + 'pere', + 'father', + 'weird', + 'awkward', + 'insane', + 'wild', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'person raising hand: medium skin tone', + char: '\u{1F64B}\u{1F3FD}', + shortName: 'person_raising_hand_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'gesture', + 'hand', + 'happy', + 'medium skin tone', + 'raised', + 'uc8', + 'diversity', + 'men', + 'hi', + 'girls night', + 'boys night', + 'celebrate', + 'daddy', + 'help', + 'crazy', + 'mom', + 'proud', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'ladies night', + 'girls only', + 'girlfriend', + 'guys night', + 'celebration', + 'event', + 'celebrating', + 'festa', + 'parties', + 'events', + 'new years', + 'new year', + 'fiesta', + 'fete', + 'newyear', + 'party', + 'festive', + 'festival', + 'yolo', + 'festejar', + 'dad', + 'papa', + 'pere', + 'father', + 'weird', + 'awkward', + 'insane', + 'wild', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'person raising hand: medium-dark skin tone', + char: '\u{1F64B}\u{1F3FE}', + shortName: 'person_raising_hand_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'gesture', + 'hand', + 'happy', + 'medium-dark skin tone', + 'raised', + 'uc8', + 'diversity', + 'men', + 'hi', + 'girls night', + 'boys night', + 'celebrate', + 'daddy', + 'help', + 'crazy', + 'mom', + 'proud', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'ladies night', + 'girls only', + 'girlfriend', + 'guys night', + 'celebration', + 'event', + 'celebrating', + 'festa', + 'parties', + 'events', + 'new years', + 'new year', + 'fiesta', + 'fete', + 'newyear', + 'party', + 'festive', + 'festival', + 'yolo', + 'festejar', + 'dad', + 'papa', + 'pere', + 'father', + 'weird', + 'awkward', + 'insane', + 'wild', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'person raising hand: dark skin tone', + char: '\u{1F64B}\u{1F3FF}', + shortName: 'person_raising_hand_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'dark skin tone', + 'gesture', + 'hand', + 'happy', + 'raised', + 'uc8', + 'diversity', + 'men', + 'hi', + 'girls night', + 'boys night', + 'celebrate', + 'daddy', + 'help', + 'crazy', + 'mom', + 'proud', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'ladies night', + 'girls only', + 'girlfriend', + 'guys night', + 'celebration', + 'event', + 'celebrating', + 'festa', + 'parties', + 'events', + 'new years', + 'new year', + 'fiesta', + 'fete', + 'newyear', + 'party', + 'festive', + 'festival', + 'yolo', + 'festejar', + 'dad', + 'papa', + 'pere', + 'father', + 'weird', + 'awkward', + 'insane', + 'wild', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman raising hand', + char: '\u{1F64B}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_raising_hand', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'gesture', + 'raising hand', + 'woman', + 'uc6', + 'diversity', + 'women', + 'hi', + 'girls night', + 'celebrate', + 'help', + 'crazy', + 'mom', + 'proud', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'ladies night', + 'girls only', + 'girlfriend', + 'celebration', + 'event', + 'celebrating', + 'festa', + 'parties', + 'events', + 'new years', + 'new year', + 'fiesta', + 'fete', + 'newyear', + 'party', + 'festive', + 'festival', + 'yolo', + 'festejar', + 'weird', + 'awkward', + 'insane', + 'wild', + 'maman', + 'mommy', + 'mama', + 'mother' + ]), + Emoji( + name: 'woman raising hand: light skin tone', + char: '\u{1F64B}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_raising_hand_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'gesture', + 'light skin tone', + 'raising hand', + 'woman', + 'uc8', + 'diversity', + 'women', + 'hi', + 'girls night', + 'celebrate', + 'help', + 'crazy', + 'mom', + 'proud', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'ladies night', + 'girls only', + 'girlfriend', + 'celebration', + 'event', + 'celebrating', + 'festa', + 'parties', + 'events', + 'new years', + 'new year', + 'fiesta', + 'fete', + 'newyear', + 'party', + 'festive', + 'festival', + 'yolo', + 'festejar', + 'weird', + 'awkward', + 'insane', + 'wild', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman raising hand: medium-light skin tone', + char: '\u{1F64B}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_raising_hand_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'gesture', + 'medium-light skin tone', + 'raising hand', + 'woman', + 'uc8', + 'diversity', + 'women', + 'hi', + 'girls night', + 'celebrate', + 'help', + 'crazy', + 'mom', + 'proud', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'ladies night', + 'girls only', + 'girlfriend', + 'celebration', + 'event', + 'celebrating', + 'festa', + 'parties', + 'events', + 'new years', + 'new year', + 'fiesta', + 'fete', + 'newyear', + 'party', + 'festive', + 'festival', + 'yolo', + 'festejar', + 'weird', + 'awkward', + 'insane', + 'wild', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman raising hand: medium skin tone', + char: '\u{1F64B}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_raising_hand_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'gesture', + 'medium skin tone', + 'raising hand', + 'woman', + 'uc8', + 'diversity', + 'women', + 'hi', + 'girls night', + 'celebrate', + 'help', + 'crazy', + 'mom', + 'proud', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'ladies night', + 'girls only', + 'girlfriend', + 'celebration', + 'event', + 'celebrating', + 'festa', + 'parties', + 'events', + 'new years', + 'new year', + 'fiesta', + 'fete', + 'newyear', + 'party', + 'festive', + 'festival', + 'yolo', + 'festejar', + 'weird', + 'awkward', + 'insane', + 'wild', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman raising hand: medium-dark skin tone', + char: '\u{1F64B}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_raising_hand_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'gesture', + 'medium-dark skin tone', + 'raising hand', + 'woman', + 'uc8', + 'diversity', + 'women', + 'hi', + 'girls night', + 'celebrate', + 'help', + 'crazy', + 'mom', + 'proud', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'ladies night', + 'girls only', + 'girlfriend', + 'celebration', + 'event', + 'celebrating', + 'festa', + 'parties', + 'events', + 'new years', + 'new year', + 'fiesta', + 'fete', + 'newyear', + 'party', + 'festive', + 'festival', + 'yolo', + 'festejar', + 'weird', + 'awkward', + 'insane', + 'wild', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman raising hand: dark skin tone', + char: '\u{1F64B}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_raising_hand_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'dark skin tone', + 'gesture', + 'raising hand', + 'woman', + 'uc8', + 'diversity', + 'women', + 'hi', + 'girls night', + 'celebrate', + 'help', + 'crazy', + 'mom', + 'proud', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'ladies night', + 'girls only', + 'girlfriend', + 'celebration', + 'event', + 'celebrating', + 'festa', + 'parties', + 'events', + 'new years', + 'new year', + 'fiesta', + 'fete', + 'newyear', + 'party', + 'festive', + 'festival', + 'yolo', + 'festejar', + 'weird', + 'awkward', + 'insane', + 'wild', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'man raising hand', + char: '\u{1F64B}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_raising_hand', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'gesture', + 'man', + 'raising hand', + 'uc6', + 'diversity', + 'men', + 'hi', + 'boys night', + 'celebrate', + 'daddy', + 'help', + 'crazy', + 'proud', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'guys night', + 'celebration', + 'event', + 'celebrating', + 'festa', + 'parties', + 'events', + 'new years', + 'new year', + 'fiesta', + 'fete', + 'newyear', + 'party', + 'festive', + 'festival', + 'yolo', + 'festejar', + 'dad', + 'papa', + 'pere', + 'father', + 'weird', + 'awkward', + 'insane', + 'wild' + ]), + Emoji( + name: 'man raising hand: light skin tone', + char: '\u{1F64B}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_raising_hand_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'gesture', + 'light skin tone', + 'man', + 'raising hand', + 'uc8', + 'diversity', + 'men', + 'hi', + 'boys night', + 'celebrate', + 'daddy', + 'help', + 'crazy', + 'proud', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'guys night', + 'celebration', + 'event', + 'celebrating', + 'festa', + 'parties', + 'events', + 'new years', + 'new year', + 'fiesta', + 'fete', + 'newyear', + 'party', + 'festive', + 'festival', + 'yolo', + 'festejar', + 'dad', + 'papa', + 'pere', + 'father', + 'weird', + 'awkward', + 'insane', + 'wild' + ], + modifiable: true), + Emoji( + name: 'man raising hand: medium-light skin tone', + char: '\u{1F64B}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_raising_hand_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'gesture', + 'man', + 'medium-light skin tone', + 'raising hand', + 'uc8', + 'diversity', + 'men', + 'hi', + 'boys night', + 'celebrate', + 'daddy', + 'help', + 'crazy', + 'proud', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'guys night', + 'celebration', + 'event', + 'celebrating', + 'festa', + 'parties', + 'events', + 'new years', + 'new year', + 'fiesta', + 'fete', + 'newyear', + 'party', + 'festive', + 'festival', + 'yolo', + 'festejar', + 'dad', + 'papa', + 'pere', + 'father', + 'weird', + 'awkward', + 'insane', + 'wild' + ], + modifiable: true), + Emoji( + name: 'man raising hand: medium skin tone', + char: '\u{1F64B}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_raising_hand_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'gesture', + 'man', + 'medium skin tone', + 'raising hand', + 'uc8', + 'diversity', + 'men', + 'hi', + 'boys night', + 'celebrate', + 'daddy', + 'help', + 'crazy', + 'proud', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'guys night', + 'celebration', + 'event', + 'celebrating', + 'festa', + 'parties', + 'events', + 'new years', + 'new year', + 'fiesta', + 'fete', + 'newyear', + 'party', + 'festive', + 'festival', + 'yolo', + 'festejar', + 'dad', + 'papa', + 'pere', + 'father', + 'weird', + 'awkward', + 'insane', + 'wild' + ], + modifiable: true), + Emoji( + name: 'man raising hand: medium-dark skin tone', + char: '\u{1F64B}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_raising_hand_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'gesture', + 'man', + 'medium-dark skin tone', + 'raising hand', + 'uc8', + 'diversity', + 'men', + 'hi', + 'boys night', + 'celebrate', + 'daddy', + 'help', + 'crazy', + 'proud', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'guys night', + 'celebration', + 'event', + 'celebrating', + 'festa', + 'parties', + 'events', + 'new years', + 'new year', + 'fiesta', + 'fete', + 'newyear', + 'party', + 'festive', + 'festival', + 'yolo', + 'festejar', + 'dad', + 'papa', + 'pere', + 'father', + 'weird', + 'awkward', + 'insane', + 'wild' + ], + modifiable: true), + Emoji( + name: 'man raising hand: dark skin tone', + char: '\u{1F64B}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_raising_hand_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'dark skin tone', + 'gesture', + 'man', + 'raising hand', + 'uc8', + 'diversity', + 'men', + 'hi', + 'boys night', + 'celebrate', + 'daddy', + 'help', + 'crazy', + 'proud', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'hello', + 'greeting', + 'bonjour', + 'bye', + 'ciao', + 'adios', + 'goodbye', + 'hey', + 'holla', + 'my name is', + 'salut', + 'welcome', + 'ПРИВЕТ', + 'tu tapelle', + 'guys night', + 'celebration', + 'event', + 'celebrating', + 'festa', + 'parties', + 'events', + 'new years', + 'new year', + 'fiesta', + 'fete', + 'newyear', + 'party', + 'festive', + 'festival', + 'yolo', + 'festejar', + 'dad', + 'papa', + 'pere', + 'father', + 'weird', + 'awkward', + 'insane', + 'wild' + ], + modifiable: true), + Emoji( + name: 'deaf person', + char: '\u{1F9CF}', + shortName: 'deaf_person', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'uc12', + 'old people', + 'diversity', + 'handicap', + 'quiet', + 'sound', + 'deaf', + 'accessibility', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'disabled', + 'disability', + 'shut up', + 'hushed', + 'silence', + 'silent', + 'shush', + 'shh', + 'volume', + 'speaker', + 'loud', + 'mic', + 'audio', + 'hear', + 'hard of hearing' + ]), + Emoji( + name: 'deaf person: light skin tone', + char: '\u{1F9CF}\u{1F3FB}', + shortName: 'deaf_person_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'uc12', + 'old people', + 'diversity', + 'handicap', + 'quiet', + 'sound', + 'deaf', + 'accessibility', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'disabled', + 'disability', + 'shut up', + 'hushed', + 'silence', + 'silent', + 'shush', + 'shh', + 'volume', + 'speaker', + 'loud', + 'mic', + 'audio', + 'hear', + 'hard of hearing' + ], + modifiable: true), + Emoji( + name: 'deaf person: medium-light skin tone', + char: '\u{1F9CF}\u{1F3FC}', + shortName: 'deaf_person_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'uc12', + 'old people', + 'diversity', + 'handicap', + 'quiet', + 'sound', + 'deaf', + 'accessibility', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'disabled', + 'disability', + 'shut up', + 'hushed', + 'silence', + 'silent', + 'shush', + 'shh', + 'volume', + 'speaker', + 'loud', + 'mic', + 'audio', + 'hear', + 'hard of hearing' + ], + modifiable: true), + Emoji( + name: 'deaf person: medium skin tone', + char: '\u{1F9CF}\u{1F3FD}', + shortName: 'deaf_person_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'uc12', + 'old people', + 'diversity', + 'handicap', + 'quiet', + 'sound', + 'deaf', + 'accessibility', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'disabled', + 'disability', + 'shut up', + 'hushed', + 'silence', + 'silent', + 'shush', + 'shh', + 'volume', + 'speaker', + 'loud', + 'mic', + 'audio', + 'hear', + 'hard of hearing' + ], + modifiable: true), + Emoji( + name: 'deaf person: medium-dark skin tone', + char: '\u{1F9CF}\u{1F3FE}', + shortName: 'deaf_person_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'uc12', + 'old people', + 'diversity', + 'handicap', + 'quiet', + 'sound', + 'deaf', + 'accessibility', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'disabled', + 'disability', + 'shut up', + 'hushed', + 'silence', + 'silent', + 'shush', + 'shh', + 'volume', + 'speaker', + 'loud', + 'mic', + 'audio', + 'hear', + 'hard of hearing' + ], + modifiable: true), + Emoji( + name: 'deaf person: dark skin tone', + char: '\u{1F9CF}\u{1F3FF}', + shortName: 'deaf_person_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'uc12', + 'old people', + 'diversity', + 'handicap', + 'quiet', + 'sound', + 'deaf', + 'accessibility', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'disabled', + 'disability', + 'shut up', + 'hushed', + 'silence', + 'silent', + 'shush', + 'shh', + 'volume', + 'speaker', + 'loud', + 'mic', + 'audio', + 'hear', + 'hard of hearing' + ], + modifiable: true), + Emoji( + name: 'deaf woman', + char: '\u{1F9CF}\u{200D}\u{2640}\u{FE0F}', + shortName: 'deaf_woman', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'uc12', + 'old people', + 'diversity', + 'women', + 'handicap', + 'quiet', + 'sound', + 'deaf', + 'accessibility', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'disabled', + 'disability', + 'shut up', + 'hushed', + 'silence', + 'silent', + 'shush', + 'shh', + 'volume', + 'speaker', + 'loud', + 'mic', + 'audio', + 'hear', + 'hard of hearing' + ]), + Emoji( + name: 'deaf woman: light skin tone', + char: '\u{1F9CF}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}', + shortName: 'deaf_woman_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'uc12', + 'old people', + 'diversity', + 'women', + 'handicap', + 'quiet', + 'sound', + 'deaf', + 'accessibility', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'disabled', + 'disability', + 'shut up', + 'hushed', + 'silence', + 'silent', + 'shush', + 'shh', + 'volume', + 'speaker', + 'loud', + 'mic', + 'audio', + 'hear', + 'hard of hearing' + ], + modifiable: true), + Emoji( + name: 'deaf woman: medium-light skin tone', + char: '\u{1F9CF}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}', + shortName: 'deaf_woman_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'uc12', + 'old people', + 'diversity', + 'women', + 'handicap', + 'quiet', + 'sound', + 'deaf', + 'accessibility', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'disabled', + 'disability', + 'shut up', + 'hushed', + 'silence', + 'silent', + 'shush', + 'shh', + 'volume', + 'speaker', + 'loud', + 'mic', + 'audio', + 'hear', + 'hard of hearing' + ], + modifiable: true), + Emoji( + name: 'deaf woman: medium skin tone', + char: '\u{1F9CF}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}', + shortName: 'deaf_woman_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'uc12', + 'old people', + 'diversity', + 'women', + 'handicap', + 'quiet', + 'sound', + 'deaf', + 'accessibility', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'disabled', + 'disability', + 'shut up', + 'hushed', + 'silence', + 'silent', + 'shush', + 'shh', + 'volume', + 'speaker', + 'loud', + 'mic', + 'audio', + 'hear', + 'hard of hearing' + ], + modifiable: true), + Emoji( + name: 'deaf woman: medium-dark skin tone', + char: '\u{1F9CF}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}', + shortName: 'deaf_woman_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'uc12', + 'old people', + 'diversity', + 'women', + 'handicap', + 'quiet', + 'sound', + 'deaf', + 'accessibility', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'disabled', + 'disability', + 'shut up', + 'hushed', + 'silence', + 'silent', + 'shush', + 'shh', + 'volume', + 'speaker', + 'loud', + 'mic', + 'audio', + 'hear', + 'hard of hearing' + ], + modifiable: true), + Emoji( + name: 'deaf woman: dark skin tone', + char: '\u{1F9CF}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}', + shortName: 'deaf_woman_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'uc12', + 'old people', + 'diversity', + 'women', + 'handicap', + 'quiet', + 'sound', + 'deaf', + 'accessibility', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'disabled', + 'disability', + 'shut up', + 'hushed', + 'silence', + 'silent', + 'shush', + 'shh', + 'volume', + 'speaker', + 'loud', + 'mic', + 'audio', + 'hear', + 'hard of hearing' + ], + modifiable: true), + Emoji( + name: 'deaf man', + char: '\u{1F9CF}\u{200D}\u{2642}\u{FE0F}', + shortName: 'deaf_man', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'uc12', + 'old people', + 'diversity', + 'handicap', + 'quiet', + 'sound', + 'deaf', + 'accessibility', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'disabled', + 'disability', + 'shut up', + 'hushed', + 'silence', + 'silent', + 'shush', + 'shh', + 'volume', + 'speaker', + 'loud', + 'mic', + 'audio', + 'hear', + 'hard of hearing' + ]), + Emoji( + name: 'deaf man: light skin tone', + char: '\u{1F9CF}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}', + shortName: 'deaf_man_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'uc12', + 'old people', + 'diversity', + 'handicap', + 'quiet', + 'sound', + 'deaf', + 'accessibility', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'disabled', + 'disability', + 'shut up', + 'hushed', + 'silence', + 'silent', + 'shush', + 'shh', + 'volume', + 'speaker', + 'loud', + 'mic', + 'audio', + 'hear', + 'hard of hearing' + ], + modifiable: true), + Emoji( + name: 'deaf man: medium-light skin tone', + char: '\u{1F9CF}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}', + shortName: 'deaf_man_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'uc12', + 'old people', + 'diversity', + 'handicap', + 'quiet', + 'sound', + 'deaf', + 'accessibility', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'disabled', + 'disability', + 'shut up', + 'hushed', + 'silence', + 'silent', + 'shush', + 'shh', + 'volume', + 'speaker', + 'loud', + 'mic', + 'audio', + 'hear', + 'hard of hearing' + ], + modifiable: true), + Emoji( + name: 'deaf man: medium skin tone', + char: '\u{1F9CF}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}', + shortName: 'deaf_man_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'uc12', + 'old people', + 'diversity', + 'handicap', + 'quiet', + 'sound', + 'deaf', + 'accessibility', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'disabled', + 'disability', + 'shut up', + 'hushed', + 'silence', + 'silent', + 'shush', + 'shh', + 'volume', + 'speaker', + 'loud', + 'mic', + 'audio', + 'hear', + 'hard of hearing' + ], + modifiable: true), + Emoji( + name: 'deaf man: medium-dark skin tone', + char: '\u{1F9CF}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}', + shortName: 'deaf_man_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'uc12', + 'old people', + 'diversity', + 'handicap', + 'quiet', + 'sound', + 'deaf', + 'accessibility', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'disabled', + 'disability', + 'shut up', + 'hushed', + 'silence', + 'silent', + 'shush', + 'shh', + 'volume', + 'speaker', + 'loud', + 'mic', + 'audio', + 'hear', + 'hard of hearing' + ], + modifiable: true), + Emoji( + name: 'deaf man: dark skin tone', + char: '\u{1F9CF}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}', + shortName: 'deaf_man_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'uc12', + 'old people', + 'diversity', + 'handicap', + 'quiet', + 'sound', + 'deaf', + 'accessibility', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'disabled', + 'disability', + 'shut up', + 'hushed', + 'silence', + 'silent', + 'shush', + 'shh', + 'volume', + 'speaker', + 'loud', + 'mic', + 'audio', + 'hear', + 'hard of hearing' + ], + modifiable: true), + Emoji( + name: 'person facepalming', + char: '\u{1F926}', + shortName: 'person_facepalming', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'disbelief', + 'exasperation', + 'face', + 'palm', + 'uc9', + 'diversity', + 'men', + 'stressed', + 'boys night', + 'facepalm', + 'dumb', + 'las vegas', + 'crazy', + 'wrong', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'guys night', + 'whoops', + 'oops', + 'mistake', + 'idiot', + 'ignorant', + 'stupid', + 'vegas', + 'weird', + 'awkward', + 'insane', + 'wild' + ]), + Emoji( + name: 'person facepalming: light skin tone', + char: '\u{1F926}\u{1F3FB}', + shortName: 'person_facepalming_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'disbelief', + 'exasperation', + 'face', + 'light skin tone', + 'palm', + 'uc9', + 'diversity', + 'men', + 'stressed', + 'boys night', + 'facepalm', + 'dumb', + 'las vegas', + 'crazy', + 'wrong', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'guys night', + 'whoops', + 'oops', + 'mistake', + 'idiot', + 'ignorant', + 'stupid', + 'vegas', + 'weird', + 'awkward', + 'insane', + 'wild' + ], + modifiable: true), + Emoji( + name: 'person facepalming: medium-light skin tone', + char: '\u{1F926}\u{1F3FC}', + shortName: 'person_facepalming_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'disbelief', + 'exasperation', + 'face', + 'medium-light skin tone', + 'palm', + 'uc9', + 'diversity', + 'men', + 'stressed', + 'boys night', + 'facepalm', + 'dumb', + 'las vegas', + 'crazy', + 'wrong', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'guys night', + 'whoops', + 'oops', + 'mistake', + 'idiot', + 'ignorant', + 'stupid', + 'vegas', + 'weird', + 'awkward', + 'insane', + 'wild' + ], + modifiable: true), + Emoji( + name: 'person facepalming: medium skin tone', + char: '\u{1F926}\u{1F3FD}', + shortName: 'person_facepalming_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'disbelief', + 'exasperation', + 'face', + 'medium skin tone', + 'palm', + 'uc9', + 'diversity', + 'men', + 'stressed', + 'boys night', + 'facepalm', + 'dumb', + 'las vegas', + 'crazy', + 'wrong', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'guys night', + 'whoops', + 'oops', + 'mistake', + 'idiot', + 'ignorant', + 'stupid', + 'vegas', + 'weird', + 'awkward', + 'insane', + 'wild' + ], + modifiable: true), + Emoji( + name: 'person facepalming: medium-dark skin tone', + char: '\u{1F926}\u{1F3FE}', + shortName: 'person_facepalming_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'disbelief', + 'exasperation', + 'face', + 'medium-dark skin tone', + 'palm', + 'uc9', + 'diversity', + 'men', + 'stressed', + 'boys night', + 'facepalm', + 'dumb', + 'las vegas', + 'crazy', + 'wrong', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'guys night', + 'whoops', + 'oops', + 'mistake', + 'idiot', + 'ignorant', + 'stupid', + 'vegas', + 'weird', + 'awkward', + 'insane', + 'wild' + ], + modifiable: true), + Emoji( + name: 'person facepalming: dark skin tone', + char: '\u{1F926}\u{1F3FF}', + shortName: 'person_facepalming_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'dark skin tone', + 'disbelief', + 'exasperation', + 'face', + 'palm', + 'uc9', + 'diversity', + 'men', + 'stressed', + 'boys night', + 'facepalm', + 'dumb', + 'las vegas', + 'crazy', + 'wrong', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'guys night', + 'whoops', + 'oops', + 'mistake', + 'idiot', + 'ignorant', + 'stupid', + 'vegas', + 'weird', + 'awkward', + 'insane', + 'wild' + ], + modifiable: true), + Emoji( + name: 'woman facepalming', + char: '\u{1F926}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_facepalming', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'disbelief', + 'exasperation', + 'facepalm', + 'woman', + 'uc9', + 'diversity', + 'women', + 'stressed', + 'facepalm', + 'dumb', + 'las vegas', + 'crazy', + 'wrong', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'whoops', + 'oops', + 'mistake', + 'idiot', + 'ignorant', + 'stupid', + 'vegas', + 'weird', + 'awkward', + 'insane', + 'wild' + ]), + Emoji( + name: 'woman facepalming: light skin tone', + char: '\u{1F926}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_facepalming_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'disbelief', + 'exasperation', + 'facepalm', + 'light skin tone', + 'woman', + 'uc9', + 'diversity', + 'women', + 'stressed', + 'facepalm', + 'dumb', + 'las vegas', + 'crazy', + 'wrong', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'whoops', + 'oops', + 'mistake', + 'idiot', + 'ignorant', + 'stupid', + 'vegas', + 'weird', + 'awkward', + 'insane', + 'wild' + ], + modifiable: true), + Emoji( + name: 'woman facepalming: medium-light skin tone', + char: '\u{1F926}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_facepalming_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'disbelief', + 'exasperation', + 'facepalm', + 'medium-light skin tone', + 'woman', + 'uc9', + 'diversity', + 'women', + 'stressed', + 'facepalm', + 'dumb', + 'las vegas', + 'crazy', + 'wrong', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'whoops', + 'oops', + 'mistake', + 'idiot', + 'ignorant', + 'stupid', + 'vegas', + 'weird', + 'awkward', + 'insane', + 'wild' + ], + modifiable: true), + Emoji( + name: 'woman facepalming: medium skin tone', + char: '\u{1F926}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_facepalming_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'disbelief', + 'exasperation', + 'facepalm', + 'medium skin tone', + 'woman', + 'uc9', + 'diversity', + 'women', + 'stressed', + 'facepalm', + 'dumb', + 'las vegas', + 'crazy', + 'wrong', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'whoops', + 'oops', + 'mistake', + 'idiot', + 'ignorant', + 'stupid', + 'vegas', + 'weird', + 'awkward', + 'insane', + 'wild' + ], + modifiable: true), + Emoji( + name: 'woman facepalming: medium-dark skin tone', + char: '\u{1F926}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_facepalming_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'disbelief', + 'exasperation', + 'facepalm', + 'medium-dark skin tone', + 'woman', + 'uc9', + 'diversity', + 'women', + 'stressed', + 'facepalm', + 'dumb', + 'las vegas', + 'crazy', + 'wrong', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'whoops', + 'oops', + 'mistake', + 'idiot', + 'ignorant', + 'stupid', + 'vegas', + 'weird', + 'awkward', + 'insane', + 'wild' + ], + modifiable: true), + Emoji( + name: 'woman facepalming: dark skin tone', + char: '\u{1F926}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_facepalming_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'dark skin tone', + 'disbelief', + 'exasperation', + 'facepalm', + 'woman', + 'uc9', + 'diversity', + 'women', + 'stressed', + 'facepalm', + 'dumb', + 'las vegas', + 'crazy', + 'wrong', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'whoops', + 'oops', + 'mistake', + 'idiot', + 'ignorant', + 'stupid', + 'vegas', + 'weird', + 'awkward', + 'insane', + 'wild' + ], + modifiable: true), + Emoji( + name: 'man facepalming', + char: '\u{1F926}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_facepalming', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'disbelief', + 'exasperation', + 'facepalm', + 'man', + 'uc9', + 'diversity', + 'men', + 'stressed', + 'boys night', + 'facepalm', + 'dumb', + 'las vegas', + 'crazy', + 'wrong', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'guys night', + 'whoops', + 'oops', + 'mistake', + 'idiot', + 'ignorant', + 'stupid', + 'vegas', + 'weird', + 'awkward', + 'insane', + 'wild' + ]), + Emoji( + name: 'man facepalming: light skin tone', + char: '\u{1F926}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_facepalming_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'disbelief', + 'exasperation', + 'facepalm', + 'light skin tone', + 'man', + 'uc9', + 'diversity', + 'men', + 'stressed', + 'boys night', + 'facepalm', + 'dumb', + 'las vegas', + 'crazy', + 'wrong', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'guys night', + 'whoops', + 'oops', + 'mistake', + 'idiot', + 'ignorant', + 'stupid', + 'vegas', + 'weird', + 'awkward', + 'insane', + 'wild' + ], + modifiable: true), + Emoji( + name: 'man facepalming: medium-light skin tone', + char: '\u{1F926}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_facepalming_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'disbelief', + 'exasperation', + 'facepalm', + 'man', + 'medium-light skin tone', + 'uc9', + 'diversity', + 'men', + 'stressed', + 'boys night', + 'facepalm', + 'dumb', + 'las vegas', + 'crazy', + 'wrong', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'guys night', + 'whoops', + 'oops', + 'mistake', + 'idiot', + 'ignorant', + 'stupid', + 'vegas', + 'weird', + 'awkward', + 'insane', + 'wild' + ], + modifiable: true), + Emoji( + name: 'man facepalming: medium skin tone', + char: '\u{1F926}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_facepalming_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'disbelief', + 'exasperation', + 'facepalm', + 'man', + 'medium skin tone', + 'uc9', + 'diversity', + 'men', + 'stressed', + 'boys night', + 'facepalm', + 'dumb', + 'las vegas', + 'crazy', + 'wrong', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'guys night', + 'whoops', + 'oops', + 'mistake', + 'idiot', + 'ignorant', + 'stupid', + 'vegas', + 'weird', + 'awkward', + 'insane', + 'wild' + ], + modifiable: true), + Emoji( + name: 'man facepalming: medium-dark skin tone', + char: '\u{1F926}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_facepalming_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'disbelief', + 'exasperation', + 'facepalm', + 'man', + 'medium-dark skin tone', + 'uc9', + 'diversity', + 'men', + 'stressed', + 'boys night', + 'facepalm', + 'dumb', + 'las vegas', + 'crazy', + 'wrong', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'guys night', + 'whoops', + 'oops', + 'mistake', + 'idiot', + 'ignorant', + 'stupid', + 'vegas', + 'weird', + 'awkward', + 'insane', + 'wild' + ], + modifiable: true), + Emoji( + name: 'man facepalming: dark skin tone', + char: '\u{1F926}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_facepalming_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'dark skin tone', + 'disbelief', + 'exasperation', + 'facepalm', + 'man', + 'uc9', + 'diversity', + 'men', + 'stressed', + 'boys night', + 'facepalm', + 'dumb', + 'las vegas', + 'crazy', + 'wrong', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'guys night', + 'whoops', + 'oops', + 'mistake', + 'idiot', + 'ignorant', + 'stupid', + 'vegas', + 'weird', + 'awkward', + 'insane', + 'wild' + ], + modifiable: true), + Emoji( + name: 'person shrugging', + char: '\u{1F937}', + shortName: 'person_shrugging', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'doubt', + 'ignorance', + 'indifference', + 'shrug', + 'uc9', + 'diversity', + 'men', + 'shrug', + 'neutral', + 'bitch', + 'daddy', + 'doubt', + 'dumb', + 'confused', + 'what', + 'crazy', + 'mystery', + 'question', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'puta', + 'pute', + 'dad', + 'papa', + 'pere', + 'father', + 'unsure', + 'thinking', + 'wonder', + 'curious', + 'worry', + 'pensive', + 'remember', + 'skeptical', + 'idiot', + 'ignorant', + 'stupid', + 'perplexed', + 'weird', + 'awkward', + 'insane', + 'wild', + 'quiz', + 'puzzled' + ]), + Emoji( + name: 'person shrugging: light skin tone', + char: '\u{1F937}\u{1F3FB}', + shortName: 'person_shrugging_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'doubt', + 'ignorance', + 'indifference', + 'light skin tone', + 'shrug', + 'uc9', + 'diversity', + 'men', + 'shrug', + 'neutral', + 'bitch', + 'daddy', + 'doubt', + 'dumb', + 'confused', + 'what', + 'crazy', + 'mystery', + 'question', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'puta', + 'pute', + 'dad', + 'papa', + 'pere', + 'father', + 'unsure', + 'thinking', + 'wonder', + 'curious', + 'worry', + 'pensive', + 'remember', + 'skeptical', + 'idiot', + 'ignorant', + 'stupid', + 'perplexed', + 'weird', + 'awkward', + 'insane', + 'wild', + 'quiz', + 'puzzled' + ], + modifiable: true), + Emoji( + name: 'person shrugging: medium-light skin tone', + char: '\u{1F937}\u{1F3FC}', + shortName: 'person_shrugging_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'doubt', + 'ignorance', + 'indifference', + 'medium-light skin tone', + 'shrug', + 'uc9', + 'diversity', + 'men', + 'shrug', + 'neutral', + 'bitch', + 'daddy', + 'doubt', + 'dumb', + 'confused', + 'what', + 'crazy', + 'mystery', + 'question', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'puta', + 'pute', + 'dad', + 'papa', + 'pere', + 'father', + 'unsure', + 'thinking', + 'wonder', + 'curious', + 'worry', + 'pensive', + 'remember', + 'skeptical', + 'idiot', + 'ignorant', + 'stupid', + 'perplexed', + 'weird', + 'awkward', + 'insane', + 'wild', + 'quiz', + 'puzzled' + ], + modifiable: true), + Emoji( + name: 'person shrugging: medium skin tone', + char: '\u{1F937}\u{1F3FD}', + shortName: 'person_shrugging_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'doubt', + 'ignorance', + 'indifference', + 'medium skin tone', + 'shrug', + 'uc9', + 'diversity', + 'men', + 'shrug', + 'neutral', + 'bitch', + 'daddy', + 'doubt', + 'dumb', + 'confused', + 'what', + 'crazy', + 'mystery', + 'question', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'puta', + 'pute', + 'dad', + 'papa', + 'pere', + 'father', + 'unsure', + 'thinking', + 'wonder', + 'curious', + 'worry', + 'pensive', + 'remember', + 'skeptical', + 'idiot', + 'ignorant', + 'stupid', + 'perplexed', + 'weird', + 'awkward', + 'insane', + 'wild', + 'quiz', + 'puzzled' + ], + modifiable: true), + Emoji( + name: 'person shrugging: medium-dark skin tone', + char: '\u{1F937}\u{1F3FE}', + shortName: 'person_shrugging_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'doubt', + 'ignorance', + 'indifference', + 'medium-dark skin tone', + 'shrug', + 'uc9', + 'diversity', + 'men', + 'shrug', + 'neutral', + 'bitch', + 'daddy', + 'doubt', + 'dumb', + 'confused', + 'what', + 'crazy', + 'mystery', + 'question', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'puta', + 'pute', + 'dad', + 'papa', + 'pere', + 'father', + 'unsure', + 'thinking', + 'wonder', + 'curious', + 'worry', + 'pensive', + 'remember', + 'skeptical', + 'idiot', + 'ignorant', + 'stupid', + 'perplexed', + 'weird', + 'awkward', + 'insane', + 'wild', + 'quiz', + 'puzzled' + ], + modifiable: true), + Emoji( + name: 'person shrugging: dark skin tone', + char: '\u{1F937}\u{1F3FF}', + shortName: 'person_shrugging_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'dark skin tone', + 'doubt', + 'ignorance', + 'indifference', + 'shrug', + 'uc9', + 'diversity', + 'men', + 'shrug', + 'neutral', + 'bitch', + 'daddy', + 'doubt', + 'dumb', + 'confused', + 'what', + 'crazy', + 'mystery', + 'question', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'puta', + 'pute', + 'dad', + 'papa', + 'pere', + 'father', + 'unsure', + 'thinking', + 'wonder', + 'curious', + 'worry', + 'pensive', + 'remember', + 'skeptical', + 'idiot', + 'ignorant', + 'stupid', + 'perplexed', + 'weird', + 'awkward', + 'insane', + 'wild', + 'quiz', + 'puzzled' + ], + modifiable: true), + Emoji( + name: 'woman shrugging', + char: '\u{1F937}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_shrugging', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'doubt', + 'ignorance', + 'indifference', + 'shrug', + 'woman', + 'uc9', + 'diversity', + 'women', + 'shrug', + 'neutral', + 'bitch', + 'doubt', + 'dumb', + 'guilty', + 'confused', + 'what', + 'crazy', + 'mystery', + 'question', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'puta', + 'pute', + 'unsure', + 'thinking', + 'wonder', + 'curious', + 'worry', + 'pensive', + 'remember', + 'skeptical', + 'idiot', + 'ignorant', + 'stupid', + 'perplexed', + 'weird', + 'awkward', + 'insane', + 'wild', + 'quiz', + 'puzzled' + ]), + Emoji( + name: 'woman shrugging: light skin tone', + char: '\u{1F937}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_shrugging_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'doubt', + 'ignorance', + 'indifference', + 'light skin tone', + 'shrug', + 'woman', + 'uc9', + 'diversity', + 'women', + 'shrug', + 'neutral', + 'bitch', + 'doubt', + 'dumb', + 'guilty', + 'confused', + 'what', + 'crazy', + 'mystery', + 'question', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'puta', + 'pute', + 'unsure', + 'thinking', + 'wonder', + 'curious', + 'worry', + 'pensive', + 'remember', + 'skeptical', + 'idiot', + 'ignorant', + 'stupid', + 'perplexed', + 'weird', + 'awkward', + 'insane', + 'wild', + 'quiz', + 'puzzled' + ], + modifiable: true), + Emoji( + name: 'woman shrugging: medium-light skin tone', + char: '\u{1F937}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_shrugging_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'doubt', + 'ignorance', + 'indifference', + 'medium-light skin tone', + 'shrug', + 'woman', + 'uc9', + 'diversity', + 'women', + 'shrug', + 'neutral', + 'bitch', + 'doubt', + 'dumb', + 'guilty', + 'confused', + 'what', + 'crazy', + 'mystery', + 'question', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'puta', + 'pute', + 'unsure', + 'thinking', + 'wonder', + 'curious', + 'worry', + 'pensive', + 'remember', + 'skeptical', + 'idiot', + 'ignorant', + 'stupid', + 'perplexed', + 'weird', + 'awkward', + 'insane', + 'wild', + 'quiz', + 'puzzled' + ], + modifiable: true), + Emoji( + name: 'woman shrugging: medium skin tone', + char: '\u{1F937}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_shrugging_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'doubt', + 'ignorance', + 'indifference', + 'medium skin tone', + 'shrug', + 'woman', + 'uc9', + 'diversity', + 'women', + 'shrug', + 'neutral', + 'bitch', + 'doubt', + 'dumb', + 'guilty', + 'confused', + 'what', + 'crazy', + 'mystery', + 'question', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'puta', + 'pute', + 'unsure', + 'thinking', + 'wonder', + 'curious', + 'worry', + 'pensive', + 'remember', + 'skeptical', + 'idiot', + 'ignorant', + 'stupid', + 'perplexed', + 'weird', + 'awkward', + 'insane', + 'wild', + 'quiz', + 'puzzled' + ], + modifiable: true), + Emoji( + name: 'woman shrugging: medium-dark skin tone', + char: '\u{1F937}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_shrugging_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'doubt', + 'ignorance', + 'indifference', + 'medium-dark skin tone', + 'shrug', + 'woman', + 'uc9', + 'diversity', + 'women', + 'shrug', + 'neutral', + 'bitch', + 'doubt', + 'dumb', + 'guilty', + 'confused', + 'what', + 'crazy', + 'mystery', + 'question', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'puta', + 'pute', + 'unsure', + 'thinking', + 'wonder', + 'curious', + 'worry', + 'pensive', + 'remember', + 'skeptical', + 'idiot', + 'ignorant', + 'stupid', + 'perplexed', + 'weird', + 'awkward', + 'insane', + 'wild', + 'quiz', + 'puzzled' + ], + modifiable: true), + Emoji( + name: 'woman shrugging: dark skin tone', + char: '\u{1F937}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_shrugging_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'dark skin tone', + 'doubt', + 'ignorance', + 'indifference', + 'shrug', + 'woman', + 'uc9', + 'diversity', + 'women', + 'shrug', + 'neutral', + 'bitch', + 'doubt', + 'dumb', + 'guilty', + 'confused', + 'what', + 'crazy', + 'mystery', + 'question', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'puta', + 'pute', + 'unsure', + 'thinking', + 'wonder', + 'curious', + 'worry', + 'pensive', + 'remember', + 'skeptical', + 'idiot', + 'ignorant', + 'stupid', + 'perplexed', + 'weird', + 'awkward', + 'insane', + 'wild', + 'quiz', + 'puzzled' + ], + modifiable: true), + Emoji( + name: 'man shrugging', + char: '\u{1F937}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_shrugging', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'doubt', + 'ignorance', + 'indifference', + 'man', + 'shrug', + 'uc9', + 'diversity', + 'men', + 'shrug', + 'neutral', + 'daddy', + 'doubt', + 'dumb', + 'guilty', + 'confused', + 'what', + 'crazy', + 'mystery', + 'question', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'dad', + 'papa', + 'pere', + 'father', + 'unsure', + 'thinking', + 'wonder', + 'curious', + 'worry', + 'pensive', + 'remember', + 'skeptical', + 'idiot', + 'ignorant', + 'stupid', + 'perplexed', + 'weird', + 'awkward', + 'insane', + 'wild', + 'quiz', + 'puzzled' + ]), + Emoji( + name: 'man shrugging: light skin tone', + char: '\u{1F937}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_shrugging_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'doubt', + 'ignorance', + 'indifference', + 'light skin tone', + 'man', + 'shrug', + 'uc9', + 'diversity', + 'men', + 'shrug', + 'neutral', + 'daddy', + 'doubt', + 'dumb', + 'guilty', + 'confused', + 'what', + 'crazy', + 'mystery', + 'question', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'dad', + 'papa', + 'pere', + 'father', + 'unsure', + 'thinking', + 'wonder', + 'curious', + 'worry', + 'pensive', + 'remember', + 'skeptical', + 'idiot', + 'ignorant', + 'stupid', + 'perplexed', + 'weird', + 'awkward', + 'insane', + 'wild', + 'quiz', + 'puzzled' + ], + modifiable: true), + Emoji( + name: 'man shrugging: medium-light skin tone', + char: '\u{1F937}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_shrugging_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'doubt', + 'ignorance', + 'indifference', + 'man', + 'medium-light skin tone', + 'shrug', + 'uc9', + 'diversity', + 'men', + 'shrug', + 'neutral', + 'daddy', + 'doubt', + 'dumb', + 'guilty', + 'confused', + 'what', + 'crazy', + 'mystery', + 'question', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'dad', + 'papa', + 'pere', + 'father', + 'unsure', + 'thinking', + 'wonder', + 'curious', + 'worry', + 'pensive', + 'remember', + 'skeptical', + 'idiot', + 'ignorant', + 'stupid', + 'perplexed', + 'weird', + 'awkward', + 'insane', + 'wild', + 'quiz', + 'puzzled' + ], + modifiable: true), + Emoji( + name: 'man shrugging: medium skin tone', + char: '\u{1F937}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_shrugging_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'doubt', + 'ignorance', + 'indifference', + 'man', + 'medium skin tone', + 'shrug', + 'uc9', + 'diversity', + 'men', + 'shrug', + 'neutral', + 'daddy', + 'doubt', + 'dumb', + 'guilty', + 'confused', + 'what', + 'crazy', + 'mystery', + 'question', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'dad', + 'papa', + 'pere', + 'father', + 'unsure', + 'thinking', + 'wonder', + 'curious', + 'worry', + 'pensive', + 'remember', + 'skeptical', + 'idiot', + 'ignorant', + 'stupid', + 'perplexed', + 'weird', + 'awkward', + 'insane', + 'wild', + 'quiz', + 'puzzled' + ], + modifiable: true), + Emoji( + name: 'man shrugging: medium-dark skin tone', + char: '\u{1F937}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_shrugging_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'doubt', + 'ignorance', + 'indifference', + 'man', + 'medium-dark skin tone', + 'shrug', + 'uc9', + 'diversity', + 'men', + 'shrug', + 'neutral', + 'daddy', + 'doubt', + 'dumb', + 'guilty', + 'confused', + 'what', + 'crazy', + 'mystery', + 'question', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'dad', + 'papa', + 'pere', + 'father', + 'unsure', + 'thinking', + 'wonder', + 'curious', + 'worry', + 'pensive', + 'remember', + 'skeptical', + 'idiot', + 'ignorant', + 'stupid', + 'perplexed', + 'weird', + 'awkward', + 'insane', + 'wild', + 'quiz', + 'puzzled' + ], + modifiable: true), + Emoji( + name: 'man shrugging: dark skin tone', + char: '\u{1F937}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_shrugging_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'dark skin tone', + 'doubt', + 'ignorance', + 'indifference', + 'man', + 'shrug', + 'uc9', + 'diversity', + 'men', + 'shrug', + 'neutral', + 'daddy', + 'doubt', + 'dumb', + 'guilty', + 'confused', + 'what', + 'crazy', + 'mystery', + 'question', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'dad', + 'papa', + 'pere', + 'father', + 'unsure', + 'thinking', + 'wonder', + 'curious', + 'worry', + 'pensive', + 'remember', + 'skeptical', + 'idiot', + 'ignorant', + 'stupid', + 'perplexed', + 'weird', + 'awkward', + 'insane', + 'wild', + 'quiz', + 'puzzled' + ], + modifiable: true), + Emoji( + name: 'person pouting', + char: '\u{1F64E}', + shortName: 'person_pouting', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'gesture', + 'pouting', + 'uc6', + 'diversity', + 'sad', + 'men', + 'angry', + 'stressed', + 'hate', + 'bitch', + 'daddy', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'triste', + 'depression', + 'negative', + 'sadness', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad', + 'i hate', + 'disgust', + 'stump', + 'shout', + 'dislike', + 'rude', + 'annoy', + 'grinch', + 'gross', + 'grumpy', + 'mean', + 'problem', + 'suck', + 'jerk', + 'asshole', + 'no', + 'puta', + 'pute', + 'dad', + 'papa', + 'pere', + 'father', + 'maman', + 'mommy', + 'mama', + 'mother' + ]), + Emoji( + name: 'person pouting: light skin tone', + char: '\u{1F64E}\u{1F3FB}', + shortName: 'person_pouting_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'gesture', + 'light skin tone', + 'pouting', + 'uc8', + 'diversity', + 'sad', + 'men', + 'angry', + 'stressed', + 'hate', + 'bitch', + 'daddy', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'triste', + 'depression', + 'negative', + 'sadness', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad', + 'i hate', + 'disgust', + 'stump', + 'shout', + 'dislike', + 'rude', + 'annoy', + 'grinch', + 'gross', + 'grumpy', + 'mean', + 'problem', + 'suck', + 'jerk', + 'asshole', + 'no', + 'puta', + 'pute', + 'dad', + 'papa', + 'pere', + 'father', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'person pouting: medium-light skin tone', + char: '\u{1F64E}\u{1F3FC}', + shortName: 'person_pouting_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'gesture', + 'medium-light skin tone', + 'pouting', + 'uc8', + 'diversity', + 'sad', + 'men', + 'angry', + 'stressed', + 'hate', + 'bitch', + 'daddy', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'triste', + 'depression', + 'negative', + 'sadness', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad', + 'i hate', + 'disgust', + 'stump', + 'shout', + 'dislike', + 'rude', + 'annoy', + 'grinch', + 'gross', + 'grumpy', + 'mean', + 'problem', + 'suck', + 'jerk', + 'asshole', + 'no', + 'puta', + 'pute', + 'dad', + 'papa', + 'pere', + 'father', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'person pouting: medium skin tone', + char: '\u{1F64E}\u{1F3FD}', + shortName: 'person_pouting_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'gesture', + 'medium skin tone', + 'pouting', + 'uc8', + 'diversity', + 'sad', + 'men', + 'angry', + 'stressed', + 'hate', + 'bitch', + 'daddy', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'triste', + 'depression', + 'negative', + 'sadness', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad', + 'i hate', + 'disgust', + 'stump', + 'shout', + 'dislike', + 'rude', + 'annoy', + 'grinch', + 'gross', + 'grumpy', + 'mean', + 'problem', + 'suck', + 'jerk', + 'asshole', + 'no', + 'puta', + 'pute', + 'dad', + 'papa', + 'pere', + 'father', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'person pouting: medium-dark skin tone', + char: '\u{1F64E}\u{1F3FE}', + shortName: 'person_pouting_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'gesture', + 'medium-dark skin tone', + 'pouting', + 'uc8', + 'diversity', + 'sad', + 'men', + 'angry', + 'stressed', + 'hate', + 'bitch', + 'daddy', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'triste', + 'depression', + 'negative', + 'sadness', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad', + 'i hate', + 'disgust', + 'stump', + 'shout', + 'dislike', + 'rude', + 'annoy', + 'grinch', + 'gross', + 'grumpy', + 'mean', + 'problem', + 'suck', + 'jerk', + 'asshole', + 'no', + 'puta', + 'pute', + 'dad', + 'papa', + 'pere', + 'father', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'person pouting: dark skin tone', + char: '\u{1F64E}\u{1F3FF}', + shortName: 'person_pouting_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'dark skin tone', + 'gesture', + 'pouting', + 'uc8', + 'diversity', + 'sad', + 'men', + 'angry', + 'stressed', + 'hate', + 'bitch', + 'daddy', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'triste', + 'depression', + 'negative', + 'sadness', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad', + 'i hate', + 'disgust', + 'stump', + 'shout', + 'dislike', + 'rude', + 'annoy', + 'grinch', + 'gross', + 'grumpy', + 'mean', + 'problem', + 'suck', + 'jerk', + 'asshole', + 'no', + 'puta', + 'pute', + 'dad', + 'papa', + 'pere', + 'father', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman pouting', + char: '\u{1F64E}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_pouting', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'gesture', + 'pouting', + 'woman', + 'uc6', + 'diversity', + 'sad', + 'women', + 'stressed', + 'bitch', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'triste', + 'depression', + 'negative', + 'sadness', + 'woman', + 'female', + 'puta', + 'pute', + 'maman', + 'mommy', + 'mama', + 'mother' + ]), + Emoji( + name: 'woman pouting: light skin tone', + char: '\u{1F64E}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_pouting_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'gesture', + 'light skin tone', + 'pouting', + 'woman', + 'uc8', + 'diversity', + 'sad', + 'women', + 'stressed', + 'bitch', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'triste', + 'depression', + 'negative', + 'sadness', + 'woman', + 'female', + 'puta', + 'pute', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman pouting: medium-light skin tone', + char: '\u{1F64E}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_pouting_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'gesture', + 'medium-light skin tone', + 'pouting', + 'woman', + 'uc8', + 'diversity', + 'sad', + 'women', + 'stressed', + 'bitch', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'triste', + 'depression', + 'negative', + 'sadness', + 'woman', + 'female', + 'puta', + 'pute', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman pouting: medium skin tone', + char: '\u{1F64E}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_pouting_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'gesture', + 'medium skin tone', + 'pouting', + 'woman', + 'uc8', + 'diversity', + 'sad', + 'women', + 'stressed', + 'bitch', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'triste', + 'depression', + 'negative', + 'sadness', + 'woman', + 'female', + 'puta', + 'pute', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman pouting: medium-dark skin tone', + char: '\u{1F64E}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_pouting_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'gesture', + 'medium-dark skin tone', + 'pouting', + 'woman', + 'uc8', + 'diversity', + 'sad', + 'women', + 'stressed', + 'bitch', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'triste', + 'depression', + 'negative', + 'sadness', + 'woman', + 'female', + 'puta', + 'pute', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman pouting: dark skin tone', + char: '\u{1F64E}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_pouting_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'dark skin tone', + 'gesture', + 'pouting', + 'woman', + 'uc8', + 'diversity', + 'sad', + 'women', + 'stressed', + 'bitch', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'triste', + 'depression', + 'negative', + 'sadness', + 'woman', + 'female', + 'puta', + 'pute', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'man pouting', + char: '\u{1F64E}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_pouting', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'gesture', + 'man', + 'pouting', + 'uc6', + 'diversity', + 'sad', + 'men', + 'angry', + 'stressed', + 'hate', + 'daddy', + 'husband', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'triste', + 'depression', + 'negative', + 'sadness', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad', + 'i hate', + 'disgust', + 'stump', + 'shout', + 'dislike', + 'rude', + 'annoy', + 'grinch', + 'gross', + 'grumpy', + 'mean', + 'problem', + 'suck', + 'jerk', + 'asshole', + 'no', + 'dad', + 'papa', + 'pere', + 'father' + ]), + Emoji( + name: 'man pouting: light skin tone', + char: '\u{1F64E}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_pouting_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'gesture', + 'light skin tone', + 'man', + 'pouting', + 'uc8', + 'diversity', + 'sad', + 'men', + 'angry', + 'stressed', + 'hate', + 'daddy', + 'husband', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'triste', + 'depression', + 'negative', + 'sadness', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad', + 'i hate', + 'disgust', + 'stump', + 'shout', + 'dislike', + 'rude', + 'annoy', + 'grinch', + 'gross', + 'grumpy', + 'mean', + 'problem', + 'suck', + 'jerk', + 'asshole', + 'no', + 'dad', + 'papa', + 'pere', + 'father' + ], + modifiable: true), + Emoji( + name: 'man pouting: medium-light skin tone', + char: '\u{1F64E}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_pouting_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'gesture', + 'man', + 'medium-light skin tone', + 'pouting', + 'uc8', + 'diversity', + 'sad', + 'men', + 'angry', + 'stressed', + 'hate', + 'daddy', + 'husband', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'triste', + 'depression', + 'negative', + 'sadness', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad', + 'i hate', + 'disgust', + 'stump', + 'shout', + 'dislike', + 'rude', + 'annoy', + 'grinch', + 'gross', + 'grumpy', + 'mean', + 'problem', + 'suck', + 'jerk', + 'asshole', + 'no', + 'dad', + 'papa', + 'pere', + 'father' + ], + modifiable: true), + Emoji( + name: 'man pouting: medium skin tone', + char: '\u{1F64E}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_pouting_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'gesture', + 'man', + 'medium skin tone', + 'pouting', + 'uc8', + 'diversity', + 'sad', + 'men', + 'angry', + 'stressed', + 'hate', + 'daddy', + 'husband', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'triste', + 'depression', + 'negative', + 'sadness', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad', + 'i hate', + 'disgust', + 'stump', + 'shout', + 'dislike', + 'rude', + 'annoy', + 'grinch', + 'gross', + 'grumpy', + 'mean', + 'problem', + 'suck', + 'jerk', + 'asshole', + 'no', + 'dad', + 'papa', + 'pere', + 'father' + ], + modifiable: true), + Emoji( + name: 'man pouting: medium-dark skin tone', + char: '\u{1F64E}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_pouting_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'gesture', + 'man', + 'medium-dark skin tone', + 'pouting', + 'uc8', + 'diversity', + 'sad', + 'men', + 'angry', + 'stressed', + 'hate', + 'daddy', + 'husband', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'triste', + 'depression', + 'negative', + 'sadness', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad', + 'i hate', + 'disgust', + 'stump', + 'shout', + 'dislike', + 'rude', + 'annoy', + 'grinch', + 'gross', + 'grumpy', + 'mean', + 'problem', + 'suck', + 'jerk', + 'asshole', + 'no', + 'dad', + 'papa', + 'pere', + 'father' + ], + modifiable: true), + Emoji( + name: 'man pouting: dark skin tone', + char: '\u{1F64E}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_pouting_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'dark skin tone', + 'gesture', + 'man', + 'pouting', + 'uc8', + 'diversity', + 'sad', + 'men', + 'angry', + 'stressed', + 'hate', + 'daddy', + 'husband', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'triste', + 'depression', + 'negative', + 'sadness', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad', + 'i hate', + 'disgust', + 'stump', + 'shout', + 'dislike', + 'rude', + 'annoy', + 'grinch', + 'gross', + 'grumpy', + 'mean', + 'problem', + 'suck', + 'jerk', + 'asshole', + 'no', + 'dad', + 'papa', + 'pere', + 'father' + ], + modifiable: true), + Emoji( + name: 'person frowning', + char: '\u{1F64D}', + shortName: 'person_frowning', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'frown', + 'gesture', + 'uc6', + 'diversity', + 'sad', + 'men', + 'angry', + 'stressed', + 'hate', + 'bitch', + 'daddy', + 'husband', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'triste', + 'depression', + 'negative', + 'sadness', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad', + 'i hate', + 'disgust', + 'stump', + 'shout', + 'dislike', + 'rude', + 'annoy', + 'grinch', + 'gross', + 'grumpy', + 'mean', + 'problem', + 'suck', + 'jerk', + 'asshole', + 'no', + 'puta', + 'pute', + 'dad', + 'papa', + 'pere', + 'father', + 'maman', + 'mommy', + 'mama', + 'mother' + ]), + Emoji( + name: 'person frowning: light skin tone', + char: '\u{1F64D}\u{1F3FB}', + shortName: 'person_frowning_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'frown', + 'gesture', + 'light skin tone', + 'uc8', + 'diversity', + 'sad', + 'men', + 'angry', + 'stressed', + 'hate', + 'bitch', + 'daddy', + 'husband', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'triste', + 'depression', + 'negative', + 'sadness', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad', + 'i hate', + 'disgust', + 'stump', + 'shout', + 'dislike', + 'rude', + 'annoy', + 'grinch', + 'gross', + 'grumpy', + 'mean', + 'problem', + 'suck', + 'jerk', + 'asshole', + 'no', + 'puta', + 'pute', + 'dad', + 'papa', + 'pere', + 'father', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'person frowning: medium-light skin tone', + char: '\u{1F64D}\u{1F3FC}', + shortName: 'person_frowning_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'frown', + 'gesture', + 'medium-light skin tone', + 'uc8', + 'diversity', + 'sad', + 'men', + 'angry', + 'stressed', + 'hate', + 'bitch', + 'daddy', + 'husband', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'triste', + 'depression', + 'negative', + 'sadness', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad', + 'i hate', + 'disgust', + 'stump', + 'shout', + 'dislike', + 'rude', + 'annoy', + 'grinch', + 'gross', + 'grumpy', + 'mean', + 'problem', + 'suck', + 'jerk', + 'asshole', + 'no', + 'puta', + 'pute', + 'dad', + 'papa', + 'pere', + 'father', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'person frowning: medium skin tone', + char: '\u{1F64D}\u{1F3FD}', + shortName: 'person_frowning_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'frown', + 'gesture', + 'medium skin tone', + 'uc8', + 'diversity', + 'sad', + 'men', + 'angry', + 'stressed', + 'hate', + 'bitch', + 'daddy', + 'husband', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'triste', + 'depression', + 'negative', + 'sadness', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad', + 'i hate', + 'disgust', + 'stump', + 'shout', + 'dislike', + 'rude', + 'annoy', + 'grinch', + 'gross', + 'grumpy', + 'mean', + 'problem', + 'suck', + 'jerk', + 'asshole', + 'no', + 'puta', + 'pute', + 'dad', + 'papa', + 'pere', + 'father', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'person frowning: medium-dark skin tone', + char: '\u{1F64D}\u{1F3FE}', + shortName: 'person_frowning_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'frown', + 'gesture', + 'medium-dark skin tone', + 'uc8', + 'diversity', + 'sad', + 'men', + 'angry', + 'stressed', + 'hate', + 'bitch', + 'daddy', + 'husband', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'triste', + 'depression', + 'negative', + 'sadness', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad', + 'i hate', + 'disgust', + 'stump', + 'shout', + 'dislike', + 'rude', + 'annoy', + 'grinch', + 'gross', + 'grumpy', + 'mean', + 'problem', + 'suck', + 'jerk', + 'asshole', + 'no', + 'puta', + 'pute', + 'dad', + 'papa', + 'pere', + 'father', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'person frowning: dark skin tone', + char: '\u{1F64D}\u{1F3FF}', + shortName: 'person_frowning_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'dark skin tone', + 'frown', + 'gesture', + 'uc8', + 'diversity', + 'sad', + 'men', + 'angry', + 'stressed', + 'hate', + 'bitch', + 'daddy', + 'husband', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'triste', + 'depression', + 'negative', + 'sadness', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad', + 'i hate', + 'disgust', + 'stump', + 'shout', + 'dislike', + 'rude', + 'annoy', + 'grinch', + 'gross', + 'grumpy', + 'mean', + 'problem', + 'suck', + 'jerk', + 'asshole', + 'no', + 'puta', + 'pute', + 'dad', + 'papa', + 'pere', + 'father', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman frowning', + char: '\u{1F64D}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_frowning', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'frowning', + 'gesture', + 'woman', + 'uc6', + 'diversity', + 'sad', + 'women', + 'stressed', + 'bitch', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'triste', + 'depression', + 'negative', + 'sadness', + 'woman', + 'female', + 'puta', + 'pute', + 'maman', + 'mommy', + 'mama', + 'mother' + ]), + Emoji( + name: 'woman frowning: light skin tone', + char: '\u{1F64D}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_frowning_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'frowning', + 'gesture', + 'light skin tone', + 'woman', + 'uc8', + 'diversity', + 'sad', + 'women', + 'stressed', + 'bitch', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'triste', + 'depression', + 'negative', + 'sadness', + 'woman', + 'female', + 'puta', + 'pute', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman frowning: medium-light skin tone', + char: '\u{1F64D}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_frowning_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'frowning', + 'gesture', + 'medium-light skin tone', + 'woman', + 'uc8', + 'diversity', + 'sad', + 'women', + 'stressed', + 'bitch', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'triste', + 'depression', + 'negative', + 'sadness', + 'woman', + 'female', + 'puta', + 'pute', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman frowning: medium skin tone', + char: '\u{1F64D}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_frowning_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'frowning', + 'gesture', + 'medium skin tone', + 'woman', + 'uc8', + 'diversity', + 'sad', + 'women', + 'stressed', + 'bitch', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'triste', + 'depression', + 'negative', + 'sadness', + 'woman', + 'female', + 'puta', + 'pute', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman frowning: medium-dark skin tone', + char: '\u{1F64D}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_frowning_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'frowning', + 'gesture', + 'medium-dark skin tone', + 'woman', + 'uc8', + 'diversity', + 'sad', + 'women', + 'stressed', + 'bitch', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'triste', + 'depression', + 'negative', + 'sadness', + 'woman', + 'female', + 'puta', + 'pute', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman frowning: dark skin tone', + char: '\u{1F64D}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_frowning_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'dark skin tone', + 'frowning', + 'gesture', + 'woman', + 'uc8', + 'diversity', + 'sad', + 'women', + 'stressed', + 'bitch', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'triste', + 'depression', + 'negative', + 'sadness', + 'woman', + 'female', + 'puta', + 'pute', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'man frowning', + char: '\u{1F64D}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_frowning', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'frowning', + 'gesture', + 'man', + 'uc6', + 'diversity', + 'sad', + 'men', + 'angry', + 'stressed', + 'hate', + 'daddy', + 'husband', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'triste', + 'depression', + 'negative', + 'sadness', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad', + 'i hate', + 'disgust', + 'stump', + 'shout', + 'dislike', + 'rude', + 'annoy', + 'grinch', + 'gross', + 'grumpy', + 'mean', + 'problem', + 'suck', + 'jerk', + 'asshole', + 'no', + 'dad', + 'papa', + 'pere', + 'father' + ]), + Emoji( + name: 'man frowning: light skin tone', + char: '\u{1F64D}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_frowning_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'frowning', + 'gesture', + 'light skin tone', + 'man', + 'uc8', + 'diversity', + 'sad', + 'men', + 'angry', + 'stressed', + 'hate', + 'daddy', + 'husband', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'triste', + 'depression', + 'negative', + 'sadness', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad', + 'i hate', + 'disgust', + 'stump', + 'shout', + 'dislike', + 'rude', + 'annoy', + 'grinch', + 'gross', + 'grumpy', + 'mean', + 'problem', + 'suck', + 'jerk', + 'asshole', + 'no', + 'dad', + 'papa', + 'pere', + 'father' + ], + modifiable: true), + Emoji( + name: 'man frowning: medium-light skin tone', + char: '\u{1F64D}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_frowning_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'frowning', + 'gesture', + 'man', + 'medium-light skin tone', + 'uc8', + 'diversity', + 'sad', + 'men', + 'angry', + 'stressed', + 'hate', + 'daddy', + 'husband', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'triste', + 'depression', + 'negative', + 'sadness', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad', + 'i hate', + 'disgust', + 'stump', + 'shout', + 'dislike', + 'rude', + 'annoy', + 'grinch', + 'gross', + 'grumpy', + 'mean', + 'problem', + 'suck', + 'jerk', + 'asshole', + 'no', + 'dad', + 'papa', + 'pere', + 'father' + ], + modifiable: true), + Emoji( + name: 'man frowning: medium skin tone', + char: '\u{1F64D}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_frowning_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'frowning', + 'gesture', + 'man', + 'medium skin tone', + 'uc8', + 'diversity', + 'sad', + 'men', + 'angry', + 'stressed', + 'hate', + 'daddy', + 'husband', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'triste', + 'depression', + 'negative', + 'sadness', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad', + 'i hate', + 'disgust', + 'stump', + 'shout', + 'dislike', + 'rude', + 'annoy', + 'grinch', + 'gross', + 'grumpy', + 'mean', + 'problem', + 'suck', + 'jerk', + 'asshole', + 'no', + 'dad', + 'papa', + 'pere', + 'father' + ], + modifiable: true), + Emoji( + name: 'man frowning: medium-dark skin tone', + char: '\u{1F64D}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_frowning_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'frowning', + 'gesture', + 'man', + 'medium-dark skin tone', + 'uc8', + 'diversity', + 'sad', + 'men', + 'angry', + 'stressed', + 'hate', + 'daddy', + 'husband', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'triste', + 'depression', + 'negative', + 'sadness', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad', + 'i hate', + 'disgust', + 'stump', + 'shout', + 'dislike', + 'rude', + 'annoy', + 'grinch', + 'gross', + 'grumpy', + 'mean', + 'problem', + 'suck', + 'jerk', + 'asshole', + 'no', + 'dad', + 'papa', + 'pere', + 'father' + ], + modifiable: true), + Emoji( + name: 'man frowning: dark skin tone', + char: '\u{1F64D}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_frowning_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personGesture, + keywords: [ + 'dark skin tone', + 'frowning', + 'gesture', + 'man', + 'uc8', + 'diversity', + 'sad', + 'men', + 'angry', + 'stressed', + 'hate', + 'daddy', + 'husband', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'triste', + 'depression', + 'negative', + 'sadness', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad', + 'i hate', + 'disgust', + 'stump', + 'shout', + 'dislike', + 'rude', + 'annoy', + 'grinch', + 'gross', + 'grumpy', + 'mean', + 'problem', + 'suck', + 'jerk', + 'asshole', + 'no', + 'dad', + 'papa', + 'pere', + 'father' + ], + modifiable: true), + Emoji( + name: 'person getting haircut', + char: '\u{1F487}', + shortName: 'person_getting_haircut', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'barber', + 'beauty', + 'haircut', + 'parlor', + 'uc6', + 'diversity', + 'men', + 'daddy', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'dad', + 'papa', + 'pere', + 'father', + 'maman', + 'mommy', + 'mama', + 'mother' + ]), + Emoji( + name: 'person getting haircut: light skin tone', + char: '\u{1F487}\u{1F3FB}', + shortName: 'person_getting_haircut_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'barber', + 'beauty', + 'haircut', + 'light skin tone', + 'parlor', + 'uc8', + 'diversity', + 'men', + 'daddy', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'dad', + 'papa', + 'pere', + 'father', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'person getting haircut: medium-light skin tone', + char: '\u{1F487}\u{1F3FC}', + shortName: 'person_getting_haircut_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'barber', + 'beauty', + 'haircut', + 'medium-light skin tone', + 'parlor', + 'uc8', + 'diversity', + 'men', + 'daddy', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'dad', + 'papa', + 'pere', + 'father', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'person getting haircut: medium skin tone', + char: '\u{1F487}\u{1F3FD}', + shortName: 'person_getting_haircut_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'barber', + 'beauty', + 'haircut', + 'medium skin tone', + 'parlor', + 'uc8', + 'diversity', + 'men', + 'daddy', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'dad', + 'papa', + 'pere', + 'father', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'person getting haircut: medium-dark skin tone', + char: '\u{1F487}\u{1F3FE}', + shortName: 'person_getting_haircut_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'barber', + 'beauty', + 'haircut', + 'medium-dark skin tone', + 'parlor', + 'uc8', + 'diversity', + 'men', + 'daddy', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'dad', + 'papa', + 'pere', + 'father', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'person getting haircut: dark skin tone', + char: '\u{1F487}\u{1F3FF}', + shortName: 'person_getting_haircut_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'barber', + 'beauty', + 'dark skin tone', + 'haircut', + 'parlor', + 'uc8', + 'diversity', + 'men', + 'daddy', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'dad', + 'papa', + 'pere', + 'father', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman getting haircut', + char: '\u{1F487}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_getting_haircut', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'haircut', + 'woman', + 'uc6', + 'diversity', + 'women', + 'girls night', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'ladies night', + 'girls only', + 'girlfriend', + 'maman', + 'mommy', + 'mama', + 'mother' + ]), + Emoji( + name: 'woman getting haircut: light skin tone', + char: '\u{1F487}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_getting_haircut_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'haircut', + 'light skin tone', + 'woman', + 'uc8', + 'diversity', + 'women', + 'girls night', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'ladies night', + 'girls only', + 'girlfriend', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman getting haircut: medium-light skin tone', + char: '\u{1F487}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_getting_haircut_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'haircut', + 'medium-light skin tone', + 'woman', + 'uc8', + 'diversity', + 'women', + 'girls night', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'ladies night', + 'girls only', + 'girlfriend', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman getting haircut: medium skin tone', + char: '\u{1F487}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_getting_haircut_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'haircut', + 'medium skin tone', + 'woman', + 'uc8', + 'diversity', + 'women', + 'girls night', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'ladies night', + 'girls only', + 'girlfriend', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman getting haircut: medium-dark skin tone', + char: '\u{1F487}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_getting_haircut_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'haircut', + 'medium-dark skin tone', + 'woman', + 'uc8', + 'diversity', + 'women', + 'girls night', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'ladies night', + 'girls only', + 'girlfriend', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman getting haircut: dark skin tone', + char: '\u{1F487}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_getting_haircut_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'dark skin tone', + 'haircut', + 'woman', + 'uc8', + 'diversity', + 'women', + 'girls night', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'ladies night', + 'girls only', + 'girlfriend', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'man getting haircut', + char: '\u{1F487}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_getting_haircut', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'haircut', + 'man', + 'uc6', + 'diversity', + 'men', + 'daddy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'dad', + 'papa', + 'pere', + 'father' + ]), + Emoji( + name: 'man getting haircut: light skin tone', + char: '\u{1F487}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_getting_haircut_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'haircut', + 'light skin tone', + 'man', + 'uc8', + 'diversity', + 'men', + 'daddy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'dad', + 'papa', + 'pere', + 'father' + ], + modifiable: true), + Emoji( + name: 'man getting haircut: medium-light skin tone', + char: '\u{1F487}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_getting_haircut_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'haircut', + 'man', + 'medium-light skin tone', + 'uc8', + 'diversity', + 'men', + 'daddy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'dad', + 'papa', + 'pere', + 'father' + ], + modifiable: true), + Emoji( + name: 'man getting haircut: medium skin tone', + char: '\u{1F487}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_getting_haircut_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'haircut', + 'man', + 'medium skin tone', + 'uc8', + 'diversity', + 'men', + 'daddy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'dad', + 'papa', + 'pere', + 'father' + ], + modifiable: true), + Emoji( + name: 'man getting haircut: medium-dark skin tone', + char: '\u{1F487}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_getting_haircut_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'haircut', + 'man', + 'medium-dark skin tone', + 'uc8', + 'diversity', + 'men', + 'daddy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'dad', + 'papa', + 'pere', + 'father' + ], + modifiable: true), + Emoji( + name: 'man getting haircut: dark skin tone', + char: '\u{1F487}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_getting_haircut_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'dark skin tone', + 'haircut', + 'man', + 'uc8', + 'diversity', + 'men', + 'daddy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'dad', + 'papa', + 'pere', + 'father' + ], + modifiable: true), + Emoji( + name: 'person getting massage', + char: '\u{1F486}', + shortName: 'person_getting_massage', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'face', + 'massage', + 'salon', + 'uc6', + 'diversity', + 'men', + 'girls night', + 'pleased', + 'yoga', + 'calm', + 'spa', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'ladies night', + 'girls only', + 'girlfriend', + 'please', + 'chill', + 'confident', + 'content', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'relax', + 'sauna', + 'maman', + 'mommy', + 'mama', + 'mother' + ]), + Emoji( + name: 'person getting massage: light skin tone', + char: '\u{1F486}\u{1F3FB}', + shortName: 'person_getting_massage_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'face', + 'light skin tone', + 'massage', + 'salon', + 'uc8', + 'diversity', + 'men', + 'girls night', + 'pleased', + 'yoga', + 'calm', + 'spa', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'ladies night', + 'girls only', + 'girlfriend', + 'please', + 'chill', + 'confident', + 'content', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'relax', + 'sauna', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'person getting massage: medium-light skin tone', + char: '\u{1F486}\u{1F3FC}', + shortName: 'person_getting_massage_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'face', + 'massage', + 'medium-light skin tone', + 'salon', + 'uc8', + 'diversity', + 'men', + 'girls night', + 'pleased', + 'yoga', + 'calm', + 'spa', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'ladies night', + 'girls only', + 'girlfriend', + 'please', + 'chill', + 'confident', + 'content', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'relax', + 'sauna', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'person getting massage: medium skin tone', + char: '\u{1F486}\u{1F3FD}', + shortName: 'person_getting_massage_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'face', + 'massage', + 'medium skin tone', + 'salon', + 'uc8', + 'diversity', + 'men', + 'girls night', + 'pleased', + 'yoga', + 'calm', + 'spa', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'ladies night', + 'girls only', + 'girlfriend', + 'please', + 'chill', + 'confident', + 'content', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'relax', + 'sauna', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'person getting massage: medium-dark skin tone', + char: '\u{1F486}\u{1F3FE}', + shortName: 'person_getting_massage_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'face', + 'massage', + 'medium-dark skin tone', + 'salon', + 'uc8', + 'diversity', + 'men', + 'girls night', + 'pleased', + 'yoga', + 'calm', + 'spa', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'ladies night', + 'girls only', + 'girlfriend', + 'please', + 'chill', + 'confident', + 'content', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'relax', + 'sauna', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'person getting massage: dark skin tone', + char: '\u{1F486}\u{1F3FF}', + shortName: 'person_getting_massage_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'dark skin tone', + 'face', + 'massage', + 'salon', + 'uc8', + 'diversity', + 'men', + 'girls night', + 'pleased', + 'yoga', + 'calm', + 'spa', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'ladies night', + 'girls only', + 'girlfriend', + 'please', + 'chill', + 'confident', + 'content', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'relax', + 'sauna', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman getting massage', + char: '\u{1F486}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_getting_face_massage', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'face', + 'massage', + 'woman', + 'uc6', + 'diversity', + 'women', + 'girls night', + 'yoga', + 'calm', + 'spa', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'ladies night', + 'girls only', + 'girlfriend', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'relax', + 'sauna', + 'maman', + 'mommy', + 'mama', + 'mother' + ]), + Emoji( + name: 'woman getting massage: light skin tone', + char: '\u{1F486}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_getting_face_massage_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'face', + 'light skin tone', + 'massage', + 'woman', + 'uc8', + 'diversity', + 'women', + 'girls night', + 'yoga', + 'calm', + 'spa', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'ladies night', + 'girls only', + 'girlfriend', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'relax', + 'sauna', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman getting massage: medium-light skin tone', + char: '\u{1F486}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_getting_face_massage_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'face', + 'massage', + 'medium-light skin tone', + 'woman', + 'uc8', + 'diversity', + 'women', + 'girls night', + 'yoga', + 'calm', + 'spa', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'ladies night', + 'girls only', + 'girlfriend', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'relax', + 'sauna', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman getting massage: medium skin tone', + char: '\u{1F486}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_getting_face_massage_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'face', + 'massage', + 'medium skin tone', + 'woman', + 'uc8', + 'diversity', + 'women', + 'girls night', + 'yoga', + 'calm', + 'spa', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'ladies night', + 'girls only', + 'girlfriend', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'relax', + 'sauna', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman getting massage: medium-dark skin tone', + char: '\u{1F486}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_getting_face_massage_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'face', + 'massage', + 'medium-dark skin tone', + 'woman', + 'uc8', + 'diversity', + 'women', + 'girls night', + 'yoga', + 'calm', + 'spa', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'ladies night', + 'girls only', + 'girlfriend', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'relax', + 'sauna', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman getting massage: dark skin tone', + char: '\u{1F486}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_getting_face_massage_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'dark skin tone', + 'face', + 'massage', + 'woman', + 'uc8', + 'diversity', + 'women', + 'girls night', + 'yoga', + 'calm', + 'spa', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'ladies night', + 'girls only', + 'girlfriend', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'relax', + 'sauna', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'man getting massage', + char: '\u{1F486}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_getting_face_massage', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'face', + 'man', + 'massage', + 'uc6', + 'diversity', + 'men', + 'yoga', + 'calm', + 'spa', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'relax', + 'sauna' + ]), + Emoji( + name: 'man getting massage: light skin tone', + char: '\u{1F486}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_getting_face_massage_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'face', + 'light skin tone', + 'man', + 'massage', + 'uc8', + 'diversity', + 'men', + 'yoga', + 'calm', + 'spa', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'relax', + 'sauna' + ], + modifiable: true), + Emoji( + name: 'man getting massage: medium-light skin tone', + char: '\u{1F486}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_getting_face_massage_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'face', + 'man', + 'massage', + 'medium-light skin tone', + 'uc8', + 'diversity', + 'men', + 'yoga', + 'calm', + 'spa', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'relax', + 'sauna' + ], + modifiable: true), + Emoji( + name: 'man getting massage: medium skin tone', + char: '\u{1F486}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_getting_face_massage_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'face', + 'man', + 'massage', + 'medium skin tone', + 'uc8', + 'diversity', + 'men', + 'yoga', + 'calm', + 'spa', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'relax', + 'sauna' + ], + modifiable: true), + Emoji( + name: 'man getting massage: medium-dark skin tone', + char: '\u{1F486}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_getting_face_massage_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'face', + 'man', + 'massage', + 'medium-dark skin tone', + 'uc8', + 'diversity', + 'men', + 'yoga', + 'calm', + 'spa', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'relax', + 'sauna' + ], + modifiable: true), + Emoji( + name: 'man getting massage: dark skin tone', + char: '\u{1F486}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_getting_face_massage_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'dark skin tone', + 'face', + 'man', + 'massage', + 'uc8', + 'diversity', + 'men', + 'yoga', + 'calm', + 'spa', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'relax', + 'sauna' + ], + modifiable: true), + Emoji( + name: 'person in steamy room', + char: '\u{1F9D6}', + shortName: 'person_in_steamy_room', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc10', + 'diversity', + 'hot', + 'steam', + 'girls night', + 'spa', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'heat', + 'warm', + 'caliente', + 'chaud', + 'heiß', + 'steaming', + 'piping', + 'ladies night', + 'girls only', + 'girlfriend', + 'relax', + 'sauna', + 'maman', + 'mommy', + 'mama', + 'mother' + ]), + Emoji( + name: 'person in steamy room: light skin tone', + char: '\u{1F9D6}\u{1F3FB}', + shortName: 'person_in_steamy_room_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'light skin tone', + 'sauna', + 'steam room', + 'uc10', + 'diversity', + 'hot', + 'steam', + 'girls night', + 'spa', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'heat', + 'warm', + 'caliente', + 'chaud', + 'heiß', + 'steaming', + 'piping', + 'ladies night', + 'girls only', + 'girlfriend', + 'relax', + 'sauna', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'person in steamy room: medium-light skin tone', + char: '\u{1F9D6}\u{1F3FC}', + shortName: 'person_in_steamy_room_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'medium-light skin tone', + 'sauna', + 'steam room', + 'uc10', + 'diversity', + 'hot', + 'steam', + 'girls night', + 'spa', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'heat', + 'warm', + 'caliente', + 'chaud', + 'heiß', + 'steaming', + 'piping', + 'ladies night', + 'girls only', + 'girlfriend', + 'relax', + 'sauna', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'person in steamy room: medium skin tone', + char: '\u{1F9D6}\u{1F3FD}', + shortName: 'person_in_steamy_room_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'medium skin tone', + 'sauna', + 'steam room', + 'uc10', + 'diversity', + 'hot', + 'steam', + 'girls night', + 'spa', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'heat', + 'warm', + 'caliente', + 'chaud', + 'heiß', + 'steaming', + 'piping', + 'ladies night', + 'girls only', + 'girlfriend', + 'relax', + 'sauna', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'person in steamy room: medium-dark skin tone', + char: '\u{1F9D6}\u{1F3FE}', + shortName: 'person_in_steamy_room_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'medium-dark skin tone', + 'sauna', + 'steam room', + 'uc10', + 'diversity', + 'hot', + 'steam', + 'girls night', + 'spa', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'heat', + 'warm', + 'caliente', + 'chaud', + 'heiß', + 'steaming', + 'piping', + 'ladies night', + 'girls only', + 'girlfriend', + 'relax', + 'sauna', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'person in steamy room: dark skin tone', + char: '\u{1F9D6}\u{1F3FF}', + shortName: 'person_in_steamy_room_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'dark skin tone', + 'sauna', + 'steam room', + 'uc10', + 'diversity', + 'hot', + 'steam', + 'girls night', + 'spa', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'heat', + 'warm', + 'caliente', + 'chaud', + 'heiß', + 'steaming', + 'piping', + 'ladies night', + 'girls only', + 'girlfriend', + 'relax', + 'sauna', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman in steamy room', + char: '\u{1F9D6}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_in_steamy_room', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'sauna', + 'steam room', + 'uc10', + 'diversity', + 'women', + 'hot', + 'steam', + 'girls night', + 'spa', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'heat', + 'warm', + 'caliente', + 'chaud', + 'heiß', + 'steaming', + 'piping', + 'ladies night', + 'girls only', + 'girlfriend', + 'relax', + 'sauna', + 'maman', + 'mommy', + 'mama', + 'mother' + ]), + Emoji( + name: 'woman in steamy room: light skin tone', + char: '\u{1F9D6}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_in_steamy_room_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'light skin tone', + 'sauna', + 'steam room', + 'uc10', + 'diversity', + 'women', + 'hot', + 'steam', + 'girls night', + 'spa', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'heat', + 'warm', + 'caliente', + 'chaud', + 'heiß', + 'steaming', + 'piping', + 'ladies night', + 'girls only', + 'girlfriend', + 'relax', + 'sauna', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman in steamy room: medium-light skin tone', + char: '\u{1F9D6}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_in_steamy_room_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'medium-light skin tone', + 'sauna', + 'steam room', + 'uc10', + 'diversity', + 'women', + 'hot', + 'steam', + 'girls night', + 'spa', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'heat', + 'warm', + 'caliente', + 'chaud', + 'heiß', + 'steaming', + 'piping', + 'ladies night', + 'girls only', + 'girlfriend', + 'relax', + 'sauna', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman in steamy room: medium skin tone', + char: '\u{1F9D6}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_in_steamy_room_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'medium skin tone', + 'sauna', + 'steam room', + 'uc10', + 'diversity', + 'women', + 'hot', + 'steam', + 'girls night', + 'spa', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'heat', + 'warm', + 'caliente', + 'chaud', + 'heiß', + 'steaming', + 'piping', + 'ladies night', + 'girls only', + 'girlfriend', + 'relax', + 'sauna', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman in steamy room: medium-dark skin tone', + char: '\u{1F9D6}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_in_steamy_room_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'medium-dark skin tone', + 'sauna', + 'steam room', + 'uc10', + 'diversity', + 'women', + 'hot', + 'steam', + 'girls night', + 'spa', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'heat', + 'warm', + 'caliente', + 'chaud', + 'heiß', + 'steaming', + 'piping', + 'ladies night', + 'girls only', + 'girlfriend', + 'relax', + 'sauna', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman in steamy room: dark skin tone', + char: '\u{1F9D6}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_in_steamy_room_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'dark skin tone', + 'sauna', + 'steam room', + 'uc10', + 'diversity', + 'women', + 'hot', + 'steam', + 'girls night', + 'spa', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'heat', + 'warm', + 'caliente', + 'chaud', + 'heiß', + 'steaming', + 'piping', + 'ladies night', + 'girls only', + 'girlfriend', + 'relax', + 'sauna', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'man in steamy room', + char: '\u{1F9D6}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_in_steamy_room', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'sauna', + 'steam room', + 'uc10', + 'diversity', + 'hot', + 'steam', + 'spa', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'heat', + 'warm', + 'caliente', + 'chaud', + 'heiß', + 'steaming', + 'piping', + 'relax', + 'sauna' + ]), + Emoji( + name: 'man in steamy room: light skin tone', + char: '\u{1F9D6}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_in_steamy_room_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'light skin tone', + 'sauna', + 'steam room', + 'uc10', + 'diversity', + 'hot', + 'steam', + 'spa', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'heat', + 'warm', + 'caliente', + 'chaud', + 'heiß', + 'steaming', + 'piping', + 'relax', + 'sauna' + ], + modifiable: true), + Emoji( + name: 'man in steamy room: medium-light skin tone', + char: '\u{1F9D6}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_in_steamy_room_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'medium-light skin tone', + 'sauna', + 'steam room', + 'uc10', + 'diversity', + 'hot', + 'steam', + 'spa', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'heat', + 'warm', + 'caliente', + 'chaud', + 'heiß', + 'steaming', + 'piping', + 'relax', + 'sauna' + ], + modifiable: true), + Emoji( + name: 'man in steamy room: medium skin tone', + char: '\u{1F9D6}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_in_steamy_room_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'medium skin tone', + 'sauna', + 'steam room', + 'uc10', + 'diversity', + 'hot', + 'steam', + 'spa', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'heat', + 'warm', + 'caliente', + 'chaud', + 'heiß', + 'steaming', + 'piping', + 'relax', + 'sauna' + ], + modifiable: true), + Emoji( + name: 'man in steamy room: medium-dark skin tone', + char: '\u{1F9D6}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_in_steamy_room_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'medium-dark skin tone', + 'sauna', + 'steam room', + 'uc10', + 'diversity', + 'hot', + 'steam', + 'spa', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'heat', + 'warm', + 'caliente', + 'chaud', + 'heiß', + 'steaming', + 'piping', + 'relax', + 'sauna' + ], + modifiable: true), + Emoji( + name: 'man in steamy room: dark skin tone', + char: '\u{1F9D6}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_in_steamy_room_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'dark skin tone', + 'sauna', + 'steam room', + 'uc10', + 'diversity', + 'hot', + 'steam', + 'spa', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'heat', + 'warm', + 'caliente', + 'chaud', + 'heiß', + 'steaming', + 'piping', + 'relax', + 'sauna' + ], + modifiable: true), + Emoji( + name: 'nail polish', + char: '\u{1F485}', + shortName: 'nail_care', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handProp, + keywords: [ + 'care', + 'cosmetics', + 'manicure', + 'nail', + 'polish', + 'uc6', + 'diversity', + 'women', + 'body', + 'hands', + 'nailpolish', + 'beautiful', + 'girls night', + 'painting', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'nails', + 'fingernails', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'ladies night', + 'girls only', + 'girlfriend', + 'painter', + 'arts', + 'maman', + 'mommy', + 'mama', + 'mother' + ]), + Emoji( + name: 'nail polish: light skin tone', + char: '\u{1F485}\u{1F3FB}', + shortName: 'nail_care_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handProp, + keywords: [ + 'care', + 'cosmetics', + 'light skin tone', + 'manicure', + 'nail', + 'polish', + 'uc8', + 'diversity', + 'women', + 'body', + 'hands', + 'nailpolish', + 'beautiful', + 'girls night', + 'painting', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'nails', + 'fingernails', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'ladies night', + 'girls only', + 'girlfriend', + 'painter', + 'arts', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'nail polish: medium-light skin tone', + char: '\u{1F485}\u{1F3FC}', + shortName: 'nail_care_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handProp, + keywords: [ + 'care', + 'cosmetics', + 'manicure', + 'medium-light skin tone', + 'nail', + 'polish', + 'uc8', + 'diversity', + 'women', + 'body', + 'hands', + 'nailpolish', + 'beautiful', + 'girls night', + 'painting', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'nails', + 'fingernails', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'ladies night', + 'girls only', + 'girlfriend', + 'painter', + 'arts', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'nail polish: medium skin tone', + char: '\u{1F485}\u{1F3FD}', + shortName: 'nail_care_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handProp, + keywords: [ + 'care', + 'cosmetics', + 'manicure', + 'medium skin tone', + 'nail', + 'polish', + 'uc8', + 'diversity', + 'women', + 'body', + 'hands', + 'nailpolish', + 'beautiful', + 'girls night', + 'painting', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'nails', + 'fingernails', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'ladies night', + 'girls only', + 'girlfriend', + 'painter', + 'arts', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'nail polish: medium-dark skin tone', + char: '\u{1F485}\u{1F3FE}', + shortName: 'nail_care_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handProp, + keywords: [ + 'care', + 'cosmetics', + 'manicure', + 'medium-dark skin tone', + 'nail', + 'polish', + 'uc8', + 'diversity', + 'women', + 'body', + 'hands', + 'nailpolish', + 'beautiful', + 'girls night', + 'painting', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'nails', + 'fingernails', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'ladies night', + 'girls only', + 'girlfriend', + 'painter', + 'arts', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'nail polish: dark skin tone', + char: '\u{1F485}\u{1F3FF}', + shortName: 'nail_care_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handProp, + keywords: [ + 'care', + 'cosmetics', + 'dark skin tone', + 'manicure', + 'nail', + 'polish', + 'uc8', + 'diversity', + 'women', + 'body', + 'hands', + 'nailpolish', + 'beautiful', + 'girls night', + 'painting', + 'mom', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'body part', + 'anatomy', + 'hand', + 'finger', + 'fingers', + 'nails', + 'fingernails', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'ladies night', + 'girls only', + 'girlfriend', + 'painter', + 'arts', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'selfie', + char: '\u{1F933}', + shortName: 'selfie', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handProp, + keywords: [ + 'camera', + 'phone', + 'selfie', + 'uc9', + 'diversity', + 'selfie', + 'fame', + 'instagram', + 'fun', + 'youtube', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'famous', + 'celebrity', + 'vlog' + ]), + Emoji( + name: 'selfie: light skin tone', + char: '\u{1F933}\u{1F3FB}', + shortName: 'selfie_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handProp, + keywords: [ + 'camera', + 'light skin tone', + 'phone', + 'selfie', + 'uc9', + 'diversity', + 'selfie', + 'fame', + 'instagram', + 'fun', + 'youtube', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'famous', + 'celebrity', + 'vlog' + ], + modifiable: true), + Emoji( + name: 'selfie: medium-light skin tone', + char: '\u{1F933}\u{1F3FC}', + shortName: 'selfie_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handProp, + keywords: [ + 'camera', + 'medium-light skin tone', + 'phone', + 'selfie', + 'uc9', + 'diversity', + 'selfie', + 'fame', + 'instagram', + 'fun', + 'youtube', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'famous', + 'celebrity', + 'vlog' + ], + modifiable: true), + Emoji( + name: 'selfie: medium skin tone', + char: '\u{1F933}\u{1F3FD}', + shortName: 'selfie_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handProp, + keywords: [ + 'camera', + 'medium skin tone', + 'phone', + 'selfie', + 'uc9', + 'diversity', + 'selfie', + 'fame', + 'instagram', + 'fun', + 'youtube', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'famous', + 'celebrity', + 'vlog' + ], + modifiable: true), + Emoji( + name: 'selfie: medium-dark skin tone', + char: '\u{1F933}\u{1F3FE}', + shortName: 'selfie_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handProp, + keywords: [ + 'camera', + 'medium-dark skin tone', + 'phone', + 'selfie', + 'uc9', + 'diversity', + 'selfie', + 'fame', + 'instagram', + 'fun', + 'youtube', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'famous', + 'celebrity', + 'vlog' + ], + modifiable: true), + Emoji( + name: 'selfie: dark skin tone', + char: '\u{1F933}\u{1F3FF}', + shortName: 'selfie_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.handProp, + keywords: [ + 'camera', + 'dark skin tone', + 'phone', + 'selfie', + 'uc9', + 'diversity', + 'selfie', + 'fame', + 'instagram', + 'fun', + 'youtube', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'famous', + 'celebrity', + 'vlog' + ], + modifiable: true), + Emoji( + name: 'woman dancing', + char: '\u{1F483}', + shortName: 'dancer', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'dancing', + 'woman', + 'uc6', + 'instruments', + 'diversity', + 'women', + 'mexican', + 'sexy', + 'circus', + 'beautiful', + 'girls night', + 'dance', + 'hawaii', + 'celebrate', + 'disco', + 'las vegas', + 'fun', + 'activity', + 'dress', + 'music', + 'instrument', + 'singing', + 'concert', + 'jaz', + 'listen', + 'singer', + 'song', + 'musique', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'mexico', + 'cinco de mayo', + 'español', + 'circus tent', + 'clown', + 'clowns', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'ladies night', + 'girls only', + 'girlfriend', + 'dancers', + 'dancing', + 'ballet', + 'ballerina', + 'dabbing', + 'salsa', + 'aloha', + 'kawaii', + 'maui', + 'moana', + 'celebration', + 'event', + 'celebrating', + 'festa', + 'parties', + 'events', + 'new years', + 'new year', + 'fiesta', + 'fete', + 'newyear', + 'party', + 'festive', + 'festival', + 'yolo', + 'festejar', + 'vegas' + ]), + Emoji( + name: 'woman dancing: light skin tone', + char: '\u{1F483}\u{1F3FB}', + shortName: 'dancer_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'dancing', + 'light skin tone', + 'woman', + 'uc8', + 'instruments', + 'diversity', + 'women', + 'mexican', + 'sexy', + 'circus', + 'beautiful', + 'girls night', + 'dance', + 'hawaii', + 'celebrate', + 'disco', + 'las vegas', + 'fun', + 'activity', + 'dress', + 'music', + 'instrument', + 'singing', + 'concert', + 'jaz', + 'listen', + 'singer', + 'song', + 'musique', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'mexico', + 'cinco de mayo', + 'español', + 'circus tent', + 'clown', + 'clowns', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'ladies night', + 'girls only', + 'girlfriend', + 'dancers', + 'dancing', + 'ballet', + 'ballerina', + 'dabbing', + 'salsa', + 'aloha', + 'kawaii', + 'maui', + 'moana', + 'celebration', + 'event', + 'celebrating', + 'festa', + 'parties', + 'events', + 'new years', + 'new year', + 'fiesta', + 'fete', + 'newyear', + 'party', + 'festive', + 'festival', + 'yolo', + 'festejar', + 'vegas' + ], + modifiable: true), + Emoji( + name: 'woman dancing: medium-light skin tone', + char: '\u{1F483}\u{1F3FC}', + shortName: 'dancer_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'dancing', + 'medium-light skin tone', + 'woman', + 'uc8', + 'instruments', + 'diversity', + 'women', + 'mexican', + 'sexy', + 'circus', + 'beautiful', + 'girls night', + 'dance', + 'hawaii', + 'celebrate', + 'disco', + 'las vegas', + 'fun', + 'activity', + 'dress', + 'music', + 'instrument', + 'singing', + 'concert', + 'jaz', + 'listen', + 'singer', + 'song', + 'musique', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'mexico', + 'cinco de mayo', + 'español', + 'circus tent', + 'clown', + 'clowns', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'ladies night', + 'girls only', + 'girlfriend', + 'dancers', + 'dancing', + 'ballet', + 'ballerina', + 'dabbing', + 'salsa', + 'aloha', + 'kawaii', + 'maui', + 'moana', + 'celebration', + 'event', + 'celebrating', + 'festa', + 'parties', + 'events', + 'new years', + 'new year', + 'fiesta', + 'fete', + 'newyear', + 'party', + 'festive', + 'festival', + 'yolo', + 'festejar', + 'vegas' + ], + modifiable: true), + Emoji( + name: 'woman dancing: medium skin tone', + char: '\u{1F483}\u{1F3FD}', + shortName: 'dancer_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'dancing', + 'medium skin tone', + 'woman', + 'uc8', + 'instruments', + 'diversity', + 'women', + 'mexican', + 'sexy', + 'circus', + 'beautiful', + 'girls night', + 'dance', + 'hawaii', + 'celebrate', + 'disco', + 'las vegas', + 'fun', + 'activity', + 'dress', + 'music', + 'instrument', + 'singing', + 'concert', + 'jaz', + 'listen', + 'singer', + 'song', + 'musique', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'mexico', + 'cinco de mayo', + 'español', + 'circus tent', + 'clown', + 'clowns', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'ladies night', + 'girls only', + 'girlfriend', + 'dancers', + 'dancing', + 'ballet', + 'ballerina', + 'dabbing', + 'salsa', + 'aloha', + 'kawaii', + 'maui', + 'moana', + 'celebration', + 'event', + 'celebrating', + 'festa', + 'parties', + 'events', + 'new years', + 'new year', + 'fiesta', + 'fete', + 'newyear', + 'party', + 'festive', + 'festival', + 'yolo', + 'festejar', + 'vegas' + ], + modifiable: true), + Emoji( + name: 'woman dancing: medium-dark skin tone', + char: '\u{1F483}\u{1F3FE}', + shortName: 'dancer_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'dancing', + 'medium-dark skin tone', + 'woman', + 'uc8', + 'instruments', + 'diversity', + 'women', + 'mexican', + 'sexy', + 'circus', + 'beautiful', + 'girls night', + 'dance', + 'hawaii', + 'celebrate', + 'disco', + 'las vegas', + 'fun', + 'activity', + 'dress', + 'music', + 'instrument', + 'singing', + 'concert', + 'jaz', + 'listen', + 'singer', + 'song', + 'musique', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'mexico', + 'cinco de mayo', + 'español', + 'circus tent', + 'clown', + 'clowns', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'ladies night', + 'girls only', + 'girlfriend', + 'dancers', + 'dancing', + 'ballet', + 'ballerina', + 'dabbing', + 'salsa', + 'aloha', + 'kawaii', + 'maui', + 'moana', + 'celebration', + 'event', + 'celebrating', + 'festa', + 'parties', + 'events', + 'new years', + 'new year', + 'fiesta', + 'fete', + 'newyear', + 'party', + 'festive', + 'festival', + 'yolo', + 'festejar', + 'vegas' + ], + modifiable: true), + Emoji( + name: 'woman dancing: dark skin tone', + char: '\u{1F483}\u{1F3FF}', + shortName: 'dancer_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'dancing', + 'dark skin tone', + 'woman', + 'uc8', + 'instruments', + 'diversity', + 'women', + 'mexican', + 'sexy', + 'circus', + 'beautiful', + 'girls night', + 'dance', + 'hawaii', + 'celebrate', + 'disco', + 'las vegas', + 'fun', + 'activity', + 'dress', + 'music', + 'instrument', + 'singing', + 'concert', + 'jaz', + 'listen', + 'singer', + 'song', + 'musique', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'mexico', + 'cinco de mayo', + 'español', + 'circus tent', + 'clown', + 'clowns', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'ladies night', + 'girls only', + 'girlfriend', + 'dancers', + 'dancing', + 'ballet', + 'ballerina', + 'dabbing', + 'salsa', + 'aloha', + 'kawaii', + 'maui', + 'moana', + 'celebration', + 'event', + 'celebrating', + 'festa', + 'parties', + 'events', + 'new years', + 'new year', + 'fiesta', + 'fete', + 'newyear', + 'party', + 'festive', + 'festival', + 'yolo', + 'festejar', + 'vegas' + ], + modifiable: true), + Emoji( + name: 'man dancing', + char: '\u{1F57A}', + shortName: 'man_dancing', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'dance', + 'man', + 'uc9', + 'instruments', + 'diversity', + 'men', + 'boys night', + 'dance', + 'celebrate', + 'disco', + 'daddy', + 'las vegas', + 'fun', + 'activity', + 'music', + 'instrument', + 'singing', + 'concert', + 'jaz', + 'listen', + 'singer', + 'song', + 'musique', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'guys night', + 'dancers', + 'dancing', + 'ballet', + 'ballerina', + 'dabbing', + 'salsa', + 'celebration', + 'event', + 'celebrating', + 'festa', + 'parties', + 'events', + 'new years', + 'new year', + 'fiesta', + 'fete', + 'newyear', + 'party', + 'festive', + 'festival', + 'yolo', + 'festejar', + 'dad', + 'papa', + 'pere', + 'father', + 'vegas' + ]), + Emoji( + name: 'man dancing: light skin tone', + char: '\u{1F57A}\u{1F3FB}', + shortName: 'man_dancing_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'dance', + 'light skin tone', + 'man', + 'uc9', + 'instruments', + 'diversity', + 'men', + 'boys night', + 'dance', + 'celebrate', + 'disco', + 'daddy', + 'las vegas', + 'fun', + 'activity', + 'music', + 'instrument', + 'singing', + 'concert', + 'jaz', + 'listen', + 'singer', + 'song', + 'musique', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'guys night', + 'dancers', + 'dancing', + 'ballet', + 'ballerina', + 'dabbing', + 'salsa', + 'celebration', + 'event', + 'celebrating', + 'festa', + 'parties', + 'events', + 'new years', + 'new year', + 'fiesta', + 'fete', + 'newyear', + 'party', + 'festive', + 'festival', + 'yolo', + 'festejar', + 'dad', + 'papa', + 'pere', + 'father', + 'vegas' + ], + modifiable: true), + Emoji( + name: 'man dancing: medium-light skin tone', + char: '\u{1F57A}\u{1F3FC}', + shortName: 'man_dancing_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'dance', + 'man', + 'medium-light skin tone', + 'uc9', + 'instruments', + 'diversity', + 'men', + 'boys night', + 'dance', + 'celebrate', + 'disco', + 'daddy', + 'las vegas', + 'fun', + 'activity', + 'music', + 'instrument', + 'singing', + 'concert', + 'jaz', + 'listen', + 'singer', + 'song', + 'musique', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'guys night', + 'dancers', + 'dancing', + 'ballet', + 'ballerina', + 'dabbing', + 'salsa', + 'celebration', + 'event', + 'celebrating', + 'festa', + 'parties', + 'events', + 'new years', + 'new year', + 'fiesta', + 'fete', + 'newyear', + 'party', + 'festive', + 'festival', + 'yolo', + 'festejar', + 'dad', + 'papa', + 'pere', + 'father', + 'vegas' + ], + modifiable: true), + Emoji( + name: 'man dancing: medium skin tone', + char: '\u{1F57A}\u{1F3FD}', + shortName: 'man_dancing_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'dance', + 'man', + 'medium skin tone', + 'uc9', + 'instruments', + 'diversity', + 'men', + 'boys night', + 'dance', + 'celebrate', + 'disco', + 'daddy', + 'las vegas', + 'fun', + 'activity', + 'music', + 'instrument', + 'singing', + 'concert', + 'jaz', + 'listen', + 'singer', + 'song', + 'musique', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'guys night', + 'dancers', + 'dancing', + 'ballet', + 'ballerina', + 'dabbing', + 'salsa', + 'celebration', + 'event', + 'celebrating', + 'festa', + 'parties', + 'events', + 'new years', + 'new year', + 'fiesta', + 'fete', + 'newyear', + 'party', + 'festive', + 'festival', + 'yolo', + 'festejar', + 'dad', + 'papa', + 'pere', + 'father', + 'vegas' + ], + modifiable: true), + Emoji( + name: 'man dancing: dark skin tone', + char: '\u{1F57A}\u{1F3FF}', + shortName: 'man_dancing_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'dance', + 'dark skin tone', + 'man', + 'uc9', + 'instruments', + 'diversity', + 'men', + 'boys night', + 'dance', + 'celebrate', + 'disco', + 'daddy', + 'las vegas', + 'fun', + 'activity', + 'music', + 'instrument', + 'singing', + 'concert', + 'jaz', + 'listen', + 'singer', + 'song', + 'musique', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'guys night', + 'dancers', + 'dancing', + 'ballet', + 'ballerina', + 'dabbing', + 'salsa', + 'celebration', + 'event', + 'celebrating', + 'festa', + 'parties', + 'events', + 'new years', + 'new year', + 'fiesta', + 'fete', + 'newyear', + 'party', + 'festive', + 'festival', + 'yolo', + 'festejar', + 'dad', + 'papa', + 'pere', + 'father', + 'vegas' + ], + modifiable: true), + Emoji( + name: 'man dancing: medium-dark skin tone', + char: '\u{1F57A}\u{1F3FE}', + shortName: 'man_dancing_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'dance', + 'man', + 'medium-dark skin tone', + 'uc9', + 'instruments', + 'diversity', + 'men', + 'boys night', + 'dance', + 'celebrate', + 'disco', + 'daddy', + 'las vegas', + 'fun', + 'activity', + 'music', + 'instrument', + 'singing', + 'concert', + 'jaz', + 'listen', + 'singer', + 'song', + 'musique', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'guys night', + 'dancers', + 'dancing', + 'ballet', + 'ballerina', + 'dabbing', + 'salsa', + 'celebration', + 'event', + 'celebrating', + 'festa', + 'parties', + 'events', + 'new years', + 'new year', + 'fiesta', + 'fete', + 'newyear', + 'party', + 'festive', + 'festival', + 'yolo', + 'festejar', + 'dad', + 'papa', + 'pere', + 'father', + 'vegas' + ], + modifiable: true), + Emoji( + name: 'people with bunny ears', + char: '\u{1F46F}', + shortName: 'people_with_bunny_ears_partying', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'bunny ear', + 'dancer', + 'partying', + 'uc6', + 'instruments', + 'silly', + 'halloween', + 'men', + 'japan', + 'sexy', + 'girls night', + 'boys night', + 'dance', + 'easter', + 'porn', + 'las vegas', + 'fun', + 'crazy', + 'playboy', + 'activity', + 'disguise', + 'music', + 'instrument', + 'singing', + 'concert', + 'jaz', + 'listen', + 'singer', + 'song', + 'musique', + 'funny', + 'samhain', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'japanese', + 'ninja', + 'ladies night', + 'girls only', + 'girlfriend', + 'guys night', + 'dancers', + 'dancing', + 'ballet', + 'ballerina', + 'dabbing', + 'salsa', + 'vegas', + 'weird', + 'awkward', + 'insane', + 'wild', + 'play boy' + ]), + Emoji( + name: 'women with bunny ears', + char: '\u{1F46F}\u{200D}\u{2640}\u{FE0F}', + shortName: 'women_with_bunny_ears_partying', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'bunny ear', + 'dancer', + 'partying', + 'women', + 'uc6', + 'instruments', + 'silly', + 'women', + 'halloween', + 'japan', + 'girls night', + 'boys night', + 'dance', + 'easter', + 'las vegas', + 'fun', + 'crazy', + 'playboy', + 'activity', + 'disguise', + 'music', + 'instrument', + 'singing', + 'concert', + 'jaz', + 'listen', + 'singer', + 'song', + 'musique', + 'funny', + 'woman', + 'female', + 'samhain', + 'japanese', + 'ninja', + 'ladies night', + 'girls only', + 'girlfriend', + 'guys night', + 'dancers', + 'dancing', + 'ballet', + 'ballerina', + 'dabbing', + 'salsa', + 'vegas', + 'weird', + 'awkward', + 'insane', + 'wild', + 'play boy' + ]), + Emoji( + name: 'men with bunny ears', + char: '\u{1F46F}\u{200D}\u{2642}\u{FE0F}', + shortName: 'men_with_bunny_ears_partying', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'bunny ear', + 'dancer', + 'men', + 'partying', + 'uc6', + 'instruments', + 'silly', + 'halloween', + 'men', + 'japan', + 'girls night', + 'boys night', + 'dance', + 'queen', + 'easter', + 'las vegas', + 'fun', + 'crazy', + 'playboy', + 'activity', + 'disguise', + 'music', + 'instrument', + 'singing', + 'concert', + 'jaz', + 'listen', + 'singer', + 'song', + 'musique', + 'funny', + 'samhain', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'japanese', + 'ninja', + 'ladies night', + 'girls only', + 'girlfriend', + 'guys night', + 'dancers', + 'dancing', + 'ballet', + 'ballerina', + 'dabbing', + 'salsa', + 'king', + 'prince', + 'princess', + 'vegas', + 'weird', + 'awkward', + 'insane', + 'wild', + 'play boy' + ]), + Emoji( + name: 'person in suit levitating', + char: '\u{1F574}\u{FE0F}', + shortName: 'levitate', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'business', + 'man', + 'suit', + 'uc7', + 'halloween', + 'men', + 'job', + 'business', + 'sunglasses', + 'google', + 'detective', + 'fame', + 'gangster', + 'super hero', + 'vampire', + 'las vegas', + 'mystery', + 'disguise', + 'samhain', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'profession', + 'boss', + 'career', + 'shades', + 'lunettes de soleil', + 'sun glasses', + 'famous', + 'celebrity', + 'thug', + 'superhero', + 'superman', + 'batman', + 'dracula', + 'vegas' + ]), + Emoji( + name: 'person in suit levitating: light skin tone', + char: '\u{1F574}\u{1F3FB}', + shortName: 'levitate_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'business', + 'light skin tone', + 'man', + 'suit', + 'uc8', + 'halloween', + 'men', + 'job', + 'business', + 'sunglasses', + 'google', + 'detective', + 'fame', + 'gangster', + 'super hero', + 'vampire', + 'las vegas', + 'mystery', + 'disguise', + 'samhain', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'profession', + 'boss', + 'career', + 'shades', + 'lunettes de soleil', + 'sun glasses', + 'famous', + 'celebrity', + 'thug', + 'superhero', + 'superman', + 'batman', + 'dracula', + 'vegas' + ], + modifiable: true), + Emoji( + name: 'person in suit levitating: medium-light skin tone', + char: '\u{1F574}\u{1F3FC}', + shortName: 'levitate_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'business', + 'man', + 'medium-light skin tone', + 'suit', + 'uc8', + 'halloween', + 'men', + 'job', + 'business', + 'sunglasses', + 'google', + 'detective', + 'fame', + 'gangster', + 'super hero', + 'vampire', + 'las vegas', + 'mystery', + 'disguise', + 'samhain', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'profession', + 'boss', + 'career', + 'shades', + 'lunettes de soleil', + 'sun glasses', + 'famous', + 'celebrity', + 'thug', + 'superhero', + 'superman', + 'batman', + 'dracula', + 'vegas' + ], + modifiable: true), + Emoji( + name: 'person in suit levitating: medium skin tone', + char: '\u{1F574}\u{1F3FD}', + shortName: 'levitate_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'business', + 'man', + 'medium skin tone', + 'suit', + 'uc8', + 'halloween', + 'men', + 'job', + 'business', + 'sunglasses', + 'google', + 'detective', + 'fame', + 'gangster', + 'super hero', + 'vampire', + 'las vegas', + 'mystery', + 'disguise', + 'samhain', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'profession', + 'boss', + 'career', + 'shades', + 'lunettes de soleil', + 'sun glasses', + 'famous', + 'celebrity', + 'thug', + 'superhero', + 'superman', + 'batman', + 'dracula', + 'vegas' + ], + modifiable: true), + Emoji( + name: 'person in suit levitating: medium-dark skin tone', + char: '\u{1F574}\u{1F3FE}', + shortName: 'levitate_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'business', + 'man', + 'medium-dark skin tone', + 'suit', + 'uc8', + 'halloween', + 'men', + 'job', + 'business', + 'sunglasses', + 'google', + 'detective', + 'fame', + 'gangster', + 'super hero', + 'vampire', + 'las vegas', + 'mystery', + 'disguise', + 'samhain', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'profession', + 'boss', + 'career', + 'shades', + 'lunettes de soleil', + 'sun glasses', + 'famous', + 'celebrity', + 'thug', + 'superhero', + 'superman', + 'batman', + 'dracula', + 'vegas' + ], + modifiable: true), + Emoji( + name: 'person in suit levitating: dark skin tone', + char: '\u{1F574}\u{1F3FF}', + shortName: 'levitate_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'business', + 'dark skin tone', + 'man', + 'suit', + 'uc8', + 'halloween', + 'men', + 'job', + 'business', + 'sunglasses', + 'google', + 'detective', + 'fame', + 'gangster', + 'super hero', + 'vampire', + 'las vegas', + 'mystery', + 'disguise', + 'samhain', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'profession', + 'boss', + 'career', + 'shades', + 'lunettes de soleil', + 'sun glasses', + 'famous', + 'celebrity', + 'thug', + 'superhero', + 'superman', + 'batman', + 'dracula', + 'vegas' + ], + modifiable: true), + Emoji( + name: 'person in manual wheelchair', + char: '\u{1F9D1}\u{200D}\u{1F9BD}', + shortName: 'person_in_manual_wheelchair', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'old people', + 'handicap', + 'accessibility', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'disabled', + 'disability' + ]), + Emoji( + name: 'person in manual wheelchair: light skin tone', + char: '\u{1F9D1}\u{1F3FB}\u{200D}\u{1F9BD}', + shortName: 'person_in_manual_wheelchair_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'old people', + 'handicap', + 'accessibility', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'disabled', + 'disability' + ], + modifiable: true), + Emoji( + name: 'person in manual wheelchair: medium-light skin tone', + char: '\u{1F9D1}\u{1F3FC}\u{200D}\u{1F9BD}', + shortName: 'person_in_manual_wheelchair_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'old people', + 'handicap', + 'accessibility', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'disabled', + 'disability' + ], + modifiable: true), + Emoji( + name: 'person in manual wheelchair: medium skin tone', + char: '\u{1F9D1}\u{1F3FD}\u{200D}\u{1F9BD}', + shortName: 'person_in_manual_wheelchair_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'old people', + 'handicap', + 'accessibility', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'disabled', + 'disability' + ], + modifiable: true), + Emoji( + name: 'person in manual wheelchair: medium-dark skin tone', + char: '\u{1F9D1}\u{1F3FE}\u{200D}\u{1F9BD}', + shortName: 'person_in_manual_wheelchair_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'old people', + 'handicap', + 'accessibility', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'disabled', + 'disability' + ], + modifiable: true), + Emoji( + name: 'person in manual wheelchair: dark skin tone', + char: '\u{1F9D1}\u{1F3FF}\u{200D}\u{1F9BD}', + shortName: 'person_in_manual_wheelchair_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'old people', + 'handicap', + 'accessibility', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'disabled', + 'disability' + ], + modifiable: true), + Emoji( + name: 'woman in manual wheelchair', + char: '\u{1F469}\u{200D}\u{1F9BD}', + shortName: 'woman_in_manual_wheelchair', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'old people', + 'diversity', + 'handicap', + 'accessibility', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'disabled', + 'disability' + ]), + Emoji( + name: 'woman in manual wheelchair: light skin tone', + char: '\u{1F469}\u{1F3FB}\u{200D}\u{1F9BD}', + shortName: 'woman_in_manual_wheelchair_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'old people', + 'diversity', + 'handicap', + 'accessibility', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'disabled', + 'disability' + ], + modifiable: true), + Emoji( + name: 'woman in manual wheelchair: medium-light skin tone', + char: '\u{1F469}\u{1F3FC}\u{200D}\u{1F9BD}', + shortName: 'woman_in_manual_wheelchair_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'old people', + 'diversity', + 'handicap', + 'accessibility', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'disabled', + 'disability' + ], + modifiable: true), + Emoji( + name: 'woman in manual wheelchair: medium skin tone', + char: '\u{1F469}\u{1F3FD}\u{200D}\u{1F9BD}', + shortName: 'woman_in_manual_wheelchair_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'old people', + 'diversity', + 'handicap', + 'accessibility', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'disabled', + 'disability' + ], + modifiable: true), + Emoji( + name: 'woman in manual wheelchair: medium-dark skin tone', + char: '\u{1F469}\u{1F3FE}\u{200D}\u{1F9BD}', + shortName: 'woman_in_manual_wheelchair_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'old people', + 'diversity', + 'handicap', + 'accessibility', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'disabled', + 'disability' + ], + modifiable: true), + Emoji( + name: 'woman in manual wheelchair: dark skin tone', + char: '\u{1F469}\u{1F3FF}\u{200D}\u{1F9BD}', + shortName: 'woman_in_manual_wheelchair_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'old people', + 'diversity', + 'handicap', + 'accessibility', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'disabled', + 'disability' + ], + modifiable: true), + Emoji( + name: 'man in manual wheelchair', + char: '\u{1F468}\u{200D}\u{1F9BD}', + shortName: 'man_in_manual_wheelchair', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'old people', + 'diversity', + 'handicap', + 'accessibility', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'disabled', + 'disability' + ]), + Emoji( + name: 'man in manual wheelchair: light skin tone', + char: '\u{1F468}\u{1F3FB}\u{200D}\u{1F9BD}', + shortName: 'man_in_manual_wheelchair_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'old people', + 'diversity', + 'handicap', + 'accessibility', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'disabled', + 'disability' + ], + modifiable: true), + Emoji( + name: 'man in manual wheelchair: medium-light skin tone', + char: '\u{1F468}\u{1F3FC}\u{200D}\u{1F9BD}', + shortName: 'man_in_manual_wheelchair_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'old people', + 'diversity', + 'handicap', + 'accessibility', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'disabled', + 'disability' + ], + modifiable: true), + Emoji( + name: 'man in manual wheelchair: medium skin tone', + char: '\u{1F468}\u{1F3FD}\u{200D}\u{1F9BD}', + shortName: 'man_in_manual_wheelchair_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'old people', + 'diversity', + 'handicap', + 'accessibility', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'disabled', + 'disability' + ], + modifiable: true), + Emoji( + name: 'man in manual wheelchair: medium-dark skin tone', + char: '\u{1F468}\u{1F3FE}\u{200D}\u{1F9BD}', + shortName: 'man_in_manual_wheelchair_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'old people', + 'diversity', + 'handicap', + 'accessibility', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'disabled', + 'disability' + ], + modifiable: true), + Emoji( + name: 'man in manual wheelchair: dark skin tone', + char: '\u{1F468}\u{1F3FF}\u{200D}\u{1F9BD}', + shortName: 'man_in_manual_wheelchair_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'old people', + 'diversity', + 'handicap', + 'accessibility', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'disabled', + 'disability' + ], + modifiable: true), + Emoji( + name: 'person in motorized wheelchair', + char: '\u{1F9D1}\u{200D}\u{1F9BC}', + shortName: 'person_in_motorized_wheelchair', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'old people', + 'handicap', + 'accessibility', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'disabled', + 'disability' + ]), + Emoji( + name: 'person in motorized wheelchair: light skin tone', + char: '\u{1F9D1}\u{1F3FB}\u{200D}\u{1F9BC}', + shortName: 'person_in_motorized_wheelchair_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'old people', + 'handicap', + 'accessibility', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'disabled', + 'disability' + ], + modifiable: true), + Emoji( + name: 'person in motorized wheelchair: medium-light skin tone', + char: '\u{1F9D1}\u{1F3FC}\u{200D}\u{1F9BC}', + shortName: 'person_in_motorized_wheelchair_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'old people', + 'handicap', + 'accessibility', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'disabled', + 'disability' + ], + modifiable: true), + Emoji( + name: 'person in motorized wheelchair: medium skin tone', + char: '\u{1F9D1}\u{1F3FD}\u{200D}\u{1F9BC}', + shortName: 'person_in_motorized_wheelchair_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'old people', + 'handicap', + 'accessibility', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'disabled', + 'disability' + ], + modifiable: true), + Emoji( + name: 'person in motorized wheelchair: medium-dark skin tone', + char: '\u{1F9D1}\u{1F3FE}\u{200D}\u{1F9BC}', + shortName: 'person_in_motorized_wheelchair_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'old people', + 'handicap', + 'accessibility', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'disabled', + 'disability' + ], + modifiable: true), + Emoji( + name: 'person in motorized wheelchair: dark skin tone', + char: '\u{1F9D1}\u{1F3FF}\u{200D}\u{1F9BC}', + shortName: 'person_in_motorized_wheelchair_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'old people', + 'handicap', + 'accessibility', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'disabled', + 'disability' + ], + modifiable: true), + Emoji( + name: 'woman in motorized wheelchair', + char: '\u{1F469}\u{200D}\u{1F9BC}', + shortName: 'woman_in_motorized_wheelchair', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'old people', + 'diversity', + 'handicap', + 'accessibility', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'disabled', + 'disability' + ]), + Emoji( + name: 'woman in motorized wheelchair: light skin tone', + char: '\u{1F469}\u{1F3FB}\u{200D}\u{1F9BC}', + shortName: 'woman_in_motorized_wheelchair_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'old people', + 'diversity', + 'handicap', + 'accessibility', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'disabled', + 'disability' + ], + modifiable: true), + Emoji( + name: 'woman in motorized wheelchair: medium-light skin tone', + char: '\u{1F469}\u{1F3FC}\u{200D}\u{1F9BC}', + shortName: 'woman_in_motorized_wheelchair_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'old people', + 'diversity', + 'handicap', + 'accessibility', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'disabled', + 'disability' + ], + modifiable: true), + Emoji( + name: 'woman in motorized wheelchair: medium skin tone', + char: '\u{1F469}\u{1F3FD}\u{200D}\u{1F9BC}', + shortName: 'woman_in_motorized_wheelchair_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'old people', + 'diversity', + 'handicap', + 'accessibility', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'disabled', + 'disability' + ], + modifiable: true), + Emoji( + name: 'woman in motorized wheelchair: medium-dark skin tone', + char: '\u{1F469}\u{1F3FE}\u{200D}\u{1F9BC}', + shortName: 'woman_in_motorized_wheelchair_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'old people', + 'diversity', + 'handicap', + 'accessibility', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'disabled', + 'disability' + ], + modifiable: true), + Emoji( + name: 'woman in motorized wheelchair: dark skin tone', + char: '\u{1F469}\u{1F3FF}\u{200D}\u{1F9BC}', + shortName: 'woman_in_motorized_wheelchair_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'old people', + 'diversity', + 'handicap', + 'accessibility', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'disabled', + 'disability' + ], + modifiable: true), + Emoji( + name: 'man in motorized wheelchair', + char: '\u{1F468}\u{200D}\u{1F9BC}', + shortName: 'man_in_motorized_wheelchair', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'old people', + 'diversity', + 'handicap', + 'accessibility', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'disabled', + 'disability' + ]), + Emoji( + name: 'man in motorized wheelchair: light skin tone', + char: '\u{1F468}\u{1F3FB}\u{200D}\u{1F9BC}', + shortName: 'man_in_motorized_wheelchair_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'old people', + 'diversity', + 'handicap', + 'accessibility', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'disabled', + 'disability' + ], + modifiable: true), + Emoji( + name: 'man in motorized wheelchair: medium-light skin tone', + char: '\u{1F468}\u{1F3FC}\u{200D}\u{1F9BC}', + shortName: 'man_in_motorized_wheelchair_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'old people', + 'diversity', + 'handicap', + 'accessibility', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'disabled', + 'disability' + ], + modifiable: true), + Emoji( + name: 'man in motorized wheelchair: medium skin tone', + char: '\u{1F468}\u{1F3FD}\u{200D}\u{1F9BC}', + shortName: 'man_in_motorized_wheelchair_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'old people', + 'diversity', + 'handicap', + 'accessibility', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'disabled', + 'disability' + ], + modifiable: true), + Emoji( + name: 'man in motorized wheelchair: medium-dark skin tone', + char: '\u{1F468}\u{1F3FE}\u{200D}\u{1F9BC}', + shortName: 'man_in_motorized_wheelchair_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'old people', + 'diversity', + 'handicap', + 'accessibility', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'disabled', + 'disability' + ], + modifiable: true), + Emoji( + name: 'man in motorized wheelchair: dark skin tone', + char: '\u{1F468}\u{1F3FF}\u{200D}\u{1F9BC}', + shortName: 'man_in_motorized_wheelchair_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'old people', + 'diversity', + 'handicap', + 'accessibility', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'disabled', + 'disability' + ], + modifiable: true), + Emoji( + name: 'person walking', + char: '\u{1F6B6}', + shortName: 'person_walking', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'hike', + 'walk', + 'walking', + 'uc6', + 'sport', + 'diversity', + 'men', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male' + ]), + Emoji( + name: 'person walking: light skin tone', + char: '\u{1F6B6}\u{1F3FB}', + shortName: 'person_walking_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'hike', + 'light skin tone', + 'walk', + 'walking', + 'uc8', + 'sport', + 'diversity', + 'men', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male' + ], + modifiable: true), + Emoji( + name: 'person walking: medium-light skin tone', + char: '\u{1F6B6}\u{1F3FC}', + shortName: 'person_walking_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'hike', + 'medium-light skin tone', + 'walk', + 'walking', + 'uc8', + 'sport', + 'diversity', + 'men', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male' + ], + modifiable: true), + Emoji( + name: 'person walking: medium skin tone', + char: '\u{1F6B6}\u{1F3FD}', + shortName: 'person_walking_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'hike', + 'medium skin tone', + 'walk', + 'walking', + 'uc8', + 'sport', + 'diversity', + 'men', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male' + ], + modifiable: true), + Emoji( + name: 'person walking: medium-dark skin tone', + char: '\u{1F6B6}\u{1F3FE}', + shortName: 'person_walking_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'hike', + 'medium-dark skin tone', + 'walk', + 'walking', + 'uc8', + 'sport', + 'diversity', + 'men', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male' + ], + modifiable: true), + Emoji( + name: 'person walking: dark skin tone', + char: '\u{1F6B6}\u{1F3FF}', + shortName: 'person_walking_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'dark skin tone', + 'hike', + 'walk', + 'walking', + 'uc8', + 'sport', + 'diversity', + 'men', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male' + ], + modifiable: true), + Emoji( + name: 'woman walking', + char: '\u{1F6B6}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_walking', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'hike', + 'walk', + 'woman', + 'uc6', + 'sport', + 'diversity', + 'women', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female' + ]), + Emoji( + name: 'woman walking: light skin tone', + char: '\u{1F6B6}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_walking_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'hike', + 'light skin tone', + 'walk', + 'woman', + 'uc8', + 'sport', + 'diversity', + 'women', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female' + ], + modifiable: true), + Emoji( + name: 'woman walking: medium-light skin tone', + char: '\u{1F6B6}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_walking_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'hike', + 'medium-light skin tone', + 'walk', + 'woman', + 'uc8', + 'sport', + 'diversity', + 'women', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female' + ], + modifiable: true), + Emoji( + name: 'woman walking: medium skin tone', + char: '\u{1F6B6}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_walking_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'hike', + 'medium skin tone', + 'walk', + 'woman', + 'uc8', + 'sport', + 'diversity', + 'women', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female' + ], + modifiable: true), + Emoji( + name: 'woman walking: medium-dark skin tone', + char: '\u{1F6B6}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_walking_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'hike', + 'medium-dark skin tone', + 'walk', + 'woman', + 'uc8', + 'sport', + 'diversity', + 'women', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female' + ], + modifiable: true), + Emoji( + name: 'woman walking: dark skin tone', + char: '\u{1F6B6}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_walking_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'dark skin tone', + 'hike', + 'walk', + 'woman', + 'uc8', + 'sport', + 'diversity', + 'women', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female' + ], + modifiable: true), + Emoji( + name: 'man walking', + char: '\u{1F6B6}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_walking', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'hike', + 'man', + 'walk', + 'uc6', + 'sport', + 'diversity', + 'men', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male' + ]), + Emoji( + name: 'man walking: light skin tone', + char: '\u{1F6B6}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_walking_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'hike', + 'light skin tone', + 'man', + 'walk', + 'uc8', + 'sport', + 'diversity', + 'men', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male' + ], + modifiable: true), + Emoji( + name: 'man walking: medium-light skin tone', + char: '\u{1F6B6}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_walking_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'hike', + 'man', + 'medium-light skin tone', + 'walk', + 'uc8', + 'sport', + 'diversity', + 'men', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male' + ], + modifiable: true), + Emoji( + name: 'man walking: medium skin tone', + char: '\u{1F6B6}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_walking_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'hike', + 'man', + 'medium skin tone', + 'walk', + 'uc8', + 'sport', + 'diversity', + 'men', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male' + ], + modifiable: true), + Emoji( + name: 'man walking: medium-dark skin tone', + char: '\u{1F6B6}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_walking_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'hike', + 'man', + 'medium-dark skin tone', + 'walk', + 'uc8', + 'sport', + 'diversity', + 'men', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male' + ], + modifiable: true), + Emoji( + name: 'man walking: dark skin tone', + char: '\u{1F6B6}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_walking_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'dark skin tone', + 'hike', + 'man', + 'walk', + 'uc8', + 'sport', + 'diversity', + 'men', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male' + ], + modifiable: true), + Emoji( + name: 'person with white cane: light skin tone', + char: '\u{1F9D1}\u{1F3FB}\u{200D}\u{1F9AF}', + shortName: 'person_with_probing_cane_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'accessibility', + 'cane', + 'handicap', + 'blind', + 'probe', + 'disabled', + 'disability', + 'white cane' + ], + modifiable: true), + Emoji( + name: 'person with white cane', + char: '\u{1F9D1}\u{200D}\u{1F9AF}', + shortName: 'person_with_probing_cane', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'cane', + 'handicap', + 'blind', + 'probe', + 'disabled', + 'disability', + 'white cane' + ]), + Emoji( + name: 'person with white cane: medium-light skin tone', + char: '\u{1F9D1}\u{1F3FC}\u{200D}\u{1F9AF}', + shortName: 'person_with_probing_cane_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'cane', + 'handicap', + 'blind', + 'probe', + 'disabled', + 'disability', + 'white cane' + ], + modifiable: true), + Emoji( + name: 'person with white cane: medium skin tone', + char: '\u{1F9D1}\u{1F3FD}\u{200D}\u{1F9AF}', + shortName: 'person_with_probing_cane_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'cane', + 'handicap', + 'blind', + 'probe', + 'disabled', + 'disability', + 'white cane' + ], + modifiable: true), + Emoji( + name: 'person with white cane: medium-dark skin tone', + char: '\u{1F9D1}\u{1F3FE}\u{200D}\u{1F9AF}', + shortName: 'person_with_probing_cane_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'cane', + 'handicap', + 'blind', + 'probe', + 'disabled', + 'disability', + 'white cane' + ], + modifiable: true), + Emoji( + name: 'person with white cane: dark skin tone', + char: '\u{1F9D1}\u{1F3FF}\u{200D}\u{1F9AF}', + shortName: 'person_with_probing_cane_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'cane', + 'handicap', + 'blind', + 'probe', + 'disabled', + 'disability', + 'white cane' + ], + modifiable: true), + Emoji( + name: 'woman with white cane', + char: '\u{1F469}\u{200D}\u{1F9AF}', + shortName: 'woman_with_probing_cane', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'diversity', + 'handicap', + 'navigate', + 'blind', + 'probe', + 'accessibility', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'disabled', + 'disability', + 'white cane' + ]), + Emoji( + name: 'woman with white cane: light skin tone', + char: '\u{1F469}\u{1F3FB}\u{200D}\u{1F9AF}', + shortName: 'woman_with_probing_cane_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'cane', + 'diversity', + 'handicap', + 'navigate', + 'blind', + 'probe', + 'accessibility', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'disabled', + 'disability', + 'white cane' + ], + modifiable: true), + Emoji( + name: 'woman with white cane: medium-light skin tone', + char: '\u{1F469}\u{1F3FC}\u{200D}\u{1F9AF}', + shortName: 'woman_with_probing_cane_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'diversity', + 'handicap', + 'navigate', + 'blind', + 'probe', + 'accessibility', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'disabled', + 'disability', + 'white cane' + ], + modifiable: true), + Emoji( + name: 'woman with white cane: medium skin tone', + char: '\u{1F469}\u{1F3FD}\u{200D}\u{1F9AF}', + shortName: 'woman_with_probing_cane_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'diversity', + 'handicap', + 'navigate', + 'blind', + 'probe', + 'accessibility', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'disabled', + 'disability', + 'white cane' + ], + modifiable: true), + Emoji( + name: 'woman with white cane: medium-dark skin tone', + char: '\u{1F469}\u{1F3FE}\u{200D}\u{1F9AF}', + shortName: 'woman_with_probing_cane_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'diversity', + 'handicap', + 'navigate', + 'blind', + 'probe', + 'accessibility', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'disabled', + 'disability', + 'white cane' + ], + modifiable: true), + Emoji( + name: 'woman with white cane: dark skin tone', + char: '\u{1F469}\u{1F3FF}\u{200D}\u{1F9AF}', + shortName: 'woman_with_probing_cane_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'diversity', + 'handicap', + 'navigate', + 'blind', + 'probe', + 'accessibility', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'disabled', + 'disability', + 'white cane' + ], + modifiable: true), + Emoji( + name: 'man with white cane', + char: '\u{1F468}\u{200D}\u{1F9AF}', + shortName: 'man_with_probing_cane', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'diversity', + 'cane', + 'handicap', + 'navigate', + 'blind', + 'probe', + 'accessibility', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'disabled', + 'disability', + 'white cane' + ]), + Emoji( + name: 'man with white cane: light skin tone', + char: '\u{1F468}\u{1F3FB}\u{200D}\u{1F9AF}', + shortName: 'man_with_probing_cane_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'diversity', + 'cane', + 'handicap', + 'navigate', + 'blind', + 'probe', + 'accessibility', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'disabled', + 'disability', + 'white cane' + ], + modifiable: true), + Emoji( + name: 'man with white cane: medium skin tone', + char: '\u{1F468}\u{1F3FD}\u{200D}\u{1F9AF}', + shortName: 'man_with_probing_cane_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'diversity', + 'cane', + 'handicap', + 'navigate', + 'blind', + 'probe', + 'accessibility', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'disabled', + 'disability', + 'white cane' + ], + modifiable: true), + Emoji( + name: 'man with white cane: medium-light skin tone', + char: '\u{1F468}\u{1F3FC}\u{200D}\u{1F9AF}', + shortName: 'man_with_probing_cane_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'diversity', + 'cane', + 'handicap', + 'navigate', + 'blind', + 'probe', + 'accessibility', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'disabled', + 'disability', + 'white cane' + ], + modifiable: true), + Emoji( + name: 'man with white cane: medium-dark skin tone', + char: '\u{1F468}\u{1F3FE}\u{200D}\u{1F9AF}', + shortName: 'man_with_probing_cane_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'diversity', + 'cane', + 'handicap', + 'navigate', + 'blind', + 'probe', + 'accessibility', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'disabled', + 'disability', + 'white cane' + ], + modifiable: true), + Emoji( + name: 'man with white cane: dark skin tone', + char: '\u{1F468}\u{1F3FF}\u{200D}\u{1F9AF}', + shortName: 'man_with_probing_cane_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'diversity', + 'cane', + 'handicap', + 'navigate', + 'blind', + 'probe', + 'accessibility', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'disabled', + 'disability', + 'white cane' + ], + modifiable: true), + Emoji( + name: 'person kneeling', + char: '\u{1F9CE}', + shortName: 'person_kneeling', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'diversity', + 'sit', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'sitting', + 'kneel', + 'kneeling' + ]), + Emoji( + name: 'person kneeling: light skin tone', + char: '\u{1F9CE}\u{1F3FB}', + shortName: 'person_kneeling_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'diversity', + 'sit', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'sitting', + 'kneel', + 'kneeling' + ], + modifiable: true), + Emoji( + name: 'person kneeling: medium-light skin tone', + char: '\u{1F9CE}\u{1F3FC}', + shortName: 'person_kneeling_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'diversity', + 'sit', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'sitting', + 'kneel', + 'kneeling' + ], + modifiable: true), + Emoji( + name: 'person kneeling: medium skin tone', + char: '\u{1F9CE}\u{1F3FD}', + shortName: 'person_kneeling_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'diversity', + 'sit', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'sitting', + 'kneel', + 'kneeling' + ], + modifiable: true), + Emoji( + name: 'person kneeling: medium-dark skin tone', + char: '\u{1F9CE}\u{1F3FE}', + shortName: 'person_kneeling_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'diversity', + 'sit', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'sitting', + 'kneel', + 'kneeling' + ], + modifiable: true), + Emoji( + name: 'person kneeling: dark skin tone', + char: '\u{1F9CE}\u{1F3FF}', + shortName: 'person_kneeling_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'diversity', + 'sit', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'sitting', + 'kneel', + 'kneeling' + ], + modifiable: true), + Emoji( + name: 'woman kneeling', + char: '\u{1F9CE}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_kneeling', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'diversity', + 'sit', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'sitting', + 'kneel', + 'kneeling' + ]), + Emoji( + name: 'woman kneeling: light skin tone', + char: '\u{1F9CE}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_kneeling_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'diversity', + 'sit', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'sitting', + 'kneel', + 'kneeling' + ], + modifiable: true), + Emoji( + name: 'woman kneeling: medium-light skin tone', + char: '\u{1F9CE}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_kneeling_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'diversity', + 'sit', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'sitting', + 'kneel', + 'kneeling' + ], + modifiable: true), + Emoji( + name: 'woman kneeling: medium skin tone', + char: '\u{1F9CE}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_kneeling_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'diversity', + 'sit', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'sitting', + 'kneel', + 'kneeling' + ], + modifiable: true), + Emoji( + name: 'woman kneeling: medium-dark skin tone', + char: '\u{1F9CE}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_kneeling_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'diversity', + 'sit', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'sitting', + 'kneel', + 'kneeling' + ], + modifiable: true), + Emoji( + name: 'woman kneeling: dark skin tone', + char: '\u{1F9CE}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_kneeling_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'diversity', + 'sit', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'sitting', + 'kneel', + 'kneeling' + ], + modifiable: true), + Emoji( + name: 'man kneeling', + char: '\u{1F9CE}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_kneeling', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'diversity', + 'sit', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'sitting', + 'kneel', + 'kneeling' + ]), + Emoji( + name: 'man kneeling: light skin tone', + char: '\u{1F9CE}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_kneeling_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'diversity', + 'sit', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'sitting', + 'kneel', + 'kneeling' + ], + modifiable: true), + Emoji( + name: 'man kneeling: medium-light skin tone', + char: '\u{1F9CE}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_kneeling_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'diversity', + 'sit', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'sitting', + 'kneel', + 'kneeling' + ], + modifiable: true), + Emoji( + name: 'man kneeling: medium skin tone', + char: '\u{1F9CE}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_kneeling_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'diversity', + 'sit', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'sitting', + 'kneel', + 'kneeling' + ], + modifiable: true), + Emoji( + name: 'man kneeling: medium-dark skin tone', + char: '\u{1F9CE}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_kneeling_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'diversity', + 'sit', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'sitting', + 'kneel', + 'kneeling' + ], + modifiable: true), + Emoji( + name: 'man kneeling: dark skin tone', + char: '\u{1F9CE}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_kneeling_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'diversity', + 'sit', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'sitting', + 'kneel', + 'kneeling' + ], + modifiable: true), + Emoji( + name: 'person running', + char: '\u{1F3C3}', + shortName: 'person_running', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'marathon', + 'running', + 'uc6', + 'sport', + 'diversity', + 'men', + 'boys night', + 'run', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'guys night', + 'running', + 'jog', + 'runner' + ]), + Emoji( + name: 'person running: light skin tone', + char: '\u{1F3C3}\u{1F3FB}', + shortName: 'person_running_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'light skin tone', + 'marathon', + 'running', + 'uc8', + 'sport', + 'diversity', + 'men', + 'boys night', + 'run', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'guys night', + 'running', + 'jog', + 'runner' + ], + modifiable: true), + Emoji( + name: 'person running: medium-light skin tone', + char: '\u{1F3C3}\u{1F3FC}', + shortName: 'person_running_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'marathon', + 'medium-light skin tone', + 'running', + 'uc8', + 'sport', + 'diversity', + 'men', + 'boys night', + 'run', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'guys night', + 'running', + 'jog', + 'runner' + ], + modifiable: true), + Emoji( + name: 'person running: medium skin tone', + char: '\u{1F3C3}\u{1F3FD}', + shortName: 'person_running_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'marathon', + 'medium skin tone', + 'running', + 'uc8', + 'sport', + 'diversity', + 'men', + 'boys night', + 'run', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'guys night', + 'running', + 'jog', + 'runner' + ], + modifiable: true), + Emoji( + name: 'person running: medium-dark skin tone', + char: '\u{1F3C3}\u{1F3FE}', + shortName: 'person_running_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'marathon', + 'medium-dark skin tone', + 'running', + 'uc8', + 'sport', + 'diversity', + 'men', + 'boys night', + 'run', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'guys night', + 'running', + 'jog', + 'runner' + ], + modifiable: true), + Emoji( + name: 'person running: dark skin tone', + char: '\u{1F3C3}\u{1F3FF}', + shortName: 'person_running_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'dark skin tone', + 'marathon', + 'running', + 'uc8', + 'sport', + 'diversity', + 'men', + 'boys night', + 'run', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'guys night', + 'running', + 'jog', + 'runner' + ], + modifiable: true), + Emoji( + name: 'woman running', + char: '\u{1F3C3}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_running', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'marathon', + 'racing', + 'running', + 'woman', + 'uc6', + 'sport', + 'diversity', + 'women', + 'run', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'running', + 'jog', + 'runner' + ]), + Emoji( + name: 'woman running: light skin tone', + char: '\u{1F3C3}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_running_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'light skin tone', + 'marathon', + 'racing', + 'running', + 'woman', + 'uc8', + 'sport', + 'diversity', + 'women', + 'run', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'running', + 'jog', + 'runner' + ], + modifiable: true), + Emoji( + name: 'woman running: medium-light skin tone', + char: '\u{1F3C3}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_running_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'marathon', + 'medium-light skin tone', + 'racing', + 'running', + 'woman', + 'uc8', + 'sport', + 'diversity', + 'women', + 'run', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'running', + 'jog', + 'runner' + ], + modifiable: true), + Emoji( + name: 'woman running: medium skin tone', + char: '\u{1F3C3}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_running_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'marathon', + 'medium skin tone', + 'racing', + 'running', + 'woman', + 'uc8', + 'sport', + 'diversity', + 'women', + 'run', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'running', + 'jog', + 'runner' + ], + modifiable: true), + Emoji( + name: 'woman running: medium-dark skin tone', + char: '\u{1F3C3}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_running_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'marathon', + 'medium-dark skin tone', + 'racing', + 'running', + 'woman', + 'uc8', + 'sport', + 'diversity', + 'women', + 'run', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'running', + 'jog', + 'runner' + ], + modifiable: true), + Emoji( + name: 'woman running: dark skin tone', + char: '\u{1F3C3}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_running_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'dark skin tone', + 'marathon', + 'racing', + 'running', + 'woman', + 'uc8', + 'sport', + 'diversity', + 'women', + 'run', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'running', + 'jog', + 'runner' + ], + modifiable: true), + Emoji( + name: 'man running', + char: '\u{1F3C3}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_running', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'man', + 'marathon', + 'racing', + 'running', + 'uc6', + 'sport', + 'men', + 'boys night', + 'run', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'guys night', + 'running', + 'jog', + 'runner' + ]), + Emoji( + name: 'man running: light skin tone', + char: '\u{1F3C3}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_running_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'light skin tone', + 'man', + 'marathon', + 'racing', + 'running', + 'uc8', + 'sport', + 'men', + 'boys night', + 'run', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'guys night', + 'running', + 'jog', + 'runner' + ], + modifiable: true), + Emoji( + name: 'man running: medium-light skin tone', + char: '\u{1F3C3}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_running_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'man', + 'marathon', + 'medium-light skin tone', + 'racing', + 'running', + 'uc8', + 'sport', + 'men', + 'boys night', + 'run', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'guys night', + 'running', + 'jog', + 'runner' + ], + modifiable: true), + Emoji( + name: 'man running: medium skin tone', + char: '\u{1F3C3}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_running_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'man', + 'marathon', + 'medium skin tone', + 'racing', + 'running', + 'uc8', + 'sport', + 'men', + 'boys night', + 'run', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'guys night', + 'running', + 'jog', + 'runner' + ], + modifiable: true), + Emoji( + name: 'man running: medium-dark skin tone', + char: '\u{1F3C3}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_running_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'man', + 'marathon', + 'medium-dark skin tone', + 'racing', + 'running', + 'uc8', + 'sport', + 'men', + 'boys night', + 'run', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'guys night', + 'running', + 'jog', + 'runner' + ], + modifiable: true), + Emoji( + name: 'man running: dark skin tone', + char: '\u{1F3C3}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_running_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'dark skin tone', + 'man', + 'marathon', + 'racing', + 'running', + 'uc8', + 'sport', + 'men', + 'boys night', + 'run', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'guys night', + 'running', + 'jog', + 'runner' + ], + modifiable: true), + Emoji( + name: 'person standing', + char: '\u{1F9CD}', + shortName: 'person_standing', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'diversity', + 'men', + 'human', + 'daddy', + 'parent', + 'wife', + 'husband', + 'mom', + 'stand', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ]), + Emoji( + name: 'person standing: light skin tone', + char: '\u{1F9CD}\u{1F3FB}', + shortName: 'person_standing_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'diversity', + 'men', + 'human', + 'daddy', + 'parent', + 'wife', + 'husband', + 'mom', + 'stand', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'person standing: medium-light skin tone', + char: '\u{1F9CD}\u{1F3FC}', + shortName: 'person_standing_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'diversity', + 'men', + 'human', + 'daddy', + 'parent', + 'wife', + 'husband', + 'mom', + 'stand', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'person standing: medium skin tone', + char: '\u{1F9CD}\u{1F3FD}', + shortName: 'person_standing_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'diversity', + 'men', + 'human', + 'daddy', + 'parent', + 'wife', + 'husband', + 'mom', + 'stand', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'person standing: medium-dark skin tone', + char: '\u{1F9CD}\u{1F3FE}', + shortName: 'person_standing_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'diversity', + 'men', + 'human', + 'daddy', + 'parent', + 'wife', + 'husband', + 'mom', + 'stand', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'person standing: dark skin tone', + char: '\u{1F9CD}\u{1F3FF}', + shortName: 'person_standing_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'diversity', + 'men', + 'human', + 'daddy', + 'parent', + 'wife', + 'husband', + 'mom', + 'stand', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman standing', + char: '\u{1F9CD}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_standing', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'diversity', + 'human', + 'parent', + 'wife', + 'mom', + 'stand', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ]), + Emoji( + name: 'woman standing: light skin tone', + char: '\u{1F9CD}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_standing_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'diversity', + 'human', + 'parent', + 'wife', + 'mom', + 'stand', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman standing: medium-light skin tone', + char: '\u{1F9CD}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_standing_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'diversity', + 'human', + 'parent', + 'wife', + 'mom', + 'stand', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman standing: medium skin tone', + char: '\u{1F9CD}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_standing_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'diversity', + 'human', + 'parent', + 'wife', + 'mom', + 'stand', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman standing: medium-dark skin tone', + char: '\u{1F9CD}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_standing_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'diversity', + 'human', + 'parent', + 'wife', + 'mom', + 'stand', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'woman standing: dark skin tone', + char: '\u{1F9CD}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_standing_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'diversity', + 'human', + 'parent', + 'wife', + 'mom', + 'stand', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'man standing', + char: '\u{1F9CD}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_standing', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'diversity', + 'men', + 'human', + 'daddy', + 'parent', + 'husband', + 'stand', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ]), + Emoji( + name: 'man standing: light skin tone', + char: '\u{1F9CD}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_standing_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'diversity', + 'men', + 'human', + 'daddy', + 'parent', + 'husband', + 'stand', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'man standing: medium-light skin tone', + char: '\u{1F9CD}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_standing_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'diversity', + 'men', + 'human', + 'daddy', + 'parent', + 'husband', + 'stand', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'man standing: medium skin tone', + char: '\u{1F9CD}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_standing_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'diversity', + 'men', + 'human', + 'daddy', + 'parent', + 'husband', + 'stand', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'man standing: medium-dark skin tone', + char: '\u{1F9CD}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_standing_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'diversity', + 'men', + 'human', + 'daddy', + 'parent', + 'husband', + 'stand', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'man standing: dark skin tone', + char: '\u{1F9CD}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_standing_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'uc12', + 'diversity', + 'men', + 'human', + 'daddy', + 'parent', + 'husband', + 'stand', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'people holding hands', + char: '\u{1F9D1}\u{200D}\u{1F91D}\u{200D}\u{1F9D1}', + shortName: 'people_holding_hands', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'lesbian', + 'gay', + 'men', + 'lgbt', + 'friend', + 'human', + 'daddy', + 'parent', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ]), + Emoji( + name: 'people holding hands: light skin tone', + char: '\u{1F9D1}\u{1F3FB}\u{200D}\u{1F91D}\u{200D}\u{1F9D1}\u{1F3FB}', + shortName: 'people_holding_hands_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'lesbian', + 'gay', + 'men', + 'lgbt', + 'friend', + 'human', + 'daddy', + 'parent', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'people holding hands: light skin tone, medium-light skin tone', + char: '\u{1F9D1}\u{1F3FB}\u{200D}\u{1F91D}\u{200D}\u{1F9D1}\u{1F3FC}', + shortName: 'people_holding_hands_tone1_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'lesbian', + 'gay', + 'men', + 'lgbt', + 'friend', + 'human', + 'daddy', + 'parent', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'people holding hands: light skin tone, medium skin tone', + char: '\u{1F9D1}\u{1F3FB}\u{200D}\u{1F91D}\u{200D}\u{1F9D1}\u{1F3FD}', + shortName: 'people_holding_hands_tone1_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'lesbian', + 'gay', + 'men', + 'lgbt', + 'friend', + 'human', + 'daddy', + 'parent', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'people holding hands: light skin tone, medium-dark skin tone', + char: '\u{1F9D1}\u{1F3FB}\u{200D}\u{1F91D}\u{200D}\u{1F9D1}\u{1F3FE}', + shortName: 'people_holding_hands_tone1_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'lesbian', + 'gay', + 'men', + 'lgbt', + 'friend', + 'human', + 'daddy', + 'parent', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'people holding hands: light skin tone, dark skin tone', + char: '\u{1F9D1}\u{1F3FB}\u{200D}\u{1F91D}\u{200D}\u{1F9D1}\u{1F3FF}', + shortName: 'people_holding_hands_tone1_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'lesbian', + 'gay', + 'men', + 'lgbt', + 'friend', + 'human', + 'daddy', + 'parent', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'people holding hands: medium-light skin tone, light skin tone', + char: '\u{1F9D1}\u{1F3FC}\u{200D}\u{1F91D}\u{200D}\u{1F9D1}\u{1F3FB}', + shortName: 'people_holding_hands_tone2_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'lesbian', + 'gay', + 'men', + 'lgbt', + 'friend', + 'human', + 'daddy', + 'parent', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'people holding hands: medium-light skin tone', + char: '\u{1F9D1}\u{1F3FC}\u{200D}\u{1F91D}\u{200D}\u{1F9D1}\u{1F3FC}', + shortName: 'people_holding_hands_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'lesbian', + 'gay', + 'men', + 'lgbt', + 'friend', + 'human', + 'daddy', + 'parent', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'people holding hands: medium-light skin tone, medium skin tone', + char: '\u{1F9D1}\u{1F3FC}\u{200D}\u{1F91D}\u{200D}\u{1F9D1}\u{1F3FD}', + shortName: 'people_holding_hands_tone2_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'lesbian', + 'gay', + 'men', + 'lgbt', + 'friend', + 'human', + 'daddy', + 'parent', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: + 'people holding hands: medium-light skin tone, medium-dark skin tone', + char: '\u{1F9D1}\u{1F3FC}\u{200D}\u{1F91D}\u{200D}\u{1F9D1}\u{1F3FE}', + shortName: 'people_holding_hands_tone2_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'lesbian', + 'gay', + 'men', + 'lgbt', + 'friend', + 'human', + 'daddy', + 'parent', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'people holding hands: medium-light skin tone, dark skin tone', + char: '\u{1F9D1}\u{1F3FC}\u{200D}\u{1F91D}\u{200D}\u{1F9D1}\u{1F3FF}', + shortName: 'people_holding_hands_tone2_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'lesbian', + 'gay', + 'men', + 'lgbt', + 'friend', + 'human', + 'daddy', + 'parent', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'people holding hands: medium skin tone, light skin tone', + char: '\u{1F9D1}\u{1F3FD}\u{200D}\u{1F91D}\u{200D}\u{1F9D1}\u{1F3FB}', + shortName: 'people_holding_hands_tone3_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'lesbian', + 'gay', + 'men', + 'lgbt', + 'friend', + 'human', + 'daddy', + 'parent', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'people holding hands: medium skin tone, medium-light skin tone', + char: '\u{1F9D1}\u{1F3FD}\u{200D}\u{1F91D}\u{200D}\u{1F9D1}\u{1F3FC}', + shortName: 'people_holding_hands_tone3_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'lesbian', + 'gay', + 'men', + 'lgbt', + 'friend', + 'human', + 'daddy', + 'parent', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'people holding hands: medium skin tone', + char: '\u{1F9D1}\u{1F3FD}\u{200D}\u{1F91D}\u{200D}\u{1F9D1}\u{1F3FD}', + shortName: 'people_holding_hands_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'lesbian', + 'gay', + 'men', + 'lgbt', + 'friend', + 'human', + 'daddy', + 'parent', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'people holding hands: medium skin tone, medium-dark skin tone', + char: '\u{1F9D1}\u{1F3FD}\u{200D}\u{1F91D}\u{200D}\u{1F9D1}\u{1F3FE}', + shortName: 'people_holding_hands_tone3_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'lesbian', + 'gay', + 'men', + 'lgbt', + 'friend', + 'human', + 'daddy', + 'parent', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'people holding hands: medium skin tone, dark skin tone', + char: '\u{1F9D1}\u{1F3FD}\u{200D}\u{1F91D}\u{200D}\u{1F9D1}\u{1F3FF}', + shortName: 'people_holding_hands_tone3_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'lesbian', + 'gay', + 'men', + 'lgbt', + 'friend', + 'human', + 'daddy', + 'parent', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'people holding hands: medium-dark skin tone, light skin tone', + char: '\u{1F9D1}\u{1F3FE}\u{200D}\u{1F91D}\u{200D}\u{1F9D1}\u{1F3FB}', + shortName: 'people_holding_hands_tone4_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'lesbian', + 'gay', + 'men', + 'lgbt', + 'friend', + 'human', + 'daddy', + 'parent', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: + 'people holding hands: medium-dark skin tone, medium-light skin tone', + char: '\u{1F9D1}\u{1F3FE}\u{200D}\u{1F91D}\u{200D}\u{1F9D1}\u{1F3FC}', + shortName: 'people_holding_hands_tone4_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'lesbian', + 'gay', + 'men', + 'lgbt', + 'friend', + 'human', + 'daddy', + 'parent', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'people holding hands: medium-dark skin tone, medium skin tone', + char: '\u{1F9D1}\u{1F3FE}\u{200D}\u{1F91D}\u{200D}\u{1F9D1}\u{1F3FD}', + shortName: 'people_holding_hands_tone4_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'lesbian', + 'gay', + 'men', + 'lgbt', + 'friend', + 'human', + 'daddy', + 'parent', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'people holding hands: medium-dark skin tone', + char: '\u{1F9D1}\u{1F3FE}\u{200D}\u{1F91D}\u{200D}\u{1F9D1}\u{1F3FE}', + shortName: 'people_holding_hands_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'lesbian', + 'gay', + 'men', + 'lgbt', + 'friend', + 'human', + 'daddy', + 'parent', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'people holding hands: medium-dark skin tone, dark skin tone', + char: '\u{1F9D1}\u{1F3FE}\u{200D}\u{1F91D}\u{200D}\u{1F9D1}\u{1F3FF}', + shortName: 'people_holding_hands_tone4_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'lesbian', + 'gay', + 'men', + 'lgbt', + 'friend', + 'human', + 'daddy', + 'parent', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'people holding hands: dark skin tone, light skin tone', + char: '\u{1F9D1}\u{1F3FF}\u{200D}\u{1F91D}\u{200D}\u{1F9D1}\u{1F3FB}', + shortName: 'people_holding_hands_tone5_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'lesbian', + 'gay', + 'men', + 'lgbt', + 'friend', + 'human', + 'daddy', + 'parent', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'people holding hands: dark skin tone, medium-light skin tone', + char: '\u{1F9D1}\u{1F3FF}\u{200D}\u{1F91D}\u{200D}\u{1F9D1}\u{1F3FC}', + shortName: 'people_holding_hands_tone5_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'lesbian', + 'gay', + 'men', + 'lgbt', + 'friend', + 'human', + 'daddy', + 'parent', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'people holding hands: dark skin tone, medium skin tone', + char: '\u{1F9D1}\u{1F3FF}\u{200D}\u{1F91D}\u{200D}\u{1F9D1}\u{1F3FD}', + shortName: 'people_holding_hands_tone5_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'lesbian', + 'gay', + 'men', + 'lgbt', + 'friend', + 'human', + 'daddy', + 'parent', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'people holding hands: dark skin tone, medium-dark skin tone', + char: '\u{1F9D1}\u{1F3FF}\u{200D}\u{1F91D}\u{200D}\u{1F9D1}\u{1F3FE}', + shortName: 'people_holding_hands_tone5_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'lesbian', + 'gay', + 'men', + 'lgbt', + 'friend', + 'human', + 'daddy', + 'parent', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'people holding hands: dark skin tone', + char: '\u{1F9D1}\u{1F3FF}\u{200D}\u{1F91D}\u{200D}\u{1F9D1}\u{1F3FF}', + shortName: 'people_holding_hands_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'lesbian', + 'gay', + 'men', + 'lgbt', + 'friend', + 'human', + 'daddy', + 'parent', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'woman and man holding hands', + char: '\u{1F46B}', + shortName: 'couple', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'couple', + 'hand', + 'hold', + 'man', + 'woman', + 'uc6', + 'family', + 'diversity', + 'wedding', + 'creationism', + 'friend', + 'human', + 'porn', + 'parent', + 'wife', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'adam & eve', + 'adam and eve', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'parents', + 'adult' + ]), + Emoji( + name: 'woman and man holding hands: light skin tone', + char: '\u{1F46B}\u{1F3FB}', + shortName: 'woman_and_man_holding_hands_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'creationism', + 'friend', + 'human', + 'porn', + 'parent', + 'wife', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'adam & eve', + 'adam and eve', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: + 'woman and man holding hands: light skin tone, medium-light skin tone', + char: '\u{1F469}\u{1F3FB}\u{200D}\u{1F91D}\u{200D}\u{1F468}\u{1F3FC}', + shortName: 'woman_and_man_holding_hands_tone1_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'creationism', + 'friend', + 'human', + 'porn', + 'parent', + 'wife', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'adam & eve', + 'adam and eve', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'woman and man holding hands: light skin tone, medium skin tone', + char: '\u{1F469}\u{1F3FB}\u{200D}\u{1F91D}\u{200D}\u{1F468}\u{1F3FD}', + shortName: 'woman_and_man_holding_hands_tone1_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'creationism', + 'friend', + 'human', + 'porn', + 'parent', + 'wife', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'adam & eve', + 'adam and eve', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: + 'woman and man holding hands: light skin tone, medium-dark skin tone', + char: '\u{1F469}\u{1F3FB}\u{200D}\u{1F91D}\u{200D}\u{1F468}\u{1F3FE}', + shortName: 'woman_and_man_holding_hands_tone1_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'creationism', + 'friend', + 'human', + 'porn', + 'parent', + 'wife', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'adam & eve', + 'adam and eve', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'woman and man holding hands: light skin tone, dark skin tone', + char: '\u{1F469}\u{1F3FB}\u{200D}\u{1F91D}\u{200D}\u{1F468}\u{1F3FF}', + shortName: 'woman_and_man_holding_hands_tone1_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'creationism', + 'friend', + 'human', + 'porn', + 'parent', + 'wife', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'adam & eve', + 'adam and eve', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: + 'woman and man holding hands: medium-light skin tone, light skin tone', + char: '\u{1F469}\u{1F3FC}\u{200D}\u{1F91D}\u{200D}\u{1F468}\u{1F3FB}', + shortName: 'woman_and_man_holding_hands_tone2_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'creationism', + 'friend', + 'human', + 'porn', + 'parent', + 'wife', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'adam & eve', + 'adam and eve', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'woman and man holding hands: medium-light skin tone', + char: '\u{1F46B}\u{1F3FC}', + shortName: 'woman_and_man_holding_hands_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'creationism', + 'friend', + 'human', + 'porn', + 'parent', + 'wife', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'adam & eve', + 'adam and eve', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: + 'woman and man holding hands: medium-light skin tone, medium skin tone', + char: '\u{1F469}\u{1F3FC}\u{200D}\u{1F91D}\u{200D}\u{1F468}\u{1F3FD}', + shortName: 'woman_and_man_holding_hands_tone2_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'creationism', + 'friend', + 'human', + 'porn', + 'parent', + 'wife', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'adam & eve', + 'adam and eve', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: + 'woman and man holding hands: medium-light skin tone, medium-dark skin tone', + char: '\u{1F469}\u{1F3FC}\u{200D}\u{1F91D}\u{200D}\u{1F468}\u{1F3FE}', + shortName: 'woman_and_man_holding_hands_tone2_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'creationism', + 'friend', + 'human', + 'porn', + 'parent', + 'wife', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'adam & eve', + 'adam and eve', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: + 'woman and man holding hands: medium-light skin tone, dark skin tone', + char: '\u{1F469}\u{1F3FC}\u{200D}\u{1F91D}\u{200D}\u{1F468}\u{1F3FF}', + shortName: 'woman_and_man_holding_hands_tone2_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'creationism', + 'friend', + 'human', + 'porn', + 'parent', + 'wife', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'adam & eve', + 'adam and eve', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'woman and man holding hands: medium skin tone, light skin tone', + char: '\u{1F469}\u{1F3FD}\u{200D}\u{1F91D}\u{200D}\u{1F468}\u{1F3FB}', + shortName: 'woman_and_man_holding_hands_tone3_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'creationism', + 'friend', + 'human', + 'porn', + 'parent', + 'wife', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'adam & eve', + 'adam and eve', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: + 'woman and man holding hands: medium skin tone, medium-light skin tone', + char: '\u{1F469}\u{1F3FD}\u{200D}\u{1F91D}\u{200D}\u{1F468}\u{1F3FC}', + shortName: 'woman_and_man_holding_hands_tone3_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'creationism', + 'friend', + 'human', + 'porn', + 'parent', + 'wife', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'adam & eve', + 'adam and eve', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'woman and man holding hands: medium skin tone', + char: '\u{1F46B}\u{1F3FD}', + shortName: 'woman_and_man_holding_hands_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'creationism', + 'friend', + 'human', + 'porn', + 'parent', + 'wife', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'adam & eve', + 'adam and eve', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: + 'woman and man holding hands: medium skin tone, medium-dark skin tone', + char: '\u{1F469}\u{1F3FD}\u{200D}\u{1F91D}\u{200D}\u{1F468}\u{1F3FE}', + shortName: 'woman_and_man_holding_hands_tone3_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'creationism', + 'friend', + 'human', + 'porn', + 'parent', + 'wife', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'adam & eve', + 'adam and eve', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'woman and man holding hands: medium skin tone, dark skin tone', + char: '\u{1F469}\u{1F3FD}\u{200D}\u{1F91D}\u{200D}\u{1F468}\u{1F3FF}', + shortName: 'woman_and_man_holding_hands_tone3_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'creationism', + 'friend', + 'human', + 'porn', + 'parent', + 'wife', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'adam & eve', + 'adam and eve', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: + 'woman and man holding hands: medium-dark skin tone, light skin tone', + char: '\u{1F469}\u{1F3FE}\u{200D}\u{1F91D}\u{200D}\u{1F468}\u{1F3FB}', + shortName: 'woman_and_man_holding_hands_tone4_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'creationism', + 'friend', + 'human', + 'porn', + 'parent', + 'wife', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'adam & eve', + 'adam and eve', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: + 'woman and man holding hands: medium-dark skin tone, medium-light skin tone', + char: '\u{1F469}\u{1F3FE}\u{200D}\u{1F91D}\u{200D}\u{1F468}\u{1F3FC}', + shortName: 'woman_and_man_holding_hands_tone4_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'creationism', + 'friend', + 'human', + 'porn', + 'parent', + 'wife', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'adam & eve', + 'adam and eve', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: + 'woman and man holding hands: medium-dark skin tone, medium skin tone', + char: '\u{1F469}\u{1F3FE}\u{200D}\u{1F91D}\u{200D}\u{1F468}\u{1F3FD}', + shortName: 'woman_and_man_holding_hands_tone4_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'creationism', + 'friend', + 'human', + 'porn', + 'parent', + 'wife', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'adam & eve', + 'adam and eve', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'woman and man holding hands: medium-dark skin tone', + char: '\u{1F46B}\u{1F3FE}', + shortName: 'woman_and_man_holding_hands_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'creationism', + 'friend', + 'human', + 'porn', + 'parent', + 'wife', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'adam & eve', + 'adam and eve', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: + 'woman and man holding hands: medium-dark skin tone, dark skin tone', + char: '\u{1F469}\u{1F3FE}\u{200D}\u{1F91D}\u{200D}\u{1F468}\u{1F3FF}', + shortName: 'woman_and_man_holding_hands_tone4_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'creationism', + 'friend', + 'human', + 'porn', + 'parent', + 'wife', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'adam & eve', + 'adam and eve', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'woman and man holding hands: dark skin tone, light skin tone', + char: '\u{1F469}\u{1F3FF}\u{200D}\u{1F91D}\u{200D}\u{1F468}\u{1F3FB}', + shortName: 'woman_and_man_holding_hands_tone5_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'creationism', + 'friend', + 'human', + 'porn', + 'parent', + 'wife', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'adam & eve', + 'adam and eve', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: + 'woman and man holding hands: dark skin tone, medium-light skin tone', + char: '\u{1F469}\u{1F3FF}\u{200D}\u{1F91D}\u{200D}\u{1F468}\u{1F3FC}', + shortName: 'woman_and_man_holding_hands_tone5_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'creationism', + 'friend', + 'human', + 'porn', + 'parent', + 'wife', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'adam & eve', + 'adam and eve', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'woman and man holding hands: dark skin tone, medium skin tone', + char: '\u{1F469}\u{1F3FF}\u{200D}\u{1F91D}\u{200D}\u{1F468}\u{1F3FD}', + shortName: 'woman_and_man_holding_hands_tone5_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'creationism', + 'friend', + 'human', + 'porn', + 'parent', + 'wife', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'adam & eve', + 'adam and eve', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: + 'woman and man holding hands: dark skin tone, medium-dark skin tone', + char: '\u{1F469}\u{1F3FF}\u{200D}\u{1F91D}\u{200D}\u{1F468}\u{1F3FE}', + shortName: 'woman_and_man_holding_hands_tone5_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'creationism', + 'friend', + 'human', + 'porn', + 'parent', + 'wife', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'adam & eve', + 'adam and eve', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'woman and man holding hands: dark skin tone', + char: '\u{1F46B}\u{1F3FF}', + shortName: 'woman_and_man_holding_hands_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'creationism', + 'friend', + 'human', + 'porn', + 'parent', + 'wife', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'adam & eve', + 'adam and eve', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'women holding hands', + char: '\u{1F46D}', + shortName: 'two_women_holding_hands', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'couple', + 'hand', + 'hold', + 'woman', + 'uc6', + 'family', + 'diversity', + 'wedding', + 'lesbian', + 'gay', + 'women', + 'lgbt', + 'girls night', + 'friend', + 'human', + 'porn', + 'parent', + 'wife', + 'mom', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'woman', + 'female', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'ladies night', + 'girls only', + 'girlfriend', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ]), + Emoji( + name: 'women holding hands: light skin tone', + char: '\u{1F46D}\u{1F3FB}', + shortName: 'women_holding_hands_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'lesbian', + 'gay', + 'women', + 'lgbt', + 'girls night', + 'friend', + 'human', + 'porn', + 'parent', + 'wife', + 'mom', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'woman', + 'female', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'ladies night', + 'girls only', + 'girlfriend', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'women holding hands: light skin tone, medium-light skin tone', + char: '\u{1F469}\u{1F3FB}\u{200D}\u{1F91D}\u{200D}\u{1F469}\u{1F3FC}', + shortName: 'women_holding_hands_tone1_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'lesbian', + 'gay', + 'women', + 'lgbt', + 'girls night', + 'friend', + 'human', + 'porn', + 'parent', + 'wife', + 'mom', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'woman', + 'female', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'ladies night', + 'girls only', + 'girlfriend', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'women holding hands: light skin tone, medium skin tone', + char: '\u{1F469}\u{1F3FB}\u{200D}\u{1F91D}\u{200D}\u{1F469}\u{1F3FD}', + shortName: 'women_holding_hands_tone1_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'lesbian', + 'gay', + 'women', + 'lgbt', + 'girls night', + 'friend', + 'human', + 'porn', + 'parent', + 'wife', + 'mom', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'woman', + 'female', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'ladies night', + 'girls only', + 'girlfriend', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'women holding hands: light skin tone, medium-dark skin tone', + char: '\u{1F469}\u{1F3FB}\u{200D}\u{1F91D}\u{200D}\u{1F469}\u{1F3FE}', + shortName: 'women_holding_hands_tone1_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'lesbian', + 'gay', + 'women', + 'lgbt', + 'girls night', + 'friend', + 'human', + 'porn', + 'parent', + 'wife', + 'mom', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'woman', + 'female', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'ladies night', + 'girls only', + 'girlfriend', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'women holding hands: light skin tone, dark skin tone', + char: '\u{1F469}\u{1F3FB}\u{200D}\u{1F91D}\u{200D}\u{1F469}\u{1F3FF}', + shortName: 'women_holding_hands_tone1_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'lesbian', + 'gay', + 'women', + 'lgbt', + 'girls night', + 'friend', + 'human', + 'porn', + 'parent', + 'wife', + 'mom', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'woman', + 'female', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'ladies night', + 'girls only', + 'girlfriend', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'women holding hands: medium-light skin tone, light skin tone', + char: '\u{1F469}\u{1F3FC}\u{200D}\u{1F91D}\u{200D}\u{1F469}\u{1F3FB}', + shortName: 'women_holding_hands_tone2_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'lesbian', + 'gay', + 'women', + 'lgbt', + 'girls night', + 'friend', + 'human', + 'porn', + 'parent', + 'wife', + 'mom', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'woman', + 'female', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'ladies night', + 'girls only', + 'girlfriend', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'women holding hands: medium-light skin tone', + char: '\u{1F46D}\u{1F3FC}', + shortName: 'women_holding_hands_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'lesbian', + 'gay', + 'women', + 'lgbt', + 'girls night', + 'friend', + 'human', + 'porn', + 'parent', + 'wife', + 'mom', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'woman', + 'female', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'ladies night', + 'girls only', + 'girlfriend', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'women holding hands: medium-light skin tone, medium skin tone', + char: '\u{1F469}\u{1F3FC}\u{200D}\u{1F91D}\u{200D}\u{1F469}\u{1F3FD}', + shortName: 'women_holding_hands_tone2_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'lesbian', + 'gay', + 'women', + 'lgbt', + 'girls night', + 'friend', + 'human', + 'porn', + 'parent', + 'wife', + 'mom', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'woman', + 'female', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'ladies night', + 'girls only', + 'girlfriend', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: + 'women holding hands: medium-light skin tone, medium-dark skin tone', + char: '\u{1F469}\u{1F3FC}\u{200D}\u{1F91D}\u{200D}\u{1F469}\u{1F3FE}', + shortName: 'women_holding_hands_tone2_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'lesbian', + 'gay', + 'women', + 'lgbt', + 'girls night', + 'friend', + 'human', + 'porn', + 'parent', + 'wife', + 'mom', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'woman', + 'female', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'ladies night', + 'girls only', + 'girlfriend', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'women holding hands: medium-light skin tone, dark skin tone', + char: '\u{1F469}\u{1F3FC}\u{200D}\u{1F91D}\u{200D}\u{1F469}\u{1F3FF}', + shortName: 'women_holding_hands_tone2_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'lesbian', + 'gay', + 'women', + 'lgbt', + 'girls night', + 'friend', + 'human', + 'porn', + 'parent', + 'wife', + 'mom', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'woman', + 'female', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'ladies night', + 'girls only', + 'girlfriend', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'women holding hands: medium skin tone, light skin tone', + char: '\u{1F469}\u{1F3FD}\u{200D}\u{1F91D}\u{200D}\u{1F469}\u{1F3FB}', + shortName: 'women_holding_hands_tone3_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'lesbian', + 'gay', + 'women', + 'lgbt', + 'girls night', + 'friend', + 'human', + 'porn', + 'parent', + 'wife', + 'mom', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'woman', + 'female', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'ladies night', + 'girls only', + 'girlfriend', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'women holding hands: medium skin tone, medium-light skin tone', + char: '\u{1F469}\u{1F3FD}\u{200D}\u{1F91D}\u{200D}\u{1F469}\u{1F3FC}', + shortName: 'women_holding_hands_tone3_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'lesbian', + 'gay', + 'women', + 'lgbt', + 'girls night', + 'friend', + 'human', + 'porn', + 'parent', + 'wife', + 'mom', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'woman', + 'female', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'ladies night', + 'girls only', + 'girlfriend', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'women holding hands: medium skin tone', + char: '\u{1F46D}\u{1F3FD}', + shortName: 'women_holding_hands_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'lesbian', + 'gay', + 'women', + 'lgbt', + 'girls night', + 'friend', + 'human', + 'porn', + 'parent', + 'wife', + 'mom', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'woman', + 'female', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'ladies night', + 'girls only', + 'girlfriend', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'women holding hands: medium skin tone, medium-dark skin tone', + char: '\u{1F469}\u{1F3FD}\u{200D}\u{1F91D}\u{200D}\u{1F469}\u{1F3FE}', + shortName: 'women_holding_hands_tone3_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'lesbian', + 'gay', + 'women', + 'lgbt', + 'girls night', + 'friend', + 'human', + 'porn', + 'parent', + 'wife', + 'mom', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'woman', + 'female', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'ladies night', + 'girls only', + 'girlfriend', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'women holding hands: medium skin tone, dark skin tone', + char: '\u{1F469}\u{1F3FD}\u{200D}\u{1F91D}\u{200D}\u{1F469}\u{1F3FF}', + shortName: 'women_holding_hands_tone3_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'lesbian', + 'gay', + 'women', + 'lgbt', + 'girls night', + 'friend', + 'human', + 'porn', + 'parent', + 'wife', + 'mom', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'woman', + 'female', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'ladies night', + 'girls only', + 'girlfriend', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'women holding hands: medium-dark skin tone, light skin tone', + char: '\u{1F469}\u{1F3FE}\u{200D}\u{1F91D}\u{200D}\u{1F469}\u{1F3FB}', + shortName: 'women_holding_hands_tone4_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'lesbian', + 'gay', + 'women', + 'lgbt', + 'girls night', + 'friend', + 'human', + 'porn', + 'parent', + 'wife', + 'mom', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'woman', + 'female', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'ladies night', + 'girls only', + 'girlfriend', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: + 'women holding hands: medium-dark skin tone, medium-light skin tone', + char: '\u{1F469}\u{1F3FE}\u{200D}\u{1F91D}\u{200D}\u{1F469}\u{1F3FC}', + shortName: 'women_holding_hands_tone4_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'lesbian', + 'gay', + 'women', + 'lgbt', + 'girls night', + 'friend', + 'human', + 'porn', + 'parent', + 'wife', + 'mom', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'woman', + 'female', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'ladies night', + 'girls only', + 'girlfriend', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'women holding hands: medium-dark skin tone, medium skin tone', + char: '\u{1F469}\u{1F3FE}\u{200D}\u{1F91D}\u{200D}\u{1F469}\u{1F3FD}', + shortName: 'women_holding_hands_tone4_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'lesbian', + 'gay', + 'women', + 'lgbt', + 'girls night', + 'friend', + 'human', + 'porn', + 'parent', + 'wife', + 'mom', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'woman', + 'female', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'ladies night', + 'girls only', + 'girlfriend', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'women holding hands: medium-dark skin tone', + char: '\u{1F46D}\u{1F3FE}', + shortName: 'women_holding_hands_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'lesbian', + 'gay', + 'women', + 'lgbt', + 'girls night', + 'friend', + 'human', + 'porn', + 'parent', + 'wife', + 'mom', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'woman', + 'female', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'ladies night', + 'girls only', + 'girlfriend', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'women holding hands: medium-dark skin tone, dark skin tone', + char: '\u{1F469}\u{1F3FE}\u{200D}\u{1F91D}\u{200D}\u{1F469}\u{1F3FF}', + shortName: 'women_holding_hands_tone4_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'lesbian', + 'gay', + 'women', + 'lgbt', + 'girls night', + 'friend', + 'human', + 'porn', + 'parent', + 'wife', + 'mom', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'woman', + 'female', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'ladies night', + 'girls only', + 'girlfriend', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'women holding hands: dark skin tone, light skin tone', + char: '\u{1F469}\u{1F3FF}\u{200D}\u{1F91D}\u{200D}\u{1F469}\u{1F3FB}', + shortName: 'women_holding_hands_tone5_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'lesbian', + 'gay', + 'women', + 'lgbt', + 'girls night', + 'friend', + 'human', + 'porn', + 'parent', + 'wife', + 'mom', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'woman', + 'female', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'ladies night', + 'girls only', + 'girlfriend', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'women holding hands: dark skin tone, medium-light skin tone', + char: '\u{1F469}\u{1F3FF}\u{200D}\u{1F91D}\u{200D}\u{1F469}\u{1F3FC}', + shortName: 'women_holding_hands_tone5_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'lesbian', + 'gay', + 'women', + 'lgbt', + 'girls night', + 'friend', + 'human', + 'porn', + 'parent', + 'wife', + 'mom', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'woman', + 'female', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'ladies night', + 'girls only', + 'girlfriend', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'women holding hands: dark skin tone, medium skin tone', + char: '\u{1F469}\u{1F3FF}\u{200D}\u{1F91D}\u{200D}\u{1F469}\u{1F3FD}', + shortName: 'women_holding_hands_tone5_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'lesbian', + 'gay', + 'women', + 'lgbt', + 'girls night', + 'friend', + 'human', + 'porn', + 'parent', + 'wife', + 'mom', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'woman', + 'female', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'ladies night', + 'girls only', + 'girlfriend', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'women holding hands: dark skin tone, medium-dark skin tone', + char: '\u{1F469}\u{1F3FF}\u{200D}\u{1F91D}\u{200D}\u{1F469}\u{1F3FE}', + shortName: 'women_holding_hands_tone5_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'lesbian', + 'gay', + 'women', + 'lgbt', + 'girls night', + 'friend', + 'human', + 'porn', + 'parent', + 'wife', + 'mom', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'woman', + 'female', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'ladies night', + 'girls only', + 'girlfriend', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'women holding hands: dark skin tone', + char: '\u{1F46D}\u{1F3FF}', + shortName: 'women_holding_hands_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'lesbian', + 'gay', + 'women', + 'lgbt', + 'girls night', + 'friend', + 'human', + 'porn', + 'parent', + 'wife', + 'mom', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'woman', + 'female', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'ladies night', + 'girls only', + 'girlfriend', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'gender', + 'people', + 'parents', + 'adult', + 'maman', + 'mommy', + 'mama', + 'mother' + ], + modifiable: true), + Emoji( + name: 'men holding hands', + char: '\u{1F46C}', + shortName: 'two_men_holding_hands', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'Gemini', + 'couple', + 'hand', + 'hold', + 'man', + 'twins', + 'zodiac', + 'uc6', + 'family', + 'diversity', + 'wedding', + 'gay', + 'men', + 'lgbt', + 'friend', + 'queen', + 'human', + 'daddy', + 'porn', + 'parent', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'king', + 'prince', + 'princess', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ]), + Emoji( + name: 'men holding hands: light skin tone', + char: '\u{1F46C}\u{1F3FB}', + shortName: 'men_holding_hands_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'gay', + 'men', + 'lgbt', + 'friend', + 'queen', + 'human', + 'daddy', + 'porn', + 'parent', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'king', + 'prince', + 'princess', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'men holding hands: light skin tone, medium-light skin tone', + char: '\u{1F468}\u{1F3FB}\u{200D}\u{1F91D}\u{200D}\u{1F468}\u{1F3FC}', + shortName: 'men_holding_hands_tone1_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'gay', + 'men', + 'lgbt', + 'friend', + 'queen', + 'human', + 'daddy', + 'porn', + 'parent', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'king', + 'prince', + 'princess', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'men holding hands: light skin tone, medium skin tone', + char: '\u{1F468}\u{1F3FB}\u{200D}\u{1F91D}\u{200D}\u{1F468}\u{1F3FD}', + shortName: 'men_holding_hands_tone1_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'gay', + 'men', + 'lgbt', + 'friend', + 'queen', + 'human', + 'daddy', + 'porn', + 'parent', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'king', + 'prince', + 'princess', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'men holding hands: light skin tone, medium-dark skin tone', + char: '\u{1F468}\u{1F3FB}\u{200D}\u{1F91D}\u{200D}\u{1F468}\u{1F3FE}', + shortName: 'men_holding_hands_tone1_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'gay', + 'men', + 'lgbt', + 'friend', + 'queen', + 'human', + 'daddy', + 'porn', + 'parent', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'king', + 'prince', + 'princess', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'men holding hands: light skin tone, dark skin tone', + char: '\u{1F468}\u{1F3FB}\u{200D}\u{1F91D}\u{200D}\u{1F468}\u{1F3FF}', + shortName: 'men_holding_hands_tone1_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'gay', + 'men', + 'lgbt', + 'friend', + 'queen', + 'human', + 'daddy', + 'porn', + 'parent', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'king', + 'prince', + 'princess', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'men holding hands: medium-light skin tone, light skin tone', + char: '\u{1F468}\u{1F3FC}\u{200D}\u{1F91D}\u{200D}\u{1F468}\u{1F3FB}', + shortName: 'men_holding_hands_tone2_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'gay', + 'men', + 'lgbt', + 'friend', + 'queen', + 'human', + 'daddy', + 'porn', + 'parent', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'king', + 'prince', + 'princess', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'men holding hands: medium-light skin tone', + char: '\u{1F46C}\u{1F3FC}', + shortName: 'men_holding_hands_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'gay', + 'men', + 'lgbt', + 'friend', + 'queen', + 'human', + 'daddy', + 'porn', + 'parent', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'king', + 'prince', + 'princess', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'men holding hands: medium-light skin tone, medium skin tone', + char: '\u{1F468}\u{1F3FC}\u{200D}\u{1F91D}\u{200D}\u{1F468}\u{1F3FD}', + shortName: 'men_holding_hands_tone2_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'gay', + 'men', + 'lgbt', + 'friend', + 'queen', + 'human', + 'daddy', + 'porn', + 'parent', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'king', + 'prince', + 'princess', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'men holding hands: medium-light skin tone, medium-dark skin tone', + char: '\u{1F468}\u{1F3FC}\u{200D}\u{1F91D}\u{200D}\u{1F468}\u{1F3FE}', + shortName: 'men_holding_hands_tone2_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'gay', + 'men', + 'lgbt', + 'friend', + 'queen', + 'human', + 'daddy', + 'porn', + 'parent', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'king', + 'prince', + 'princess', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'men holding hands: medium-light skin tone, dark skin tone', + char: '\u{1F468}\u{1F3FC}\u{200D}\u{1F91D}\u{200D}\u{1F468}\u{1F3FF}', + shortName: 'men_holding_hands_tone2_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'gay', + 'men', + 'lgbt', + 'friend', + 'queen', + 'human', + 'daddy', + 'porn', + 'parent', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'king', + 'prince', + 'princess', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'men holding hands: medium skin tone, light skin tone', + char: '\u{1F468}\u{1F3FD}\u{200D}\u{1F91D}\u{200D}\u{1F468}\u{1F3FB}', + shortName: 'men_holding_hands_tone3_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'gay', + 'men', + 'lgbt', + 'friend', + 'queen', + 'human', + 'daddy', + 'porn', + 'parent', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'king', + 'prince', + 'princess', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'men holding hands: medium skin tone, medium-light skin tone', + char: '\u{1F468}\u{1F3FD}\u{200D}\u{1F91D}\u{200D}\u{1F468}\u{1F3FC}', + shortName: 'men_holding_hands_tone3_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'gay', + 'men', + 'lgbt', + 'friend', + 'queen', + 'human', + 'daddy', + 'porn', + 'parent', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'king', + 'prince', + 'princess', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'men holding hands: medium skin tone', + char: '\u{1F46C}\u{1F3FD}', + shortName: 'men_holding_hands_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'gay', + 'men', + 'lgbt', + 'friend', + 'queen', + 'human', + 'daddy', + 'porn', + 'parent', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'king', + 'prince', + 'princess', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'men holding hands: medium skin tone, medium-dark skin tone', + char: '\u{1F468}\u{1F3FD}\u{200D}\u{1F91D}\u{200D}\u{1F468}\u{1F3FE}', + shortName: 'men_holding_hands_tone3_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'gay', + 'men', + 'lgbt', + 'friend', + 'queen', + 'human', + 'daddy', + 'porn', + 'parent', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'king', + 'prince', + 'princess', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'men holding hands: medium skin tone, dark skin tone', + char: '\u{1F468}\u{1F3FD}\u{200D}\u{1F91D}\u{200D}\u{1F468}\u{1F3FF}', + shortName: 'men_holding_hands_tone3_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'gay', + 'men', + 'lgbt', + 'friend', + 'queen', + 'human', + 'daddy', + 'porn', + 'parent', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'king', + 'prince', + 'princess', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'men holding hands: medium-dark skin tone, light skin tone', + char: '\u{1F468}\u{1F3FE}\u{200D}\u{1F91D}\u{200D}\u{1F468}\u{1F3FB}', + shortName: 'men_holding_hands_tone4_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'gay', + 'men', + 'lgbt', + 'friend', + 'queen', + 'human', + 'daddy', + 'porn', + 'parent', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'king', + 'prince', + 'princess', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'men holding hands: medium-dark skin tone, medium-light skin tone', + char: '\u{1F468}\u{1F3FE}\u{200D}\u{1F91D}\u{200D}\u{1F468}\u{1F3FC}', + shortName: 'men_holding_hands_tone4_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'gay', + 'men', + 'lgbt', + 'friend', + 'queen', + 'human', + 'daddy', + 'porn', + 'parent', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'king', + 'prince', + 'princess', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'men holding hands: medium-dark skin tone, medium skin tone', + char: '\u{1F468}\u{1F3FE}\u{200D}\u{1F91D}\u{200D}\u{1F468}\u{1F3FD}', + shortName: 'men_holding_hands_tone4_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'gay', + 'men', + 'lgbt', + 'friend', + 'queen', + 'human', + 'daddy', + 'porn', + 'parent', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'king', + 'prince', + 'princess', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'men holding hands: medium-dark skin tone', + char: '\u{1F46C}\u{1F3FE}', + shortName: 'men_holding_hands_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'gay', + 'men', + 'lgbt', + 'friend', + 'queen', + 'human', + 'daddy', + 'porn', + 'parent', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'king', + 'prince', + 'princess', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'men holding hands: medium-dark skin tone, dark skin tone', + char: '\u{1F468}\u{1F3FE}\u{200D}\u{1F91D}\u{200D}\u{1F468}\u{1F3FF}', + shortName: 'men_holding_hands_tone4_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'gay', + 'men', + 'lgbt', + 'friend', + 'queen', + 'human', + 'daddy', + 'porn', + 'parent', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'king', + 'prince', + 'princess', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'men holding hands: dark skin tone, light skin tone', + char: '\u{1F468}\u{1F3FF}\u{200D}\u{1F91D}\u{200D}\u{1F468}\u{1F3FB}', + shortName: 'men_holding_hands_tone5_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'gay', + 'men', + 'lgbt', + 'friend', + 'queen', + 'human', + 'daddy', + 'porn', + 'parent', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'king', + 'prince', + 'princess', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'men holding hands: dark skin tone, medium-light skin tone', + char: '\u{1F468}\u{1F3FF}\u{200D}\u{1F91D}\u{200D}\u{1F468}\u{1F3FC}', + shortName: 'men_holding_hands_tone5_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'gay', + 'men', + 'lgbt', + 'friend', + 'queen', + 'human', + 'daddy', + 'porn', + 'parent', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'king', + 'prince', + 'princess', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'men holding hands: dark skin tone, medium skin tone', + char: '\u{1F468}\u{1F3FF}\u{200D}\u{1F91D}\u{200D}\u{1F468}\u{1F3FD}', + shortName: 'men_holding_hands_tone5_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'gay', + 'men', + 'lgbt', + 'friend', + 'queen', + 'human', + 'daddy', + 'porn', + 'parent', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'king', + 'prince', + 'princess', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'men holding hands: dark skin tone, medium-dark skin tone', + char: '\u{1F468}\u{1F3FF}\u{200D}\u{1F91D}\u{200D}\u{1F468}\u{1F3FE}', + shortName: 'men_holding_hands_tone5_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'gay', + 'men', + 'lgbt', + 'friend', + 'queen', + 'human', + 'daddy', + 'porn', + 'parent', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'king', + 'prince', + 'princess', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'men holding hands: dark skin tone', + char: '\u{1F46C}\u{1F3FF}', + shortName: 'men_holding_hands_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'uc12', + 'family', + 'diversity', + 'wedding', + 'gay', + 'men', + 'lgbt', + 'friend', + 'queen', + 'human', + 'daddy', + 'porn', + 'parent', + 'husband', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'king', + 'prince', + 'princess', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult' + ], + modifiable: true), + Emoji( + name: 'couple with heart', + char: '\u{1F491}', + shortName: 'couple_with_heart', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'couple', + 'love', + 'uc6', + 'wedding', + 'lesbian', + 'gay', + 'men', + 'love', + 'sex', + 'lgbt', + 'pink', + 'husband', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'i love you', + 'te amo', + "je t'aime", + 'anniversary', + 'lovin', + 'amour', + 'aimer', + 'amor', + 'valentines day', + 'enamour', + 'lovey', + 'fuck', + 'fucking', + 'horny', + 'humping', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'rose' + ]), + Emoji( + name: 'couple with heart: woman, man', + char: '\u{1F469}\u{200D}\u{2764}\u{FE0F}\u{200D}\u{1F468}', + shortName: 'couple_with_heart_woman_man', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'couple', + 'love', + 'man', + 'woman', + 'uc6', + 'wedding', + 'love', + 'sex', + 'husband', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'i love you', + 'te amo', + "je t'aime", + 'anniversary', + 'lovin', + 'amour', + 'aimer', + 'amor', + 'valentines day', + 'enamour', + 'lovey', + 'fuck', + 'fucking', + 'horny', + 'humping' + ]), + Emoji( + name: 'couple with heart: woman, woman', + char: '\u{1F469}\u{200D}\u{2764}\u{FE0F}\u{200D}\u{1F469}', + shortName: 'couple_ww', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'couple', + 'love', + 'woman', + 'uc6', + 'wedding', + 'lesbian', + 'gay', + 'women', + 'love', + 'sex', + 'lgbt', + 'pink', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'woman', + 'female', + 'i love you', + 'te amo', + "je t'aime", + 'anniversary', + 'lovin', + 'amour', + 'aimer', + 'amor', + 'valentines day', + 'enamour', + 'lovey', + 'fuck', + 'fucking', + 'horny', + 'humping', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'rose' + ]), + Emoji( + name: 'couple with heart: man, man', + char: '\u{1F468}\u{200D}\u{2764}\u{FE0F}\u{200D}\u{1F468}', + shortName: 'couple_mm', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'couple', + 'love', + 'man', + 'uc6', + 'wedding', + 'gay', + 'men', + 'love', + 'sex', + 'lgbt', + 'pink', + 'husband', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'i love you', + 'te amo', + "je t'aime", + 'anniversary', + 'lovin', + 'amour', + 'aimer', + 'amor', + 'valentines day', + 'enamour', + 'lovey', + 'fuck', + 'fucking', + 'horny', + 'humping', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'rose' + ]), + Emoji( + name: 'kiss', + char: '\u{1F48F}', + shortName: 'couplekiss', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'couple', + 'uc6', + 'wedding', + 'lesbian', + 'gay', + 'men', + 'love', + 'sex', + 'hug', + 'lgbt', + 'pink', + 'kisses', + 'husband', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'i love you', + 'te amo', + "je t'aime", + 'anniversary', + 'lovin', + 'amour', + 'aimer', + 'amor', + 'valentines day', + 'enamour', + 'lovey', + 'fuck', + 'fucking', + 'horny', + 'humping', + 'embrace', + 'hugs', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'rose', + 'bisous', + 'beijos', + 'besos', + 'bise', + 'blowing kisses', + 'kissy' + ]), + Emoji( + name: 'kiss: woman, man', + char: + '\u{1F469}\u{200D}\u{2764}\u{FE0F}\u{200D}\u{1F48B}\u{200D}\u{1F468}', + shortName: 'kiss_woman_man', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'couple', + 'man', + 'woman', + 'uc6', + 'wedding', + 'love', + 'sex', + 'hug', + 'kisses', + 'husband', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'i love you', + 'te amo', + "je t'aime", + 'anniversary', + 'lovin', + 'amour', + 'aimer', + 'amor', + 'valentines day', + 'enamour', + 'lovey', + 'fuck', + 'fucking', + 'horny', + 'humping', + 'embrace', + 'hugs', + 'bisous', + 'beijos', + 'besos', + 'bise', + 'blowing kisses', + 'kissy' + ]), + Emoji( + name: 'kiss: woman, woman', + char: + '\u{1F469}\u{200D}\u{2764}\u{FE0F}\u{200D}\u{1F48B}\u{200D}\u{1F469}', + shortName: 'kiss_ww', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'couple', + 'woman', + 'uc6', + 'wedding', + 'lesbian', + 'gay', + 'women', + 'love', + 'sex', + 'hug', + 'lgbt', + 'pink', + 'kisses', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'woman', + 'female', + 'i love you', + 'te amo', + "je t'aime", + 'anniversary', + 'lovin', + 'amour', + 'aimer', + 'amor', + 'valentines day', + 'enamour', + 'lovey', + 'fuck', + 'fucking', + 'horny', + 'humping', + 'embrace', + 'hugs', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'rose', + 'bisous', + 'beijos', + 'besos', + 'bise', + 'blowing kisses', + 'kissy' + ]), + Emoji( + name: 'kiss: man, man', + char: + '\u{1F468}\u{200D}\u{2764}\u{FE0F}\u{200D}\u{1F48B}\u{200D}\u{1F468}', + shortName: 'kiss_mm', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'couple', + 'man', + 'uc6', + 'wedding', + 'gay', + 'men', + 'love', + 'sex', + 'hug', + 'lgbt', + 'pink', + 'kisses', + 'husband', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'twink', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'i love you', + 'te amo', + "je t'aime", + 'anniversary', + 'lovin', + 'amour', + 'aimer', + 'amor', + 'valentines day', + 'enamour', + 'lovey', + 'fuck', + 'fucking', + 'horny', + 'humping', + 'embrace', + 'hugs', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'rose', + 'bisous', + 'beijos', + 'besos', + 'bise', + 'blowing kisses', + 'kissy' + ]), + Emoji( + name: 'family', + char: '\u{1F46A}', + shortName: 'family', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'family', + 'uc6', + 'family', + 'lesbian', + 'gay', + 'men', + 'christmas', + 'lgbt', + 'human', + 'daddy', + 'parent', + 'wife', + 'husband', + 'child', + 'mom', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'twink', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'navidad', + 'xmas', + 'noel', + 'merry christmas', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant', + 'maman', + 'mommy', + 'mama', + 'mother' + ]), + Emoji( + name: 'family: man, woman, boy', + char: '\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F466}', + shortName: 'family_man_woman_boy', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'boy', + 'family', + 'man', + 'woman', + 'uc6', + 'family', + 'human', + 'daddy', + 'parent', + 'wife', + 'husband', + 'child', + 'mom', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant', + 'maman', + 'mommy', + 'mama', + 'mother' + ]), + Emoji( + name: 'family: man, woman, girl', + char: '\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F467}', + shortName: 'family_mwg', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'family', + 'girl', + 'man', + 'woman', + 'uc6', + 'family', + 'human', + 'daddy', + 'parent', + 'wife', + 'husband', + 'child', + 'mom', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant', + 'maman', + 'mommy', + 'mama', + 'mother' + ]), + Emoji( + name: 'family: man, woman, girl, boy', + char: '\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F467}\u{200D}\u{1F466}', + shortName: 'family_mwgb', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'boy', + 'family', + 'girl', + 'man', + 'woman', + 'uc6', + 'family', + 'human', + 'daddy', + 'parent', + 'wife', + 'husband', + 'child', + 'mom', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant', + 'maman', + 'mommy', + 'mama', + 'mother' + ]), + Emoji( + name: 'family: man, woman, boy, boy', + char: '\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F466}\u{200D}\u{1F466}', + shortName: 'family_mwbb', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'boy', + 'family', + 'man', + 'woman', + 'uc6', + 'family', + 'human', + 'daddy', + 'parent', + 'wife', + 'husband', + 'child', + 'mom', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant', + 'maman', + 'mommy', + 'mama', + 'mother' + ]), + Emoji( + name: 'family: man, woman, girl, girl', + char: '\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F467}\u{200D}\u{1F467}', + shortName: 'family_mwgg', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'family', + 'girl', + 'man', + 'woman', + 'uc6', + 'family', + 'human', + 'daddy', + 'parent', + 'wife', + 'husband', + 'child', + 'mom', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant', + 'maman', + 'mommy', + 'mama', + 'mother' + ]), + Emoji( + name: 'family: woman, woman, boy', + char: '\u{1F469}\u{200D}\u{1F469}\u{200D}\u{1F466}', + shortName: 'family_wwb', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'boy', + 'family', + 'woman', + 'uc6', + 'family', + 'lesbian', + 'gay', + 'women', + 'lgbt', + 'human', + 'parent', + 'wife', + 'child', + 'mom', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'twink', + 'woman', + 'female', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'gender', + 'people', + 'parents', + 'adult', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant', + 'maman', + 'mommy', + 'mama', + 'mother' + ]), + Emoji( + name: 'family: woman, woman, girl', + char: '\u{1F469}\u{200D}\u{1F469}\u{200D}\u{1F467}', + shortName: 'family_wwg', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'family', + 'girl', + 'woman', + 'uc6', + 'family', + 'lesbian', + 'gay', + 'women', + 'lgbt', + 'human', + 'parent', + 'wife', + 'child', + 'mom', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'twink', + 'woman', + 'female', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'gender', + 'people', + 'parents', + 'adult', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant', + 'maman', + 'mommy', + 'mama', + 'mother' + ]), + Emoji( + name: 'family: woman, woman, girl, boy', + char: '\u{1F469}\u{200D}\u{1F469}\u{200D}\u{1F467}\u{200D}\u{1F466}', + shortName: 'family_wwgb', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'boy', + 'family', + 'girl', + 'woman', + 'uc6', + 'family', + 'lesbian', + 'gay', + 'women', + 'lgbt', + 'human', + 'parent', + 'wife', + 'child', + 'mom', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'twink', + 'woman', + 'female', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'gender', + 'people', + 'parents', + 'adult', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant', + 'maman', + 'mommy', + 'mama', + 'mother' + ]), + Emoji( + name: 'family: woman, woman, boy, boy', + char: '\u{1F469}\u{200D}\u{1F469}\u{200D}\u{1F466}\u{200D}\u{1F466}', + shortName: 'family_wwbb', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'boy', + 'family', + 'woman', + 'uc6', + 'family', + 'lesbian', + 'gay', + 'women', + 'lgbt', + 'human', + 'parent', + 'wife', + 'child', + 'mom', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'twink', + 'woman', + 'female', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'gender', + 'people', + 'parents', + 'adult', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant', + 'maman', + 'mommy', + 'mama', + 'mother' + ]), + Emoji( + name: 'family: woman, woman, girl, girl', + char: '\u{1F469}\u{200D}\u{1F469}\u{200D}\u{1F467}\u{200D}\u{1F467}', + shortName: 'family_wwgg', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'family', + 'girl', + 'woman', + 'uc6', + 'family', + 'lesbian', + 'gay', + 'women', + 'lgbt', + 'human', + 'parent', + 'wife', + 'child', + 'mom', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'twink', + 'woman', + 'female', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'gender', + 'people', + 'parents', + 'adult', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant', + 'maman', + 'mommy', + 'mama', + 'mother' + ]), + Emoji( + name: 'family: man, man, boy', + char: '\u{1F468}\u{200D}\u{1F468}\u{200D}\u{1F466}', + shortName: 'family_mmb', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'boy', + 'family', + 'man', + 'uc6', + 'family', + 'gay', + 'men', + 'lgbt', + 'human', + 'daddy', + 'parent', + 'husband', + 'child', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'twink', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant' + ]), + Emoji( + name: 'family: man, man, girl', + char: '\u{1F468}\u{200D}\u{1F468}\u{200D}\u{1F467}', + shortName: 'family_mmg', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'family', + 'girl', + 'man', + 'uc6', + 'family', + 'gay', + 'men', + 'lgbt', + 'human', + 'daddy', + 'parent', + 'husband', + 'child', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'twink', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant' + ]), + Emoji( + name: 'family: man, man, girl, boy', + char: '\u{1F468}\u{200D}\u{1F468}\u{200D}\u{1F467}\u{200D}\u{1F466}', + shortName: 'family_mmgb', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'boy', + 'family', + 'girl', + 'man', + 'uc6', + 'family', + 'gay', + 'men', + 'lgbt', + 'human', + 'daddy', + 'parent', + 'husband', + 'child', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'twink', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant' + ]), + Emoji( + name: 'family: man, man, boy, boy', + char: '\u{1F468}\u{200D}\u{1F468}\u{200D}\u{1F466}\u{200D}\u{1F466}', + shortName: 'family_mmbb', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'boy', + 'family', + 'man', + 'uc6', + 'family', + 'gay', + 'men', + 'lgbt', + 'human', + 'daddy', + 'parent', + 'husband', + 'child', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'twink', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant' + ]), + Emoji( + name: 'family: man, man, girl, girl', + char: '\u{1F468}\u{200D}\u{1F468}\u{200D}\u{1F467}\u{200D}\u{1F467}', + shortName: 'family_mmgg', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'family', + 'girl', + 'man', + 'uc6', + 'family', + 'gay', + 'men', + 'lgbt', + 'human', + 'daddy', + 'parent', + 'husband', + 'child', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'twink', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant' + ]), + Emoji( + name: 'family: woman, boy', + char: '\u{1F469}\u{200D}\u{1F466}', + shortName: 'family_woman_boy', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'boy', + 'family', + 'woman', + 'uc6', + 'family', + 'women', + 'human', + 'parent', + 'wife', + 'child', + 'mom', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'woman', + 'female', + 'gender', + 'people', + 'parents', + 'adult', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant', + 'maman', + 'mommy', + 'mama', + 'mother' + ]), + Emoji( + name: 'family: woman, girl', + char: '\u{1F469}\u{200D}\u{1F467}', + shortName: 'family_woman_girl', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'family', + 'girl', + 'woman', + 'uc6', + 'family', + 'women', + 'human', + 'parent', + 'wife', + 'child', + 'mom', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'woman', + 'female', + 'gender', + 'people', + 'parents', + 'adult', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant', + 'maman', + 'mommy', + 'mama', + 'mother' + ]), + Emoji( + name: 'family: woman, girl, boy', + char: '\u{1F469}\u{200D}\u{1F467}\u{200D}\u{1F466}', + shortName: 'family_woman_girl_boy', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'boy', + 'family', + 'girl', + 'woman', + 'uc6', + 'family', + 'women', + 'human', + 'parent', + 'wife', + 'child', + 'mom', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'woman', + 'female', + 'gender', + 'people', + 'parents', + 'adult', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant', + 'maman', + 'mommy', + 'mama', + 'mother' + ]), + Emoji( + name: 'family: woman, boy, boy', + char: '\u{1F469}\u{200D}\u{1F466}\u{200D}\u{1F466}', + shortName: 'family_woman_boy_boy', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'boy', + 'family', + 'woman', + 'uc6', + 'family', + 'women', + 'human', + 'parent', + 'wife', + 'child', + 'mom', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'woman', + 'female', + 'gender', + 'people', + 'parents', + 'adult', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant', + 'maman', + 'mommy', + 'mama', + 'mother' + ]), + Emoji( + name: 'family: woman, girl, girl', + char: '\u{1F469}\u{200D}\u{1F467}\u{200D}\u{1F467}', + shortName: 'family_woman_girl_girl', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'family', + 'girl', + 'woman', + 'uc6', + 'family', + 'women', + 'human', + 'parent', + 'wife', + 'child', + 'mom', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'woman', + 'female', + 'gender', + 'people', + 'parents', + 'adult', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant', + 'maman', + 'mommy', + 'mama', + 'mother' + ]), + Emoji( + name: 'family: man, boy', + char: '\u{1F468}\u{200D}\u{1F466}', + shortName: 'family_man_boy', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'boy', + 'family', + 'man', + 'uc6', + 'family', + 'men', + 'human', + 'daddy', + 'parent', + 'husband', + 'child', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant' + ]), + Emoji( + name: 'family: man, girl', + char: '\u{1F468}\u{200D}\u{1F467}', + shortName: 'family_man_girl', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'family', + 'girl', + 'man', + 'uc6', + 'family', + 'men', + 'human', + 'daddy', + 'parent', + 'husband', + 'child', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant' + ]), + Emoji( + name: 'family: man, girl, boy', + char: '\u{1F468}\u{200D}\u{1F467}\u{200D}\u{1F466}', + shortName: 'family_man_girl_boy', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'boy', + 'family', + 'girl', + 'man', + 'uc6', + 'family', + 'men', + 'human', + 'daddy', + 'parent', + 'husband', + 'child', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant' + ]), + Emoji( + name: 'family: man, boy, boy', + char: '\u{1F468}\u{200D}\u{1F466}\u{200D}\u{1F466}', + shortName: 'family_man_boy_boy', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'boy', + 'family', + 'man', + 'uc6', + 'family', + 'men', + 'human', + 'daddy', + 'parent', + 'husband', + 'child', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant' + ]), + Emoji( + name: 'family: man, girl, girl', + char: '\u{1F468}\u{200D}\u{1F467}\u{200D}\u{1F467}', + shortName: 'family_man_girl_girl', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.family, + keywords: [ + 'family', + 'girl', + 'man', + 'uc6', + 'family', + 'men', + 'human', + 'daddy', + 'parent', + 'husband', + 'child', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'gender', + 'people', + 'dad', + 'papa', + 'pere', + 'father', + 'parents', + 'adult', + 'children', + 'girl', + 'boy', + 'kids', + 'niño', + 'enfant' + ]), + Emoji( + name: 'yarn', + char: '\u{1F9F6}', + shortName: 'yarn', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.artsCrafts, + keywords: [ + 'uc11', + 'cat', + 'household', + 'sew', + 'kitty', + 'kitten', + 'cats', + 'kittens', + 'kitties', + 'feline', + 'felines', + 'cat face', + 'gato', + 'meow', + 'knit', + 'embroider', + 'stitch', + 'repair', + 'crochet', + 'alter', + 'seamstress', + 'fix' + ]), + Emoji( + name: 'thread', + char: '\u{1F9F5}', + shortName: 'thread', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.artsCrafts, + keywords: [ + 'uc11', + 'household', + 'sew', + 'knit', + 'embroider', + 'stitch', + 'repair', + 'crochet', + 'alter', + 'seamstress', + 'fix' + ]), + Emoji( + name: 'coat', + char: '\u{1F9E5}', + shortName: 'coat', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.clothing, + keywords: [ + 'jacket', + 'uc10', + 'fashion', + 'winter', + 'cold', + 'jacket', + 'clothes', + 'clothing', + 'style', + 'chilly', + 'chilled', + 'brisk', + 'freezing', + 'frostbite', + 'icicles', + 'veste' + ]), + Emoji( + name: 'lab coat', + char: '\u{1F97C}', + shortName: 'lab_coat', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.clothing, + keywords: ['uc11', 'science', 'jacket', 'medical', 'lab', 'veste']), + Emoji( + name: 'safety vest', + char: '\u{1F9BA}', + shortName: 'safety_vest', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.clothing, + keywords: [ + 'uc12', + '911', + 'jacket', + 'construction', + 'emergency', + 'injury', + 'veste' + ]), + Emoji( + name: 'woman’s clothes', + char: '\u{1F45A}', + shortName: 'womans_clothes', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.clothing, + keywords: [ + 'clothing', + 'woman', + 'uc6', + 'fashion', + 'women', + 'pink', + 'mom', + 'clothes', + 'clothing', + 'style', + 'woman', + 'female', + 'rose', + 'maman', + 'mommy', + 'mama', + 'mother' + ]), + Emoji( + name: 't-shirt', + char: '\u{1F455}', + shortName: 'shirt', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.clothing, + keywords: [ + 'clothing', + 'shirt', + 'tshirt', + 'uc6', + 'fashion', + 'men', + 'clothes', + 'clothing', + 'style', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male' + ]), + Emoji( + name: 'jeans', + char: '\u{1F456}', + shortName: 'jeans', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.clothing, + keywords: [ + 'clothing', + 'pants', + 'trousers', + 'uc6', + 'fashion', + 'men', + 'pants', + 'clothes', + 'clothing', + 'style', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'pant', + 'levis', + 'slacks' + ]), + Emoji( + name: 'briefs', + char: '\u{1FA72}', + shortName: 'briefs', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.clothing, + keywords: [ + 'uc12', + 'fashion', + 'men', + 'underwear', + 'clothes', + 'clothing', + 'style', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'undergarments', + 'boxers', + 'panties', + 'boy shorts', + 'panty', + 'biancheria intima', + 'sous-vêtements', + 'ropa interior', + 'speedos' + ]), + Emoji( + name: 'shorts', + char: '\u{1FA73}', + shortName: 'shorts', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.clothing, + keywords: [ + 'uc12', + 'fashion', + 'vacation', + 'swim', + 'beach', + 'scuba', + 'pantalones cortos', + 'clothes', + 'clothing', + 'style', + 'swimming', + 'swimmer', + 'snorkel', + 'kurze Hose', + 'pantaloncini' + ]), + Emoji( + name: 'necktie', + char: '\u{1F454}', + shortName: 'necktie', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.clothing, + keywords: [ + 'clothing', + 'uc6', + 'fashion', + 'men', + 'accessories', + 'business', + 'clothes', + 'clothing', + 'style', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male' + ]), + Emoji( + name: 'dress', + char: '\u{1F457}', + shortName: 'dress', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.clothing, + keywords: [ + 'clothing', + 'uc6', + 'fashion', + 'women', + 'sexy', + 'beautiful', + 'girls night', + 'pink', + 'vintage', + 'florida', + 'mom', + 'dress', + 'clothes', + 'clothing', + 'style', + 'woman', + 'female', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'ladies night', + 'girls only', + 'girlfriend', + 'rose', + 'maman', + 'mommy', + 'mama', + 'mother' + ]), + Emoji( + name: 'bikini', + char: '\u{1F459}', + shortName: 'bikini', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.clothing, + keywords: [ + 'clothing', + 'swim', + 'uc6', + 'fashion', + 'women', + 'sexy', + 'tropical', + 'vacation', + 'swim', + 'beach', + 'hawaii', + 'california', + 'florida', + 'las vegas', + 'summer', + 'binoculars', + 'clothes', + 'clothing', + 'style', + 'woman', + 'female', + 'swimming', + 'swimmer', + 'aloha', + 'kawaii', + 'maui', + 'moana', + 'vegas', + 'weekend' + ]), + Emoji( + name: 'one-piece swimsuit', + char: '\u{1FA71}', + shortName: 'one_piece_swimsuit', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.clothing, + keywords: [ + 'uc12', + 'fashion', + 'tropical', + 'vacation', + 'swim', + 'beach', + 'hawaii', + 'california', + 'florida', + 'scuba', + 'summer', + 'clothes', + 'clothing', + 'style', + 'swimming', + 'swimmer', + 'aloha', + 'kawaii', + 'maui', + 'moana', + 'snorkel', + 'weekend' + ]), + Emoji( + name: 'kimono', + char: '\u{1F458}', + shortName: 'kimono', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.clothing, + keywords: [ + 'clothing', + 'uc6', + 'fashion', + 'japan', + 'pink', + 'dress', + 'clothes', + 'clothing', + 'style', + 'japanese', + 'ninja', + 'rose' + ]), + Emoji( + name: 'sari', + char: '\u{1F97B}', + shortName: 'sari', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.clothing, + keywords: [ + 'uc12', + 'fashion', + 'saree', + 'dress', + 'clothes', + 'clothing', + 'style', + 'shari', + 'nivi', + 'choli', + 'ravike', + 'cholo', + 'parkar', + 'ul-pavadai' + ]), + Emoji( + name: 'flat shoe', + char: '\u{1F97F}', + shortName: 'womans_flat_shoe', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.clothing, + keywords: [ + 'uc11', + 'fashion', + 'women', + 'shoe', + 'accessories', + 'pink', + 'mom', + 'clothes', + 'clothing', + 'style', + 'woman', + 'female', + 'shoes', + 'baskets', + 'loafers', + 'sandals', + 'pumps', + 'boots', + 'heels', + 'rose', + 'maman', + 'mommy', + 'mama', + 'mother' + ]), + Emoji( + name: 'high-heeled shoe', + char: '\u{1F460}', + shortName: 'high_heel', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.clothing, + keywords: [ + 'clothing', + 'heel', + 'shoe', + 'woman', + 'uc6', + 'fashion', + 'women', + 'shoe', + 'sexy', + 'accessories', + 'girls night', + 'california', + 'las vegas', + 'rich', + 'mom', + 'clothes', + 'clothing', + 'style', + 'woman', + 'female', + 'shoes', + 'baskets', + 'loafers', + 'sandals', + 'pumps', + 'boots', + 'heels', + 'ladies night', + 'girls only', + 'girlfriend', + 'vegas', + 'grand', + 'expensive', + 'fancy', + 'maman', + 'mommy', + 'mama', + 'mother' + ]), + Emoji( + name: 'woman’s sandal', + char: '\u{1F461}', + shortName: 'sandal', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.clothing, + keywords: [ + 'clothing', + 'sandal', + 'shoe', + 'woman', + 'uc6', + 'fashion', + 'women', + 'shoe', + 'accessories', + 'pink', + 'summer', + 'mom', + 'clothes', + 'clothing', + 'style', + 'woman', + 'female', + 'shoes', + 'baskets', + 'loafers', + 'sandals', + 'pumps', + 'boots', + 'heels', + 'rose', + 'weekend', + 'maman', + 'mommy', + 'mama', + 'mother' + ]), + Emoji( + name: 'woman’s boot', + char: '\u{1F462}', + shortName: 'boot', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.clothing, + keywords: [ + 'boot', + 'clothing', + 'shoe', + 'woman', + 'uc6', + 'fashion', + 'women', + 'shoe', + 'sexy', + 'accessories', + 'vintage', + 'rich', + 'clothes', + 'clothing', + 'style', + 'woman', + 'female', + 'shoes', + 'baskets', + 'loafers', + 'sandals', + 'pumps', + 'boots', + 'heels', + 'grand', + 'expensive', + 'fancy' + ]), + Emoji( + name: 'man’s shoe', + char: '\u{1F45E}', + shortName: 'mans_shoe', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.clothing, + keywords: [ + 'clothing', + 'man', + 'shoe', + 'uc6', + 'fashion', + 'shoe', + 'men', + 'accessories', + 'vintage', + 'clothes', + 'clothing', + 'style', + 'shoes', + 'baskets', + 'loafers', + 'sandals', + 'pumps', + 'boots', + 'heels', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male' + ]), + Emoji( + name: 'running shoe', + char: '\u{1F45F}', + shortName: 'athletic_shoe', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.clothing, + keywords: [ + 'athletic', + 'clothing', + 'shoe', + 'sneaker', + 'uc6', + 'sport', + 'fashion', + 'shoe', + 'accessories', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'clothes', + 'clothing', + 'style', + 'shoes', + 'baskets', + 'loafers', + 'sandals', + 'pumps', + 'boots', + 'heels' + ]), + Emoji( + name: 'hiking boot', + char: '\u{1F97E}', + shortName: 'hiking_boot', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.clothing, + keywords: [ + 'uc11', + 'shoe', + 'accessories', + 'mountain', + 'activity', + 'rock climbing', + 'shoes', + 'baskets', + 'loafers', + 'sandals', + 'pumps', + 'boots', + 'heels', + 'climber' + ]), + Emoji( + name: 'thong sandal', + char: '\u{1FA74}', + shortName: 'thong_sandal', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.clothing, + keywords: [ + 'uc13', + 'tropical', + 'beach', + 'hawaii', + 'flip flop', + 'california', + 'florida', + 'summer', + 'aloha', + 'kawaii', + 'maui', + 'moana', + 'weekend' + ]), + Emoji( + name: 'socks', + char: '\u{1F9E6}', + shortName: 'socks', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.clothing, + keywords: [ + 'stocking', + 'uc10', + 'fashion', + 'winter', + 'cold', + 'accessories', + 'clothes', + 'clothing', + 'style', + 'chilly', + 'chilled', + 'brisk', + 'freezing', + 'frostbite', + 'icicles' + ]), + Emoji( + name: 'gloves', + char: '\u{1F9E4}', + shortName: 'gloves', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.clothing, + keywords: [ + 'hand', + 'uc10', + 'fashion', + 'winter', + 'cold', + 'accessories', + 'gloves', + 'mittins', + 'clothes', + 'clothing', + 'style', + 'chilly', + 'chilled', + 'brisk', + 'freezing', + 'frostbite', + 'icicles', + 'muff' + ]), + Emoji( + name: 'scarf', + char: '\u{1F9E3}', + shortName: 'scarf', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.clothing, + keywords: [ + 'neck', + 'uc10', + 'fashion', + 'winter', + 'cold', + 'accessories', + 'clothes', + 'clothing', + 'style', + 'chilly', + 'chilled', + 'brisk', + 'freezing', + 'frostbite', + 'icicles' + ]), + Emoji( + name: 'top hat', + char: '\u{1F3A9}', + shortName: 'tophat', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.clothing, + keywords: [ + 'clothing', + 'hat', + 'top', + 'tophat', + 'uc6', + 'fashion', + 'wedding', + 'hat', + 'men', + 'accessories', + 'magic', + 'vintage', + 'rich', + 'clothes', + 'clothing', + 'style', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'hats', + 'cap', + 'caps', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'spell', + 'genie', + 'magical', + 'grand', + 'expensive', + 'fancy' + ]), + Emoji( + name: 'billed cap', + char: '\u{1F9E2}', + shortName: 'billed_cap', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.clothing, + keywords: [ + 'baseball cap', + 'uc10', + 'fashion', + 'hat', + 'accessories', + 'clothes', + 'clothing', + 'style', + 'hats', + 'cap', + 'caps' + ]), + Emoji( + name: 'woman’s hat', + char: '\u{1F452}', + shortName: 'womans_hat', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.clothing, + keywords: [ + 'clothing', + 'hat', + 'woman', + 'uc6', + 'fashion', + 'hat', + 'women', + 'accessories', + 'pink', + 'vintage', + 'easter', + 'rich', + 'clothes', + 'clothing', + 'style', + 'hats', + 'cap', + 'caps', + 'woman', + 'female', + 'rose', + 'grand', + 'expensive', + 'fancy' + ]), + Emoji( + name: 'graduation cap', + char: '\u{1F393}', + shortName: 'mortar_board', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.clothing, + keywords: [ + 'cap', + 'celebration', + 'clothing', + 'graduation', + 'hat', + 'uc6', + 'hat', + 'classroom', + 'accessories', + 'graduate', + 'hats', + 'cap', + 'caps', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning' + ]), + Emoji( + name: 'rescue worker’s helmet', + char: '\u{26D1}\u{FE0F}', + shortName: 'helmet_with_cross', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.clothing, + keywords: [ + 'aid', + 'cross', + 'face', + 'hat', + 'helmet', + 'uc5', + 'hat', + 'accessories', + 'job', + '911', + 'help', + 'helmet', + 'hats', + 'cap', + 'caps', + 'profession', + 'boss', + 'career', + 'emergency', + 'injury' + ]), + Emoji( + name: 'military helmet', + char: '\u{1FA96}', + shortName: 'military_helmet', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.clothing, + keywords: [ + 'uc13', + 'hat', + 'soldier', + 'helmet', + 'army', + 'hats', + 'cap', + 'caps' + ]), + Emoji( + name: 'crown', + char: '\u{1F451}', + shortName: 'crown', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.clothing, + keywords: [ + 'clothing', + 'king', + 'queen', + 'uc6', + 'accessories', + 'power', + 'queen', + 'england', + 'bling', + 'fame', + 'crown', + 'rich', + 'king', + 'prince', + 'princess', + 'united kingdom', + 'london', + 'uk', + 'jewels', + 'gems', + 'jewel', + 'jewelry', + 'treasure', + 'famous', + 'celebrity', + 'tiara', + 'grand', + 'expensive', + 'fancy' + ]), + Emoji( + name: 'ring', + char: '\u{1F48D}', + shortName: 'ring', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.clothing, + keywords: [ + 'diamond', + 'uc6', + 'wedding', + 'accessories', + 'girls night', + 'trap', + 'vintage', + 'bling', + 'diamond', + 'rich', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'ladies night', + 'girls only', + 'girlfriend', + 'jewels', + 'gems', + 'jewel', + 'jewelry', + 'treasure', + 'grand', + 'expensive', + 'fancy' + ]), + Emoji( + name: 'clutch bag', + char: '\u{1F45D}', + shortName: 'pouch', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.clothing, + keywords: [ + 'bag', + 'clothing', + 'pouch', + 'uc6', + 'fashion', + 'women', + 'bag', + 'accessories', + 'mom', + 'clothes', + 'clothing', + 'style', + 'woman', + 'female', + 'swag', + 'maman', + 'mommy', + 'mama', + 'mother' + ]), + Emoji( + name: 'purse', + char: '\u{1F45B}', + shortName: 'purse', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.clothing, + keywords: [ + 'clothing', + 'coin', + 'uc6', + 'fashion', + 'women', + 'bag', + 'money', + 'accessories', + 'pink', + 'vintage', + 'mom', + 'clothes', + 'clothing', + 'style', + 'woman', + 'female', + 'swag', + 'cash', + 'dollars', + 'dollar', + 'bucks', + 'currency', + 'funds', + 'payment', + 'money face', + 'reward', + 'thief', + 'bank', + 'benjamins', + 'argent', + 'dinero', + 'i soldi', + 'Geld', + 'rose', + 'maman', + 'mommy', + 'mama', + 'mother' + ]), + Emoji( + name: 'handbag', + char: '\u{1F45C}', + shortName: 'handbag', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.clothing, + keywords: [ + 'bag', + 'clothing', + 'purse', + 'uc6', + 'fashion', + 'women', + 'bag', + 'vacation', + 'accessories', + 'rich', + 'mom', + 'work', + 'clothes', + 'clothing', + 'style', + 'woman', + 'female', + 'swag', + 'grand', + 'expensive', + 'fancy', + 'maman', + 'mommy', + 'mama', + 'mother', + 'office' + ]), + Emoji( + name: 'briefcase', + char: '\u{1F4BC}', + shortName: 'briefcase', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.office, + keywords: [ + 'briefcase', + 'uc6', + 'fashion', + 'bag', + 'men', + 'classroom', + 'accessories', + 'nutcase', + 'job', + 'business', + 'rich', + 'work', + 'clothes', + 'clothing', + 'style', + 'swag', + 'man', + 'guy', + 'guys', + 'gentleman', + 'male', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'profession', + 'boss', + 'career', + 'grand', + 'expensive', + 'fancy', + 'office' + ]), + Emoji( + name: 'backpack', + char: '\u{1F392}', + shortName: 'school_satchel', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.clothing, + keywords: [ + 'bag', + 'satchel', + 'school', + 'uc6', + 'fashion', + 'bag', + 'classroom', + 'vacation', + 'accessories', + 'backpack', + 'suitcase', + 'clothes', + 'clothing', + 'style', + 'swag', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'carry-on' + ]), + Emoji( + name: 'luggage', + char: '\u{1F9F3}', + shortName: 'luggage', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.hotel, + keywords: [ + 'uc11', + 'bag', + 'travel', + 'vacation', + 'suitcase', + 'household', + 'swag', + 'carry-on' + ]), + Emoji( + name: 'glasses', + char: '\u{1F453}', + shortName: 'eyeglasses', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.clothing, + keywords: [ + 'clothing', + 'eye', + 'eyeglasses', + 'eyewear', + 'uc6', + 'fashion', + 'glasses', + 'accessories', + 'harry potter', + 'detective', + 'clothes', + 'clothing', + 'style', + 'eyeglasses', + 'eye glasses' + ]), + Emoji( + name: 'sunglasses', + char: '\u{1F576}\u{FE0F}', + shortName: 'dark_sunglasses', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.clothing, + keywords: [ + 'dark', + 'eye', + 'eyewear', + 'glasses', + 'uc7', + 'fashion', + 'glasses', + 'accessories', + 'awesome', + 'beautiful', + 'sunglasses', + 'hawaii', + 'california', + 'florida', + 'las vegas', + 'summer', + 'clothes', + 'clothing', + 'style', + 'eyeglasses', + 'eye glasses', + 'okay', + 'got it', + 'cool', + 'ok', + 'will do', + 'like', + 'bien', + 'yep', + 'yup', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'shades', + 'lunettes de soleil', + 'sun glasses', + 'aloha', + 'kawaii', + 'maui', + 'moana', + 'vegas', + 'weekend' + ]), + Emoji( + name: 'goggles', + char: '\u{1F97D}', + shortName: 'goggles', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.clothing, + keywords: [ + 'uc11', + 'glasses', + 'science', + 'accessories', + 'medical', + 'eyeglasses', + 'eye glasses', + 'lab' + ]), + Emoji( + name: 'closed umbrella', + char: '\u{1F302}', + shortName: 'closed_umbrella', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.skyWeather, + keywords: [ + 'clothing', + 'rain', + 'umbrella', + 'uc6', + 'sky', + 'rain', + 'accessories', + 'umbrella', + 'england', + 'cane', + 'united kingdom', + 'london', + 'uk' + ]), + Emoji( + name: 'curly hair', + char: '\u{1F9B1}', + shortName: 'curly_haired', + emojiGroup: EmojiGroup.component, + emojiSubgroup: EmojiSubgroup.hairStyle, + keywords: [ + 'uc11', + 'diversity', + 'body', + 'hair', + 'afro', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + "'fro", + 'curls', + 'frizzy', + 'perm' + ]), + Emoji( + name: 'red hair', + char: '\u{1F9B0}', + shortName: 'red_haired', + emojiGroup: EmojiGroup.component, + emojiSubgroup: EmojiSubgroup.hairStyle, + keywords: [ + 'uc11', + 'diversity', + 'body', + 'hair', + 'ginger', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy' + ]), + Emoji( + name: 'white hair', + char: '\u{1F9B3}', + shortName: 'white_haired', + emojiGroup: EmojiGroup.component, + emojiSubgroup: EmojiSubgroup.hairStyle, + keywords: [ + 'uc11', + 'old people', + 'diversity', + 'body', + 'hair', + 'grey hair', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'silver hair' + ]), + Emoji( + name: 'bald', + char: '\u{1F9B2}', + shortName: 'bald', + emojiGroup: EmojiGroup.component, + emojiSubgroup: EmojiSubgroup.hairStyle, + keywords: [ + 'uc11', + 'old people', + 'diversity', + 'body', + 'shaved head', + 'hairless', + 'grandparents', + 'elderly', + 'grandma', + 'grandpa', + 'grandmother', + 'grandfather', + 'mamie', + 'papy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'body part', + 'anatomy', + 'balding' + ]), + Emoji( + name: 'dog face', + char: '\u{1F436}', + shortName: 'dog', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: [ + 'dog', + 'face', + 'pet', + 'uc6', + 'animal', + 'dog', + 'pug', + 'harry potter', + 'bingo', + 'bitch', + 'pets', + 'pokemon', + 'minecraft', + 'animals', + 'animal kingdom', + 'puppy', + 'doggy', + 'memes', + 'dogs', + 'perro', + 'puppies', + 'chien', + 'pugs', + 'puta', + 'pute' + ]), + Emoji( + name: 'cat face', + char: '\u{1F431}', + shortName: 'cat', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: [ + 'cat', + 'face', + 'pet', + 'uc6', + 'animal', + 'halloween', + 'cat', + 'vagina', + 'pussy', + 'glitter', + 'pets', + 'pokemon', + 'porn', + 'animals', + 'animal kingdom', + 'samhain', + 'kitty', + 'kitten', + 'cats', + 'kittens', + 'kitties', + 'feline', + 'felines', + 'cat face', + 'gato', + 'meow', + 'condom' + ]), + Emoji( + name: 'mouse face', + char: '\u{1F42D}', + shortName: 'mouse', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: [ + 'face', + 'mouse', + 'uc6', + 'animal', + 'mickey', + 'disney', + 'pokemon', + 'rodent', + 'animals', + 'animal kingdom', + 'cartoon' + ]), + Emoji( + name: 'hamster', + char: '\u{1F439}', + shortName: 'hamster', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: [ + 'face', + 'hamster', + 'pet', + 'uc6', + 'animal', + 'pets', + 'rodent', + 'animals', + 'animal kingdom' + ]), + Emoji( + name: 'rabbit face', + char: '\u{1F430}', + shortName: 'rabbit', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: [ + 'bunny', + 'face', + 'pet', + 'rabbit', + 'uc6', + 'animal', + 'wildlife', + 'magic', + 'easter', + 'pets', + 'pokemon', + 'playboy', + 'animals', + 'animal kingdom', + 'spell', + 'genie', + 'magical', + 'play boy' + ]), + Emoji( + name: 'fox', + char: '\u{1F98A}', + shortName: 'fox', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: [ + 'face', + 'fox', + 'uc9', + 'animal', + 'wildlife', + 'forest', + 'animals', + 'animal kingdom', + 'rainforest' + ]), + Emoji( + name: 'bear', + char: '\u{1F43B}', + shortName: 'bear', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: [ + 'bear', + 'face', + 'uc6', + 'animal', + 'wildlife', + 'roar', + 'gummy', + 'california', + 'forest', + 'animals', + 'animal kingdom', + 'rawr', + 'rainforest' + ]), + Emoji( + name: 'panda', + char: '\u{1F43C}', + shortName: 'panda_face', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: [ + 'face', + 'panda', + 'uc6', + 'animal', + 'wildlife', + 'roar', + 'chinese', + 'forest', + 'animals', + 'animal kingdom', + 'rawr', + 'chinois', + 'asian', + 'chine', + 'rainforest' + ]), + Emoji( + name: 'polar bear', + char: '\u{1F43B}\u{200D}\u{2744}\u{FE0F}', + shortName: 'polar_bear', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: ['uc13', 'animal', 'polar bear', 'animals', 'animal kingdom']), + Emoji( + name: 'koala', + char: '\u{1F428}', + shortName: 'koala', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: [ + 'bear', + 'uc6', + 'animal', + 'wildlife', + 'australia', + 'forest', + 'marsupial', + 'animals', + 'animal kingdom', + 'rainforest', + 'marsupials' + ]), + Emoji( + name: 'tiger face', + char: '\u{1F42F}', + shortName: 'tiger', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: [ + 'face', + 'tiger', + 'uc6', + 'animal', + 'wildlife', + 'roar', + 'cat', + 'forest', + 'animals', + 'animal kingdom', + 'rawr', + 'kitty', + 'kitten', + 'cats', + 'kittens', + 'kitties', + 'feline', + 'felines', + 'cat face', + 'gato', + 'meow', + 'rainforest' + ]), + Emoji( + name: 'lion', + char: '\u{1F981}', + shortName: 'lion_face', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: [ + 'Leo', + 'face', + 'lion', + 'zodiac', + 'uc8', + 'animal', + 'wildlife', + 'roar', + 'cat', + 'england', + 'forest', + 'animals', + 'animal kingdom', + 'rawr', + 'kitty', + 'kitten', + 'cats', + 'kittens', + 'kitties', + 'feline', + 'felines', + 'cat face', + 'gato', + 'meow', + 'united kingdom', + 'london', + 'uk', + 'rainforest' + ]), + Emoji( + name: 'cow face', + char: '\u{1F42E}', + shortName: 'cow', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: [ + 'cow', + 'face', + 'uc6', + 'animal', + 'farm', + 'texas', + 'minecraft', + 'animals', + 'animal kingdom' + ]), + Emoji( + name: 'pig face', + char: '\u{1F437}', + shortName: 'pig', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: [ + 'face', + 'pig', + 'uc6', + 'animal', + 'pig', + 'farm', + 'guinea pig', + 'pets', + 'minecraft', + 'animals', + 'animal kingdom', + 'pork' + ]), + Emoji( + name: 'pig nose', + char: '\u{1F43D}', + shortName: 'pig_nose', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: [ + 'face', + 'nose', + 'pig', + 'uc6', + 'animal', + 'pig', + 'guinea pig', + 'pets', + 'animals', + 'animal kingdom', + 'pork' + ]), + Emoji( + name: 'frog', + char: '\u{1F438}', + shortName: 'frog', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalAmphibian, + keywords: [ + 'face', + 'frog', + 'uc6', + 'animal', + 'wildlife', + 'forest', + 'pets', + 'pokemon', + 'river', + 'animals', + 'animal kingdom', + 'rainforest' + ]), + Emoji( + name: 'monkey face', + char: '\u{1F435}', + shortName: 'monkey_face', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: [ + 'face', + 'monkey', + 'uc6', + 'animal', + 'monkey', + 'animals', + 'animal kingdom', + 'progi', + 'ape', + 'primate' + ]), + Emoji( + name: 'see-no-evil monkey', + char: '\u{1F648}', + shortName: 'see_no_evil', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.monkeyFace, + keywords: [ + 'evil', + 'face', + 'forbidden', + 'gesture', + 'monkey', + 'no', + 'not', + 'prohibited', + 'see', + 'uc6', + 'animal', + 'monkey', + 'porn', + 'shame', + 'animals', + 'animal kingdom', + 'progi', + 'ape', + 'primate' + ]), + Emoji( + name: 'hear-no-evil monkey', + char: '\u{1F649}', + shortName: 'hear_no_evil', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.monkeyFace, + keywords: [ + 'evil', + 'face', + 'forbidden', + 'gesture', + 'hear', + 'monkey', + 'no', + 'not', + 'prohibited', + 'uc6', + 'animal', + 'monkey', + 'animals', + 'animal kingdom', + 'progi', + 'ape', + 'primate' + ]), + Emoji( + name: 'speak-no-evil monkey', + char: '\u{1F64A}', + shortName: 'speak_no_evil', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.monkeyFace, + keywords: [ + 'evil', + 'face', + 'forbidden', + 'gesture', + 'monkey', + 'no', + 'not', + 'prohibited', + 'speak', + 'uc6', + 'animal', + 'monkey', + 'facebook', + 'quiet', + 'animals', + 'animal kingdom', + 'progi', + 'ape', + 'primate', + 'shut up', + 'hushed', + 'silence', + 'silent', + 'shush', + 'shh' + ]), + Emoji( + name: 'monkey', + char: '\u{1F412}', + shortName: 'monkey', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: [ + 'monkey', + 'uc6', + 'animal', + 'wildlife', + 'monkey', + 'forest', + 'pokemon', + 'animals', + 'animal kingdom', + 'progi', + 'ape', + 'primate', + 'rainforest' + ]), + Emoji( + name: 'chicken', + char: '\u{1F414}', + shortName: 'chicken', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalBird, + keywords: [ + 'bird', + 'chicken', + 'uc6', + 'animal', + 'birds', + 'farm', + 'jewish', + 'pets', + 'minecraft', + 'animals', + 'animal kingdom', + 'goose', + 'hannukah', + 'hanukkah', + 'israel' + ]), + Emoji( + name: 'penguin', + char: '\u{1F427}', + shortName: 'penguin', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalBird, + keywords: [ + 'bird', + 'penguin', + 'uc6', + 'animal', + 'wildlife', + 'birds', + 'ocean', + 'animals', + 'animal kingdom', + 'goose', + 'sea' + ]), + Emoji( + name: 'bird', + char: '\u{1F426}', + shortName: 'bird', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalBird, + keywords: [ + 'bird', + 'uc6', + 'animal', + 'wildlife', + 'twitter', + 'birds', + 'forest', + 'pets', + 'pokemon', + 'animals', + 'animal kingdom', + 'goose', + 'rainforest' + ]), + Emoji( + name: 'baby chick', + char: '\u{1F424}', + shortName: 'baby_chick', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalBird, + keywords: [ + 'baby', + 'bird', + 'chick', + 'uc6', + 'animal', + 'easter', + 'birds', + 'pets', + 'animals', + 'animal kingdom', + 'goose' + ]), + Emoji( + name: 'hatching chick', + char: '\u{1F423}', + shortName: 'hatching_chick', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalBird, + keywords: [ + 'baby', + 'bird', + 'chick', + 'hatching', + 'uc6', + 'animal', + 'easter', + 'birds', + 'eggs', + 'pets', + 'animals', + 'animal kingdom', + 'goose' + ]), + Emoji( + name: 'front-facing baby chick', + char: '\u{1F425}', + shortName: 'hatched_chick', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalBird, + keywords: [ + 'baby', + 'bird', + 'chick', + 'uc6', + 'animal', + 'easter', + 'birds', + 'pets', + 'pokemon', + 'minecraft', + 'animals', + 'animal kingdom', + 'goose' + ]), + Emoji( + name: 'duck', + char: '\u{1F986}', + shortName: 'duck', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalBird, + keywords: [ + 'bird', + 'duck', + 'uc9', + 'animal', + 'wildlife', + 'quack', + 'birds', + 'hunt', + 'animals', + 'animal kingdom', + 'goose' + ]), + Emoji( + name: 'dodo', + char: '\u{1F9A4}', + shortName: 'dodo', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalBird, + keywords: [ + 'uc13', + 'animal', + 'birds', + 'animals', + 'animal kingdom', + 'goose' + ]), + Emoji( + name: 'eagle', + char: '\u{1F985}', + shortName: 'eagle', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalBird, + keywords: [ + 'bird', + 'eagle', + 'uc9', + 'animal', + 'wildlife', + 'america', + 'birds', + 'forest', + 'pokemon', + 'independence day', + 'animals', + 'animal kingdom', + 'usa', + 'united states', + 'united states of america', + 'american', + 'goose', + 'rainforest', + '4th of july' + ]), + Emoji( + name: 'owl', + char: '\u{1F989}', + shortName: 'owl', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalBird, + keywords: [ + 'bird', + 'owl', + 'wise', + 'uc9', + 'animal', + 'wildlife', + 'halloween', + 'birds', + 'forest', + 'animals', + 'animal kingdom', + 'samhain', + 'goose', + 'rainforest' + ]), + Emoji( + name: 'bat', + char: '\u{1F987}', + shortName: 'bat', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: [ + 'bat', + 'vampire', + 'uc9', + 'animal', + 'wildlife', + 'halloween', + 'harry potter', + 'forest', + 'pokemon', + 'super hero', + 'vampire', + 'animals', + 'animal kingdom', + 'samhain', + 'rainforest', + 'superhero', + 'superman', + 'batman', + 'dracula' + ]), + Emoji( + name: 'wolf', + char: '\u{1F43A}', + shortName: 'wolf', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: [ + 'face', + 'wolf', + 'uc6', + 'animal', + 'wildlife', + 'roar', + 'forest', + 'pokemon', + 'minecraft', + 'animals', + 'animal kingdom', + 'rawr', + 'rainforest' + ]), + Emoji( + name: 'boar', + char: '\u{1F417}', + shortName: 'boar', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: [ + 'pig', + 'uc6', + 'animal', + 'wildlife', + 'pig', + 'farm', + 'forest', + 'guinea pig', + 'hunt', + 'animals', + 'animal kingdom', + 'pork', + 'rainforest' + ]), + Emoji( + name: 'horse face', + char: '\u{1F434}', + shortName: 'horse', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: [ + 'face', + 'horse', + 'uc6', + 'animal', + 'wildlife', + 'horse racing', + 'donkey', + 'farm', + 'pets', + 'pokemon', + 'texas', + 'minecraft', + 'horse', + 'animals', + 'animal kingdom', + 'horseback riding', + 'horse and rider', + 'horses', + 'horseshoe', + 'pony', + 'poney' + ]), + Emoji( + name: 'unicorn', + char: '\u{1F984}', + shortName: 'unicorn', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: [ + 'face', + 'unicorn', + 'uc8', + 'animal', + 'halloween', + 'emojione', + 'hug', + 'lgbt', + 'unicorn', + 'pink', + 'facebook', + 'dream', + 'pokemon', + 'fantasy', + 'animals', + 'animal kingdom', + 'samhain', + 'emoji one', + 'embrace', + 'hugs', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'unicorns', + 'onesie', + 'rose', + 'dreams' + ]), + Emoji( + name: 'honeybee', + char: '\u{1F41D}', + shortName: 'bee', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalBug, + keywords: [ + 'bee', + 'insect', + 'uc6', + 'animal', + 'wildlife', + 'insects', + 'animals', + 'animal kingdom', + 'insect', + 'bugs', + 'bug' + ]), + Emoji( + name: 'bug', + char: '\u{1F41B}', + shortName: 'bug', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalBug, + keywords: [ + 'insect', + 'uc6', + 'animal', + 'wildlife', + 'insects', + 'gummy', + 'forest', + 'pokemon', + 'worm', + 'animals', + 'animal kingdom', + 'insect', + 'bugs', + 'bug', + 'rainforest', + 'caterpillar' + ]), + Emoji( + name: 'butterfly', + char: '\u{1F98B}', + shortName: 'butterfly', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalBug, + keywords: [ + 'butterfly', + 'insect', + 'pretty', + 'uc9', + 'animal', + 'wildlife', + 'insects', + 'forest', + 'pokemon', + 'animals', + 'animal kingdom', + 'insect', + 'bugs', + 'bug', + 'rainforest' + ]), + Emoji( + name: 'snail', + char: '\u{1F40C}', + shortName: 'snail', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalBug, + keywords: [ + 'snail', + 'uc6', + 'animal', + 'wildlife', + 'insects', + 'emojione', + 'animals', + 'animal kingdom', + 'insect', + 'bugs', + 'bug', + 'emoji one' + ]), + Emoji( + name: 'worm', + char: '\u{1FAB1}', + shortName: 'worm', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalBug, + keywords: ['uc13', 'animal', 'wildlife', 'animals', 'animal kingdom']), + Emoji( + name: 'lady beetle', + char: '\u{1F41E}', + shortName: 'lady_beetle', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalBug, + keywords: [ + 'beetle', + 'insect', + 'ladybird', + 'ladybug', + 'uc6', + 'animal', + 'wildlife', + 'insects', + 'animals', + 'animal kingdom', + 'insect', + 'bugs', + 'bug' + ]), + Emoji( + name: 'ant', + char: '\u{1F41C}', + shortName: 'ant', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalBug, + keywords: [ + 'insect', + 'uc6', + 'animal', + 'wildlife', + 'insects', + 'pokemon', + 'animals', + 'animal kingdom', + 'insect', + 'bugs', + 'bug' + ]), + Emoji( + name: 'fly', + char: '\u{1FAB0}', + shortName: 'fly', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalBug, + keywords: [ + 'uc13', + 'animal', + 'wildlife', + 'insects', + 'shit', + 'animals', + 'animal kingdom', + 'insect', + 'bugs', + 'bug', + 'poop', + 'turd', + 'feces', + 'pile', + 'merde', + 'butthole', + 'caca', + 'crap', + 'dirty', + 'pooo', + 'mess', + 'brown', + 'poopoo' + ]), + Emoji( + name: 'mosquito', + char: '\u{1F99F}', + shortName: 'mosquito', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalBug, + keywords: [ + 'uc11', + 'animal', + 'wildlife', + 'insects', + 'bite', + 'animals', + 'animal kingdom', + 'insect', + 'bugs', + 'bug' + ]), + Emoji( + name: 'cockroach', + char: '\u{1FAB3}', + shortName: 'cockroach', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalBug, + keywords: [ + 'uc13', + 'animal', + 'wildlife', + 'insects', + 'animals', + 'animal kingdom', + 'insect', + 'bugs', + 'bug' + ]), + Emoji( + name: 'beetle', + char: '\u{1FAB2}', + shortName: 'beetle', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalBug, + keywords: [ + 'uc13', + 'animal', + 'wildlife', + 'insects', + 'animals', + 'animal kingdom', + 'insect', + 'bugs', + 'bug' + ]), + Emoji( + name: 'cricket', + char: '\u{1F997}', + shortName: 'cricket', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalBug, + keywords: [ + 'uc10', + 'animal', + 'wildlife', + 'insects', + 'animals', + 'animal kingdom', + 'insect', + 'bugs', + 'bug' + ]), + Emoji( + name: 'spider', + char: '\u{1F577}\u{FE0F}', + shortName: 'spider', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalBug, + keywords: [ + 'insect', + 'uc7', + 'animal', + 'wildlife', + 'insects', + 'halloween', + 'australia', + 'harry potter', + 'forest', + 'pets', + 'animals', + 'animal kingdom', + 'insect', + 'bugs', + 'bug', + 'samhain', + 'rainforest' + ]), + Emoji( + name: 'spider web', + char: '\u{1F578}\u{FE0F}', + shortName: 'spider_web', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalBug, + keywords: [ + 'spider', + 'web', + 'uc7', + 'halloween', + 'forest', + 'samhain', + 'rainforest' + ]), + Emoji( + name: 'scorpion', + char: '\u{1F982}', + shortName: 'scorpion', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalBug, + keywords: [ + 'Scorpius', + 'scorpio', + 'zodiac', + 'uc8', + 'animal', + 'wildlife', + 'insects', + 'reptile', + 'animals', + 'animal kingdom', + 'insect', + 'bugs', + 'bug', + 'reptiles' + ]), + Emoji( + name: 'turtle', + char: '\u{1F422}', + shortName: 'turtle', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalReptile, + keywords: [ + 'terrapin', + 'tortoise', + 'turtle', + 'uc6', + 'animal', + 'wildlife', + 'reptile', + 'pets', + 'pokemon', + 'river', + 'ocean', + 'animals', + 'animal kingdom', + 'reptiles', + 'sea' + ]), + Emoji( + name: 'snake', + char: '\u{1F40D}', + shortName: 'snake', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalReptile, + keywords: [ + 'Ophiuchus', + 'bearer', + 'serpent', + 'zodiac', + 'uc6', + 'animal', + 'wildlife', + 'reptile', + 'creationism', + 'australia', + 'harry potter', + 'forest', + 'pets', + 'pokemon', + 'ocean', + 'animals', + 'animal kingdom', + 'reptiles', + 'adam & eve', + 'adam and eve', + 'rainforest', + 'sea' + ]), + Emoji( + name: 'lizard', + char: '\u{1F98E}', + shortName: 'lizard', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalReptile, + keywords: [ + 'lizard', + 'reptile', + 'uc9', + 'animal', + 'wildlife', + 'reptile', + 'forest', + 'pets', + 'pokemon', + 'animals', + 'animal kingdom', + 'reptiles', + 'rainforest' + ]), + Emoji( + name: 'T-Rex', + char: '\u{1F996}', + shortName: 't_rex', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalReptile, + keywords: [ + 'Tyrannosaurus Rex', + 'uc10', + 'animal', + 'dinosaur', + 'Tyrannosaurus Rex', + 'animals', + 'animal kingdom', + 'trex', + 't rex' + ]), + Emoji( + name: 'sauropod', + char: '\u{1F995}', + shortName: 'sauropod', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalReptile, + keywords: [ + 'brachiosaurus', + 'brontosaurus', + 'diplodocus', + 'uc10', + 'animal', + 'dinosaur', + 'Brontosaurus', + 'animals', + 'animal kingdom', + 'Diplodocus', + 'Brachiosaurus' + ]), + Emoji( + name: 'octopus', + char: '\u{1F419}', + shortName: 'octopus', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMarine, + keywords: [ + 'octopus', + 'uc6', + 'animal', + 'wildlife', + 'pussy', + 'pokemon', + 'porn', + 'scuba', + 'seafood', + 'ocean', + 'animals', + 'animal kingdom', + 'condom', + 'snorkel', + 'sea' + ]), + Emoji( + name: 'squid', + char: '\u{1F991}', + shortName: 'squid', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodMarine, + keywords: [ + 'food', + 'molusc', + 'squid', + 'uc9', + 'animal', + 'wildlife', + 'scuba', + 'seafood', + 'ocean', + 'animals', + 'animal kingdom', + 'snorkel', + 'sea' + ]), + Emoji( + name: 'shrimp', + char: '\u{1F990}', + shortName: 'shrimp', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodMarine, + keywords: [ + 'food', + 'shellfish', + 'shrimp', + 'small', + 'uc9', + 'animal', + 'wildlife', + 'prawn', + 'scuba', + 'seafood', + 'ocean', + 'crustacean', + 'animals', + 'animal kingdom', + 'snorkel', + 'sea' + ]), + Emoji( + name: 'lobster', + char: '\u{1F99E}', + shortName: 'lobster', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodMarine, + keywords: [ + 'uc11', + 'animal', + 'food', + 'wildlife', + 'seafood', + 'ocean', + 'crustacean', + 'animals', + 'animal kingdom', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'sea' + ]), + Emoji( + name: 'crab', + char: '\u{1F980}', + shortName: 'crab', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodMarine, + keywords: [ + 'Cancer', + 'zodiac', + 'uc8', + 'animal', + 'wildlife', + 'tropical', + 'pokemon', + 'scuba', + 'seafood', + 'ocean', + 'crustacean', + 'animals', + 'animal kingdom', + 'snorkel', + 'sea' + ]), + Emoji( + name: 'blowfish', + char: '\u{1F421}', + shortName: 'blowfish', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMarine, + keywords: [ + 'fish', + 'uc6', + 'animal', + 'wildlife', + 'japan', + 'scuba', + 'ocean', + 'animals', + 'animal kingdom', + 'japanese', + 'ninja', + 'snorkel', + 'sea' + ]), + Emoji( + name: 'tropical fish', + char: '\u{1F420}', + shortName: 'tropical_fish', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMarine, + keywords: [ + 'fish', + 'tropical', + 'uc6', + 'animal', + 'wildlife', + 'tropical', + 'pets', + 'scuba', + 'ocean', + 'animals', + 'animal kingdom', + 'snorkel', + 'sea' + ]), + Emoji( + name: 'fish', + char: '\u{1F41F}', + shortName: 'fish', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMarine, + keywords: [ + 'Pisces', + 'zodiac', + 'uc6', + 'animal', + 'wildlife', + 'tropical', + 'pets', + 'scuba', + 'seafood', + 'river', + 'ocean', + 'animals', + 'animal kingdom', + 'snorkel', + 'sea' + ]), + Emoji( + name: 'seal', + char: '\u{1F9AD}', + shortName: 'seal', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMarine, + keywords: [ + 'uc13', + 'animal', + 'wildlife', + 'ocean', + 'animals', + 'animal kingdom', + 'sea' + ]), + Emoji( + name: 'dolphin', + char: '\u{1F42C}', + shortName: 'dolphin', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMarine, + keywords: [ + 'flipper', + 'uc6', + 'animal', + 'wildlife', + 'tropical', + 'florida', + 'scuba', + 'ocean', + 'animals', + 'animal kingdom', + 'snorkel', + 'sea' + ]), + Emoji( + name: 'spouting whale', + char: '\u{1F433}', + shortName: 'whale', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMarine, + keywords: [ + 'face', + 'spouting', + 'whale', + 'uc6', + 'animal', + 'wildlife', + 'tropical', + 'whales', + 'scuba', + 'ocean', + 'animals', + 'animal kingdom', + 'whale', + 'moby', + 'snorkel', + 'sea' + ]), + Emoji( + name: 'whale', + char: '\u{1F40B}', + shortName: 'whale2', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMarine, + keywords: [ + 'whale', + 'uc6', + 'animal', + 'wildlife', + 'tropical', + 'whales', + 'scuba', + 'ocean', + 'animals', + 'animal kingdom', + 'whale', + 'moby', + 'snorkel', + 'sea' + ]), + Emoji( + name: 'shark', + char: '\u{1F988}', + shortName: 'shark', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMarine, + keywords: [ + 'fish', + 'shark', + 'uc9', + 'animal', + 'wildlife', + 'florida', + 'scuba', + 'ocean', + 'animals', + 'animal kingdom', + 'snorkel', + 'sea' + ]), + Emoji( + name: 'crocodile', + char: '\u{1F40A}', + shortName: 'crocodile', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalReptile, + keywords: [ + 'crocodile', + 'uc6', + 'animal', + 'wildlife', + 'reptile', + 'florida', + 'river', + 'alligator', + 'animals', + 'animal kingdom', + 'reptiles' + ]), + Emoji( + name: 'tiger', + char: '\u{1F405}', + shortName: 'tiger2', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: [ + 'tiger', + 'uc6', + 'animal', + 'wildlife', + 'roar', + 'cat', + 'circus', + 'forest', + 'pokemon', + 'animals', + 'animal kingdom', + 'rawr', + 'kitty', + 'kitten', + 'cats', + 'kittens', + 'kitties', + 'feline', + 'felines', + 'cat face', + 'gato', + 'meow', + 'circus tent', + 'clown', + 'clowns', + 'rainforest' + ]), + Emoji( + name: 'leopard', + char: '\u{1F406}', + shortName: 'leopard', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: [ + 'leopard', + 'uc6', + 'animal', + 'wildlife', + 'roar', + 'cat', + 'forest', + 'animals', + 'animal kingdom', + 'rawr', + 'kitty', + 'kitten', + 'cats', + 'kittens', + 'kitties', + 'feline', + 'felines', + 'cat face', + 'gato', + 'meow', + 'rainforest' + ]), + Emoji( + name: 'zebra', + char: '\u{1F993}', + shortName: 'zebra', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: [ + 'stripe', + 'uc10', + 'animal', + 'wildlife', + 'horse', + 'animals', + 'animal kingdom' + ]), + Emoji( + name: 'gorilla', + char: '\u{1F98D}', + shortName: 'gorilla', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: [ + 'gorilla', + 'uc9', + 'animal', + 'wildlife', + 'forest', + 'pokemon', + 'animals', + 'animal kingdom', + 'rainforest' + ]), + Emoji( + name: 'orangutan', + char: '\u{1F9A7}', + shortName: 'orangutan', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: [ + 'uc12', + 'animal', + 'wildlife', + 'monkey', + 'animals', + 'animal kingdom', + 'progi', + 'ape', + 'primate' + ]), + Emoji( + name: 'elephant', + char: '\u{1F418}', + shortName: 'elephant', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: [ + 'elephant', + 'uc6', + 'animal', + 'wildlife', + 'circus', + 'elephant', + 'animals', + 'animal kingdom', + 'circus tent', + 'clown', + 'clowns' + ]), + Emoji( + name: 'mammoth', + char: '\u{1F9A3}', + shortName: 'mammoth', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: [ + 'uc13', + 'animal', + 'wildlife', + 'mammuthus', + 'elephant', + 'animals', + 'animal kingdom' + ]), + Emoji( + name: 'bison', + char: '\u{1F9AC}', + shortName: 'bison', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: ['uc13', 'animal', 'wildlife', 'animals', 'animal kingdom']), + Emoji( + name: 'hippopotamus', + char: '\u{1F99B}', + shortName: 'hippopotamus', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: ['uc11', 'animal', 'wildlife', 'animals', 'animal kingdom']), + Emoji( + name: 'rhinoceros', + char: '\u{1F98F}', + shortName: 'rhino', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: [ + 'rhinoceros', + 'uc9', + 'animal', + 'wildlife', + 'forest', + 'pokemon', + 'animals', + 'animal kingdom', + 'rainforest' + ]), + Emoji( + name: 'camel', + char: '\u{1F42A}', + shortName: 'dromedary_camel', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: [ + 'dromedary', + 'hump', + 'uc6', + 'animal', + 'wildlife', + 'animals', + 'animal kingdom' + ]), + Emoji( + name: 'two-hump camel', + char: '\u{1F42B}', + shortName: 'camel', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: [ + 'bactrian', + 'camel', + 'hump', + 'uc6', + 'animal', + 'wildlife', + 'hump day', + 'animals', + 'animal kingdom' + ]), + Emoji( + name: 'giraffe', + char: '\u{1F992}', + shortName: 'giraffe', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: [ + 'spots', + 'uc10', + 'animal', + 'wildlife', + 'animals', + 'animal kingdom' + ]), + Emoji( + name: 'kangaroo', + char: '\u{1F998}', + shortName: 'kangaroo', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: [ + 'uc11', + 'animal', + 'wildlife', + 'australia', + 'marsupial', + 'animals', + 'animal kingdom', + 'marsupials' + ]), + Emoji( + name: 'water buffalo', + char: '\u{1F403}', + shortName: 'water_buffalo', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: [ + 'buffalo', + 'water', + 'uc6', + 'animal', + 'wildlife', + 'scotland', + 'animals', + 'animal kingdom', + 'scottish' + ]), + Emoji( + name: 'ox', + char: '\u{1F402}', + shortName: 'ox', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: [ + 'Taurus', + 'bull', + 'zodiac', + 'uc6', + 'animal', + 'farm', + 'texas', + 'animals', + 'animal kingdom' + ]), + Emoji( + name: 'cow', + char: '\u{1F404}', + shortName: 'cow2', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: [ + 'cow', + 'uc6', + 'animal', + 'farm', + 'texas', + 'animals', + 'animal kingdom' + ]), + Emoji( + name: 'horse', + char: '\u{1F40E}', + shortName: 'racehorse', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: [ + 'equestrian', + 'racehorse', + 'racing', + 'uc6', + 'animal', + 'wildlife', + 'horse racing', + 'donkey', + 'farm', + 'pets', + 'pokemon', + 'texas', + 'viking', + 'horse', + 'animals', + 'animal kingdom', + 'horseback riding', + 'horse and rider', + 'horses', + 'horseshoe', + 'pony', + 'poney', + 'knight' + ]), + Emoji( + name: 'pig', + char: '\u{1F416}', + shortName: 'pig2', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: [ + 'sow', + 'uc6', + 'animal', + 'pink', + 'pig', + 'farm', + 'guinea pig', + 'pets', + 'minecraft', + 'animals', + 'animal kingdom', + 'rose', + 'pork' + ]), + Emoji( + name: 'ram', + char: '\u{1F40F}', + shortName: 'ram', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: [ + 'Aries', + 'male', + 'sheep', + 'zodiac', + 'uc6', + 'animal', + 'wildlife', + 'farm', + 'sheep', + 'animals', + 'animal kingdom' + ]), + Emoji( + name: 'ewe', + char: '\u{1F411}', + shortName: 'sheep', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: [ + 'female', + 'sheep', + 'uc6', + 'animal', + 'farm', + 'lamb', + 'sheep', + 'animals', + 'animal kingdom' + ]), + Emoji( + name: 'llama', + char: '\u{1F999}', + shortName: 'llama', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: ['uc11', 'animal', 'farm', 'animals', 'animal kingdom']), + Emoji( + name: 'goat', + char: '\u{1F410}', + shortName: 'goat', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: [ + 'Capricorn', + 'zodiac', + 'uc6', + 'animal', + 'farm', + 'animals', + 'animal kingdom' + ]), + Emoji( + name: 'deer', + char: '\u{1F98C}', + shortName: 'deer', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: [ + 'deer', + 'uc9', + 'animal', + 'wildlife', + 'christmas', + 'raindeer', + 'forest', + 'hunt', + 'animals', + 'animal kingdom', + 'navidad', + 'xmas', + 'noel', + 'merry christmas', + 'moose', + 'rudolph', + 'rainforest' + ]), + Emoji( + name: 'dog', + char: '\u{1F415}', + shortName: 'dog2', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: [ + 'pet', + 'uc6', + 'animal', + 'dog', + 'japan', + 'pug', + 'bingo', + 'bitch', + 'farm', + 'pets', + 'pokemon', + 'minecraft', + 'hunt', + 'animals', + 'animal kingdom', + 'puppy', + 'doggy', + 'memes', + 'dogs', + 'perro', + 'puppies', + 'chien', + 'japanese', + 'ninja', + 'pugs', + 'puta', + 'pute' + ]), + Emoji( + name: 'poodle', + char: '\u{1F429}', + shortName: 'poodle', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: [ + 'dog', + 'uc6', + 'animal', + 'dog', + 'pink', + 'paris', + 'bitch', + 'pets', + 'animals', + 'animal kingdom', + 'puppy', + 'doggy', + 'memes', + 'dogs', + 'perro', + 'puppies', + 'chien', + 'rose', + 'french', + 'france', + 'puta', + 'pute' + ]), + Emoji( + name: 'guide dog', + char: '\u{1F9AE}', + shortName: 'guide_dog', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: [ + 'uc12', + 'animal', + 'dog', + 'handicap', + 'pets', + 'blind', + 'animals', + 'animal kingdom', + 'puppy', + 'doggy', + 'memes', + 'dogs', + 'perro', + 'puppies', + 'chien', + 'disabled', + 'disability' + ]), + Emoji( + name: 'service dog', + char: '\u{1F415}\u{200D}\u{1F9BA}', + shortName: 'service_dog', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: [ + 'uc12', + 'animal', + 'dog', + 'handicap', + 'pets', + 'blind', + 'animals', + 'animal kingdom', + 'puppy', + 'doggy', + 'memes', + 'dogs', + 'perro', + 'puppies', + 'chien', + 'disabled', + 'disability' + ]), + Emoji( + name: 'cat', + char: '\u{1F408}', + shortName: 'cat2', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: [ + 'pet', + 'uc6', + 'animal', + 'cat', + 'pussy', + 'grass', + 'farm', + 'pets', + 'pokemon', + 'porn', + 'animals', + 'animal kingdom', + 'kitty', + 'kitten', + 'cats', + 'kittens', + 'kitties', + 'feline', + 'felines', + 'cat face', + 'gato', + 'meow', + 'condom' + ]), + Emoji( + name: 'black cat', + char: '\u{1F408}\u{200D}\u{2B1B}', + shortName: 'black_cat', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: [ + 'uc13', + 'animal', + 'halloween', + 'cat', + 'luck', + 'sol', + 'farm', + 'animals', + 'animal kingdom', + 'samhain', + 'kitty', + 'kitten', + 'cats', + 'kittens', + 'kitties', + 'feline', + 'felines', + 'cat face', + 'gato', + 'meow', + 'good luck', + 'lucky', + 'shit outta luck', + 'shit out of luck', + 'bad luck' + ]), + Emoji( + name: 'rooster', + char: '\u{1F413}', + shortName: 'rooster', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalBird, + keywords: [ + 'bird', + 'rooster', + 'uc6', + 'animal', + 'wildlife', + 'birds', + 'farm', + 'animals', + 'animal kingdom', + 'goose' + ]), + Emoji( + name: 'turkey', + char: '\u{1F983}', + shortName: 'turkey', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalBird, + keywords: [ + 'bird', + 'turkey', + 'uc8', + 'animal', + 'wildlife', + 'birds', + 'farm', + 'thanksgiving', + 'animals', + 'animal kingdom', + 'goose' + ]), + Emoji( + name: 'peacock', + char: '\u{1F99A}', + shortName: 'peacock', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalBird, + keywords: [ + 'uc11', + 'animal', + 'wildlife', + 'birds', + 'farm', + 'animals', + 'animal kingdom', + 'goose' + ]), + Emoji( + name: 'parrot', + char: '\u{1F99C}', + shortName: 'parrot', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalBird, + keywords: [ + 'uc11', + 'animal', + 'wildlife', + 'tropical', + 'pirate', + 'birds', + 'animals', + 'animal kingdom', + 'goose' + ]), + Emoji( + name: 'swan', + char: '\u{1F9A2}', + shortName: 'swan', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalBird, + keywords: [ + 'uc11', + 'animal', + 'wildlife', + 'birds', + 'animals', + 'animal kingdom', + 'goose' + ]), + Emoji( + name: 'flamingo', + char: '\u{1F9A9}', + shortName: 'flamingo', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalBird, + keywords: [ + 'uc12', + 'animal', + 'wildlife', + 'pink', + 'birds', + 'animals', + 'animal kingdom', + 'rose', + 'goose' + ]), + Emoji( + name: 'dove', + char: '\u{1F54A}\u{FE0F}', + shortName: 'dove', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalBird, + keywords: [ + 'bird', + 'fly', + 'peace', + 'uc7', + 'animal', + 'wildlife', + 'wedding', + 'religion', + 'peace', + 'pray', + 'birds', + 'animals', + 'animal kingdom', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'peace out', + 'peace sign', + 'prayer', + 'praying', + 'prayers', + 'grateful', + 'sorry', + 'heaven', + 'bless', + 'faith', + 'holy', + 'spirit', + 'hopeful', + 'blessed', + 'preach', + 'offering', + 'goose' + ]), + Emoji( + name: 'rabbit', + char: '\u{1F407}', + shortName: 'rabbit2', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: [ + 'bunny', + 'pet', + 'uc6', + 'animal', + 'wildlife', + 'magic', + 'easter', + 'pets', + 'pokemon', + 'hunt', + 'animals', + 'animal kingdom', + 'spell', + 'genie', + 'magical' + ]), + Emoji( + name: 'raccoon', + char: '\u{1F99D}', + shortName: 'raccoon', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: [ + 'uc11', + 'animal', + 'wildlife', + 'forest', + 'animals', + 'animal kingdom', + 'rainforest' + ]), + Emoji( + name: 'skunk', + char: '\u{1F9A8}', + shortName: 'skunk', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: [ + 'uc12', + 'animal', + 'wildlife', + 'stinky', + 'forest', + 'animals', + 'animal kingdom', + 'smell', + 'stink', + 'odor', + 'rainforest' + ]), + Emoji( + name: 'badger', + char: '\u{1F9A1}', + shortName: 'badger', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: [ + 'uc11', + 'animal', + 'wildlife', + 'forest', + 'animals', + 'animal kingdom', + 'rainforest' + ]), + Emoji( + name: 'beaver', + char: '\u{1F9AB}', + shortName: 'beaver', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: [ + 'uc13', + 'animal', + 'wildlife', + 'forest', + 'rodent', + 'beaver', + 'animals', + 'animal kingdom', + 'rainforest' + ]), + Emoji( + name: 'otter', + char: '\u{1F9A6}', + shortName: 'otter', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: [ + 'uc12', + 'animal', + 'wildlife', + 'forest', + 'ocean', + 'otor', + 'animals', + 'animal kingdom', + 'rainforest', + 'sea', + 'oter', + 'wódr̥' + ]), + Emoji( + name: 'sloth', + char: '\u{1F9A5}', + shortName: 'sloth', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: [ + 'uc12', + 'animal', + 'wildlife', + 'forest', + 'lazy', + 'animals', + 'animal kingdom', + 'rainforest' + ]), + Emoji( + name: 'mouse', + char: '\u{1F401}', + shortName: 'mouse2', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: [ + 'mouse', + 'uc6', + 'animal', + 'wildlife', + 'mickey', + 'disney', + 'pokemon', + 'rodent', + 'animals', + 'animal kingdom', + 'cartoon' + ]), + Emoji( + name: 'rat', + char: '\u{1F400}', + shortName: 'rat', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: [ + 'rat', + 'uc6', + 'animal', + 'wildlife', + 'harry potter', + 'pokemon', + 'rodent', + 'animals', + 'animal kingdom' + ]), + Emoji( + name: 'chipmunk', + char: '\u{1F43F}\u{FE0F}', + shortName: 'chipmunk', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: [ + 'chipmunk', + 'uc7', + 'animal', + 'wildlife', + 'squirrel', + 'forest', + 'pokemon', + 'rodent', + 'animals', + 'animal kingdom', + 'rainforest' + ]), + Emoji( + name: 'hedgehog', + char: '\u{1F994}', + shortName: 'hedgehog', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: [ + 'spiny', + 'uc10', + 'animal', + 'wildlife', + 'erinaceinae', + 'animals', + 'animal kingdom' + ]), + Emoji( + name: 'paw prints', + char: '\u{1F43E}', + shortName: 'feet', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMammal, + keywords: [ + 'feet', + 'paw', + 'print', + 'uc6', + 'animal', + 'paws', + 'animals', + 'animal kingdom' + ]), + Emoji( + name: 'dragon', + char: '\u{1F409}', + shortName: 'dragon', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalReptile, + keywords: [ + 'fairy tale', + 'uc6', + 'animal', + 'roar', + 'reptile', + 'harry potter', + 'pokemon', + 'minecraft', + 'fantasy', + 'animals', + 'animal kingdom', + 'rawr', + 'reptiles' + ]), + Emoji( + name: 'dragon face', + char: '\u{1F432}', + shortName: 'dragon_face', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalReptile, + keywords: [ + 'dragon', + 'face', + 'fairy tale', + 'uc6', + 'animal', + 'roar', + 'monster', + 'reptile', + 'pokemon', + 'minecraft', + 'fantasy', + 'animals', + 'animal kingdom', + 'rawr', + 'monsters', + 'beast', + 'reptiles' + ]), + Emoji( + name: 'cactus', + char: '\u{1F335}', + shortName: 'cactus', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.plantOther, + keywords: [ + 'plant', + 'uc6', + 'nature', + 'plant', + 'trees', + 'plants', + 'tree', + 'branch', + 'wood' + ]), + Emoji( + name: 'Christmas tree', + char: '\u{1F384}', + shortName: 'christmas_tree', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.event, + keywords: [ + 'Christmas', + 'celebration', + 'tree', + 'uc6', + 'holidays', + 'plant', + 'christmas', + 'santa', + 'trees', + 'advent', + 'holiday', + 'plants', + 'navidad', + 'xmas', + 'noel', + 'merry christmas', + 'santa clause', + 'santa claus', + 'tree', + 'branch', + 'wood' + ]), + Emoji( + name: 'evergreen tree', + char: '\u{1F332}', + shortName: 'evergreen_tree', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.plantOther, + keywords: [ + 'tree', + 'uc6', + 'nature', + 'plant', + 'camp', + 'trees', + 'forest', + 'parks', + 'plants', + 'camping', + 'tent', + 'camper', + 'outdoor', + 'outside', + 'tree', + 'branch', + 'wood', + 'rainforest', + 'regional park', + 'nature park', + 'natural park' + ]), + Emoji( + name: 'deciduous tree', + char: '\u{1F333}', + shortName: 'deciduous_tree', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.plantOther, + keywords: [ + 'deciduous', + 'shedding', + 'tree', + 'uc6', + 'nature', + 'plant', + 'camp', + 'trees', + 'farm', + 'forest', + 'parks', + 'plants', + 'camping', + 'tent', + 'camper', + 'outdoor', + 'outside', + 'tree', + 'branch', + 'wood', + 'rainforest', + 'regional park', + 'nature park', + 'natural park' + ]), + Emoji( + name: 'palm tree', + char: '\u{1F334}', + shortName: 'palm_tree', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.plantOther, + keywords: [ + 'palm', + 'tree', + 'uc6', + 'nature', + 'plant', + 'tropical', + 'trees', + 'california', + 'coconut', + 'florida', + 'palma', + 'plants', + 'tree', + 'branch', + 'wood', + 'palmas' + ]), + Emoji( + name: 'seedling', + char: '\u{1F331}', + shortName: 'seedling', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.plantOther, + keywords: [ + 'young', + 'uc6', + 'nature', + 'plant', + 'trees', + 'leaf', + 'grass', + 'weed', + 'farm', + 'irish', + 'pokemon', + 'plants', + 'tree', + 'branch', + 'wood', + 'leaves', + 'saint patricks day', + 'st patricks day', + 'leprechaun' + ]), + Emoji( + name: 'herb', + char: '\u{1F33F}', + shortName: 'herb', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.plantOther, + keywords: [ + 'leaf', + 'uc6', + 'nature', + 'plant', + 'leaf', + 'grass', + 'weed', + 'farm', + 'plants', + 'leaves' + ]), + Emoji( + name: 'shamrock', + char: '\u{2618}\u{FE0F}', + shortName: 'shamrock', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.plantOther, + keywords: [ + 'plant', + 'uc4', + 'nature', + 'plant', + 'luck', + 'leaf', + 'grass', + 'irish', + 'plants', + 'good luck', + 'lucky', + 'leaves', + 'saint patricks day', + 'st patricks day', + 'leprechaun' + ]), + Emoji( + name: 'four leaf clover', + char: '\u{1F340}', + shortName: 'four_leaf_clover', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.plantOther, + keywords: [ + '4', + 'clover', + 'four', + 'leaf', + 'uc6', + 'nature', + 'plant', + 'luck', + 'leaf', + 'sol', + 'grass', + 'bingo', + 'irish', + 'plants', + 'good luck', + 'lucky', + 'leaves', + 'shit outta luck', + 'shit out of luck', + 'bad luck', + 'saint patricks day', + 'st patricks day', + 'leprechaun' + ]), + Emoji( + name: 'pine decoration', + char: '\u{1F38D}', + shortName: 'bamboo', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.event, + keywords: [ + 'Japanese', + 'bamboo', + 'celebration', + 'pine', + 'uc6', + 'nature', + 'plant', + 'japan', + 'plants', + 'japanese', + 'ninja' + ]), + Emoji( + name: 'tanabata tree', + char: '\u{1F38B}', + shortName: 'tanabata_tree', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.event, + keywords: [ + 'Japanese', + 'banner', + 'celebration', + 'tree', + 'uc6', + 'nature', + 'plant', + 'japan', + 'trees', + 'plants', + 'japanese', + 'ninja', + 'tree', + 'branch', + 'wood' + ]), + Emoji( + name: 'leaf fluttering in wind', + char: '\u{1F343}', + shortName: 'leaves', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.plantOther, + keywords: [ + 'blow', + 'flutter', + 'leaf', + 'wind', + 'uc6', + 'weather', + 'nature', + 'plant', + 'trees', + 'leaf', + 'storm', + 'autumn', + 'plants', + 'tree', + 'branch', + 'wood', + 'leaves', + 'fall' + ]), + Emoji( + name: 'fallen leaf', + char: '\u{1F342}', + shortName: 'fallen_leaf', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.plantOther, + keywords: [ + 'falling', + 'leaf', + 'uc6', + 'nature', + 'plant', + 'trees', + 'leaf', + 'autumn', + 'plants', + 'tree', + 'branch', + 'wood', + 'leaves', + 'fall' + ]), + Emoji( + name: 'maple leaf', + char: '\u{1F341}', + shortName: 'maple_leaf', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.plantOther, + keywords: [ + 'falling', + 'leaf', + 'maple', + 'uc6', + 'nature', + 'plant', + 'trees', + 'leaf', + 'autumn', + 'marijuana', + 'plants', + 'tree', + 'branch', + 'wood', + 'leaves', + 'fall' + ]), + Emoji( + name: 'feather', + char: '\u{1FAB6}', + shortName: 'feather', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalBird, + keywords: [ + 'uc13', + 'animal', + 'nature', + 'lgbt', + 'birds', + 'plume', + 'animals', + 'animal kingdom', + 'homosexual', + 'bisex', + 'transgender', + 'non binary', + 'pansexuality', + 'intersex', + 'goose', + 'quill', + 'plumage', + 'feathering', + 'pluma', + 'piuma', + 'feder' + ]), + Emoji( + name: 'mushroom', + char: '\u{1F344}', + shortName: 'mushroom', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodVegetable, + keywords: [ + 'toadstool', + 'uc6', + 'food', + 'nature', + 'vegetables', + 'drugs', + 'plant', + 'disney', + 'poison', + 'pokemon', + 'mushroom', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'vegetable', + 'veggie', + 'legume', + 'drug', + 'narcotics', + 'plants', + 'cartoon', + 'toxic', + 'toxins' + ]), + Emoji( + name: 'spiral shell', + char: '\u{1F41A}', + shortName: 'shell', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalMarine, + keywords: [ + 'shell', + 'spiral', + 'uc6', + 'nature', + 'tropical', + 'scuba', + 'ocean', + 'snorkel', + 'sea' + ]), + Emoji( + name: 'rock', + char: '\u{1FAA8}', + shortName: 'rock', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeBuilding, + keywords: [ + 'uc13', + 'nature', + 'parks', + 'climb', + 'boulder', + 'regional park', + 'nature park', + 'natural park', + 'pebble' + ]), + Emoji( + name: 'wood', + char: '\u{1FAB5}', + shortName: 'wood', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeBuilding, + keywords: [ + 'uc13', + 'nature', + 'beaver', + 'parks', + 'regional park', + 'nature park', + 'natural park' + ]), + Emoji( + name: 'sheaf of rice', + char: '\u{1F33E}', + shortName: 'ear_of_rice', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.plantOther, + keywords: [ + 'ear', + 'grain', + 'rice', + 'uc6', + 'nature', + 'plant', + 'leaf', + 'grass', + 'farm', + 'plants', + 'leaves' + ]), + Emoji( + name: 'potted plant', + char: '\u{1FAB4}', + shortName: 'potted_plant', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.plantOther, + keywords: ['uc13', 'nature', 'plant', 'leaf', 'plants', 'leaves']), + Emoji( + name: 'bouquet', + char: '\u{1F490}', + shortName: 'bouquet', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.plantFlower, + keywords: [ + 'flower', + 'uc6', + 'nature', + 'wedding', + 'flower', + 'plant', + 'love', + 'rip', + 'condolence', + 'beautiful', + 'roses', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'flowers', + 'plants', + 'i love you', + 'te amo', + "je t'aime", + 'anniversary', + 'lovin', + 'amour', + 'aimer', + 'amor', + 'valentines day', + 'enamour', + 'lovey', + 'rest in peace', + 'compassion', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely' + ]), + Emoji( + name: 'tulip', + char: '\u{1F337}', + shortName: 'tulip', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.plantFlower, + keywords: [ + 'flower', + 'uc6', + 'nature', + 'flower', + 'plant', + 'vagina', + 'beautiful', + 'girls night', + 'pink', + 'easter', + 'flowers', + 'plants', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'ladies night', + 'girls only', + 'girlfriend', + 'rose' + ]), + Emoji( + name: 'rose', + char: '\u{1F339}', + shortName: 'rose', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.plantFlower, + keywords: [ + 'flower', + 'uc6', + 'nature', + 'flower', + 'plant', + 'love', + 'rip', + 'condolence', + 'beautiful', + 'flowers', + 'plants', + 'i love you', + 'te amo', + "je t'aime", + 'anniversary', + 'lovin', + 'amour', + 'aimer', + 'amor', + 'valentines day', + 'enamour', + 'lovey', + 'rest in peace', + 'compassion', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely' + ]), + Emoji( + name: 'wilted flower', + char: '\u{1F940}', + shortName: 'wilted_rose', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.plantFlower, + keywords: [ + 'flower', + 'wilted', + 'uc9', + 'flower', + 'halloween', + 'plant', + 'dead', + 'flowers', + 'samhain', + 'plants', + 'death', + 'die', + 'dying', + 'fart', + 'goth', + 'grave', + 'headstone', + 'horror', + 'hurt', + 'kill', + 'murder', + 'tomb', + 'toot', + 'died' + ]), + Emoji( + name: 'hibiscus', + char: '\u{1F33A}', + shortName: 'hibiscus', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.plantFlower, + keywords: [ + 'flower', + 'uc6', + 'nature', + 'flower', + 'plant', + 'tropical', + 'beautiful', + 'pink', + 'flowers', + 'plants', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'rose' + ]), + Emoji( + name: 'cherry blossom', + char: '\u{1F338}', + shortName: 'cherry_blossom', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.plantFlower, + keywords: [ + 'blossom', + 'cherry', + 'flower', + 'uc6', + 'nature', + 'flower', + 'plant', + 'japan', + 'tropical', + 'beautiful', + 'hawaii', + 'pink', + 'sakura', + 'flowers', + 'plants', + 'japanese', + 'ninja', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'aloha', + 'kawaii', + 'maui', + 'moana', + 'rose' + ]), + Emoji( + name: 'blossom', + char: '\u{1F33C}', + shortName: 'blossom', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.plantFlower, + keywords: [ + 'flower', + 'uc6', + 'nature', + 'flower', + 'plant', + 'vagina', + 'beautiful', + 'flowers', + 'plants', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely' + ]), + Emoji( + name: 'sunflower', + char: '\u{1F33B}', + shortName: 'sunflower', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.plantFlower, + keywords: [ + 'flower', + 'sun', + 'uc6', + 'nature', + 'flower', + 'plant', + 'beautiful', + 'farm', + 'flowers', + 'plants', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely' + ]), + Emoji( + name: 'sun with face', + char: '\u{1F31E}', + shortName: 'sun_with_face', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.skyWeather, + keywords: [ + 'bright', + 'face', + 'sun', + 'uc6', + 'sun', + 'sky', + 'day', + 'hump day', + 'morning', + 'sunglasses', + 'california', + 'pokemon', + 'las vegas', + 'summer', + 'sunshine', + 'sunny', + 'eclipse', + 'solar', + 'solareclipse', + 'shiny', + 'good morning', + 'shades', + 'lunettes de soleil', + 'sun glasses', + 'vegas', + 'weekend' + ]), + Emoji( + name: 'full moon face', + char: '\u{1F31D}', + shortName: 'full_moon_with_face', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.skyWeather, + keywords: [ + 'bright', + 'face', + 'full', + 'moon', + 'uc6', + 'halloween', + 'space', + 'sky', + 'moon', + 'goodnight', + 'samhain', + 'outer space', + 'galaxy', + 'universe', + 'nasa', + 'spaceship', + 'night', + 'moons', + 'lunar', + 'lunareclipse', + 'lunar eclipse' + ]), + Emoji( + name: 'first quarter moon face', + char: '\u{1F31B}', + shortName: 'first_quarter_moon_with_face', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.skyWeather, + keywords: [ + 'face', + 'moon', + 'quarter', + 'uc6', + 'space', + 'sky', + 'moon', + 'goodnight', + 'outer space', + 'galaxy', + 'universe', + 'nasa', + 'spaceship', + 'night', + 'moons', + 'lunar', + 'lunareclipse', + 'lunar eclipse' + ]), + Emoji( + name: 'last quarter moon face', + char: '\u{1F31C}', + shortName: 'last_quarter_moon_with_face', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.skyWeather, + keywords: [ + 'face', + 'moon', + 'quarter', + 'uc6', + 'space', + 'sky', + 'moon', + 'goodnight', + 'outer space', + 'galaxy', + 'universe', + 'nasa', + 'spaceship', + 'night', + 'moons', + 'lunar', + 'lunareclipse', + 'lunar eclipse' + ]), + Emoji( + name: 'new moon face', + char: '\u{1F31A}', + shortName: 'new_moon_with_face', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.skyWeather, + keywords: [ + 'face', + 'moon', + 'uc6', + 'space', + 'sky', + 'moon', + 'goodnight', + 'pokemon', + 'outer space', + 'galaxy', + 'universe', + 'nasa', + 'spaceship', + 'night', + 'moons', + 'lunar', + 'lunareclipse', + 'lunar eclipse' + ]), + Emoji( + name: 'full moon', + char: '\u{1F315}', + shortName: 'full_moon', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.skyWeather, + keywords: [ + 'full', + 'moon', + 'uc6', + 'halloween', + 'space', + 'sky', + 'moon', + 'goodnight', + 'samhain', + 'outer space', + 'galaxy', + 'universe', + 'nasa', + 'spaceship', + 'night', + 'moons', + 'lunar', + 'lunareclipse', + 'lunar eclipse' + ]), + Emoji( + name: 'waning gibbous moon', + char: '\u{1F316}', + shortName: 'waning_gibbous_moon', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.skyWeather, + keywords: [ + 'gibbous', + 'moon', + 'waning', + 'uc6', + 'space', + 'sky', + 'moon', + 'goodnight', + 'outer space', + 'galaxy', + 'universe', + 'nasa', + 'spaceship', + 'night', + 'moons', + 'lunar', + 'lunareclipse', + 'lunar eclipse' + ]), + Emoji( + name: 'last quarter moon', + char: '\u{1F317}', + shortName: 'last_quarter_moon', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.skyWeather, + keywords: [ + 'moon', + 'quarter', + 'uc6', + 'space', + 'sky', + 'moon', + 'goodnight', + 'outer space', + 'galaxy', + 'universe', + 'nasa', + 'spaceship', + 'night', + 'moons', + 'lunar', + 'lunareclipse', + 'lunar eclipse' + ]), + Emoji( + name: 'waning crescent moon', + char: '\u{1F318}', + shortName: 'waning_crescent_moon', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.skyWeather, + keywords: [ + 'crescent', + 'moon', + 'waning', + 'uc6', + 'space', + 'sky', + 'moon', + 'goodnight', + 'outer space', + 'galaxy', + 'universe', + 'nasa', + 'spaceship', + 'night', + 'moons', + 'lunar', + 'lunareclipse', + 'lunar eclipse' + ]), + Emoji( + name: 'new moon', + char: '\u{1F311}', + shortName: 'new_moon', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.skyWeather, + keywords: [ + 'dark', + 'moon', + 'uc6', + 'halloween', + 'space', + 'sky', + 'moon', + 'goodnight', + 'samhain', + 'outer space', + 'galaxy', + 'universe', + 'nasa', + 'spaceship', + 'night', + 'moons', + 'lunar', + 'lunareclipse', + 'lunar eclipse' + ]), + Emoji( + name: 'waxing crescent moon', + char: '\u{1F312}', + shortName: 'waxing_crescent_moon', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.skyWeather, + keywords: [ + 'crescent', + 'moon', + 'waxing', + 'uc6', + 'space', + 'sky', + 'moon', + 'goodnight', + 'outer space', + 'galaxy', + 'universe', + 'nasa', + 'spaceship', + 'night', + 'moons', + 'lunar', + 'lunareclipse', + 'lunar eclipse' + ]), + Emoji( + name: 'first quarter moon', + char: '\u{1F313}', + shortName: 'first_quarter_moon', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.skyWeather, + keywords: [ + 'moon', + 'quarter', + 'uc6', + 'space', + 'sky', + 'moon', + 'goodnight', + 'outer space', + 'galaxy', + 'universe', + 'nasa', + 'spaceship', + 'night', + 'moons', + 'lunar', + 'lunareclipse', + 'lunar eclipse' + ]), + Emoji( + name: 'waxing gibbous moon', + char: '\u{1F314}', + shortName: 'waxing_gibbous_moon', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.skyWeather, + keywords: [ + 'gibbous', + 'moon', + 'waxing', + 'uc6', + 'space', + 'sky', + 'moon', + 'goodnight', + 'outer space', + 'galaxy', + 'universe', + 'nasa', + 'spaceship', + 'night', + 'moons', + 'lunar', + 'lunareclipse', + 'lunar eclipse' + ]), + Emoji( + name: 'crescent moon', + char: '\u{1F319}', + shortName: 'crescent_moon', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.skyWeather, + keywords: [ + 'crescent', + 'moon', + 'uc6', + 'space', + 'sky', + 'moon', + 'goodnight', + 'outer space', + 'galaxy', + 'universe', + 'nasa', + 'spaceship', + 'night', + 'moons', + 'lunar', + 'lunareclipse', + 'lunar eclipse' + ]), + Emoji( + name: 'globe showing Americas', + char: '\u{1F30E}', + shortName: 'earth_americas', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeMap, + keywords: [ + 'Americas', + 'earth', + 'globe', + 'world', + 'uc6', + 'weather', + 'america', + 'space', + 'map', + 'vacation', + 'globe', + 'history', + 'world', + 'usa', + 'united states', + 'united states of america', + 'american', + 'outer space', + 'galaxy', + 'universe', + 'nasa', + 'spaceship', + 'maps', + 'location', + 'locate', + 'local', + 'lost', + 'globes', + 'planet', + 'earth', + 'earthquake', + 'ancient', + 'old' + ]), + Emoji( + name: 'globe showing Europe-Africa', + char: '\u{1F30D}', + shortName: 'earth_africa', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeMap, + keywords: [ + 'Africa', + 'Europe', + 'earth', + 'globe', + 'world', + 'uc6', + 'space', + 'map', + 'vacation', + 'globe', + 'history', + 'world', + 'outer space', + 'galaxy', + 'universe', + 'nasa', + 'spaceship', + 'maps', + 'location', + 'locate', + 'local', + 'lost', + 'globes', + 'planet', + 'earth', + 'earthquake', + 'ancient', + 'old' + ]), + Emoji( + name: 'globe showing Asia-Australia', + char: '\u{1F30F}', + shortName: 'earth_asia', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeMap, + keywords: [ + 'Asia', + 'Australia', + 'earth', + 'globe', + 'world', + 'uc6', + 'space', + 'map', + 'vacation', + 'globe', + 'history', + 'world', + 'outer space', + 'galaxy', + 'universe', + 'nasa', + 'spaceship', + 'maps', + 'location', + 'locate', + 'local', + 'lost', + 'globes', + 'planet', + 'earth', + 'earthquake', + 'ancient', + 'old' + ]), + Emoji( + name: 'ringed planet', + char: '\u{1FA90}', + shortName: 'ringed_planet', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.skyWeather, + keywords: [ + 'uc12', + 'space', + 'saturn', + 'world', + 'outer space', + 'galaxy', + 'universe', + 'nasa', + 'spaceship', + 'saturnine' + ]), + Emoji( + name: 'dizzy', + char: '\u{1F4AB}', + shortName: 'dizzy', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.emotion, + keywords: [ + 'comic', + 'star', + 'uc6', + 'star', + 'star wars', + 'drunk', + 'hit', + 'anime', + 'stars', + 'flustered', + 'dizzy', + 'punch', + 'pow', + 'bam', + 'manga' + ]), + Emoji( + name: 'star', + char: '\u{2B50}', + shortName: 'star', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.skyWeather, + keywords: [ + 'star', + 'uc5', + 'space', + 'sky', + 'star', + 'star wars', + 'fame', + 'texas', + 'outer space', + 'galaxy', + 'universe', + 'nasa', + 'spaceship', + 'stars', + 'famous', + 'celebrity' + ]), + Emoji( + name: 'glowing star', + char: '\u{1F31F}', + shortName: 'star2', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.skyWeather, + keywords: [ + 'glittery', + 'glow', + 'shining', + 'sparkle', + 'star', + 'uc6', + 'space', + 'sky', + 'star', + 'christmas', + 'star wars', + 'fame', + 'sparkle', + 'outer space', + 'galaxy', + 'universe', + 'nasa', + 'spaceship', + 'stars', + 'navidad', + 'xmas', + 'noel', + 'merry christmas', + 'famous', + 'celebrity', + 'bright', + 'shine', + 'twinkle' + ]), + Emoji( + name: 'sparkles', + char: '\u{2728}', + shortName: 'sparkles', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.event, + keywords: [ + 'sparkle', + 'star', + 'uc6', + 'star', + 'birthday', + 'girls night', + 'magic', + 'glitter', + 'bling', + 'fame', + 'sparkle', + 'stars', + 'birth', + 'cumpleaños', + 'anniversaire', + 'bday', + 'ladies night', + 'girls only', + 'girlfriend', + 'spell', + 'genie', + 'magical', + 'jewels', + 'gems', + 'jewel', + 'jewelry', + 'treasure', + 'famous', + 'celebrity', + 'bright', + 'shine', + 'twinkle' + ]), + Emoji( + name: 'high voltage', + char: '\u{26A1}', + shortName: 'zap', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.skyWeather, + keywords: [ + 'danger', + 'electric', + 'electricity', + 'lightning', + 'voltage', + 'zap', + 'uc4', + 'weather', + 'halloween', + 'sky', + 'diarrhea', + 'lightning', + 'electric', + 'harry potter', + 'magic', + 'power', + 'storm', + 'bling', + 'pokemon', + 'samhain', + 'shits', + 'the shits', + 'spell', + 'genie', + 'magical', + 'jewels', + 'gems', + 'jewel', + 'jewelry', + 'treasure' + ]), + Emoji( + name: 'comet', + char: '\u{2604}\u{FE0F}', + shortName: 'comet', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.skyWeather, + keywords: [ + 'space', + 'uc1', + 'space', + 'sky', + 'outer space', + 'galaxy', + 'universe', + 'nasa', + 'spaceship' + ]), + Emoji( + name: 'collision', + char: '\u{1F4A5}', + shortName: 'boom', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.emotion, + keywords: [ + 'boom', + 'comic', + 'uc6', + 'blast', + 'explosion', + 'power', + 'flame', + 'anime', + 'sparkle', + 'boom', + 'explode', + 'burn', + 'match', + 'flames', + 'manga', + 'bright', + 'shine', + 'twinkle' + ]), + Emoji( + name: 'fire', + char: '\u{1F525}', + shortName: 'fire', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.skyWeather, + keywords: [ + 'flame', + 'tool', + 'uc6', + 'love', + 'christmas', + 'wth', + 'hot', + 'harry potter', + 'flame', + 'jewish', + 'porn', + 'independence day', + 'fires', + 'i love you', + 'te amo', + "je t'aime", + 'anniversary', + 'lovin', + 'amour', + 'aimer', + 'amor', + 'valentines day', + 'enamour', + 'lovey', + 'navidad', + 'xmas', + 'noel', + 'merry christmas', + 'what the hell', + 'heat', + 'warm', + 'caliente', + 'chaud', + 'heiß', + 'burn', + 'match', + 'flames', + 'hannukah', + 'hanukkah', + 'israel', + '4th of july' + ]), + Emoji( + name: 'tornado', + char: '\u{1F32A}\u{FE0F}', + shortName: 'cloud_tornado', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.skyWeather, + keywords: [ + 'cloud', + 'whirlwind', + 'uc7', + 'weather', + 'sky', + 'power', + 'storm', + 'clean', + 'texas' + ]), + Emoji( + name: 'rainbow', + char: '\u{1F308}', + shortName: 'rainbow', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.skyWeather, + keywords: [ + 'rain', + 'uc6', + 'weather', + 'gay', + 'sky', + 'rain', + 'rainbow', + 'gay pride', + 'hawaii', + 'color', + 'glitter', + 'easter', + 'irish', + 'mirror', + 'twink', + 'aloha', + 'kawaii', + 'maui', + 'moana', + 'colour', + 'coloring', + 'colouring', + 'drawing', + 'marker', + 'sketch', + 'saint patricks day', + 'st patricks day', + 'leprechaun' + ]), + Emoji( + name: 'sun', + char: '\u{2600}\u{FE0F}', + shortName: 'sunny', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.skyWeather, + keywords: [ + 'bright', + 'rays', + 'sunny', + 'uc1', + 'weather', + 'sun', + 'space', + 'sky', + 'day', + 'hot', + 'morning', + 'sunglasses', + 'power', + 'california', + 'las vegas', + 'summer', + 'independence day', + 'sunshine', + 'sunny', + 'eclipse', + 'solar', + 'solareclipse', + 'shiny', + 'outer space', + 'galaxy', + 'universe', + 'nasa', + 'spaceship', + 'heat', + 'warm', + 'caliente', + 'chaud', + 'heiß', + 'good morning', + 'shades', + 'lunettes de soleil', + 'sun glasses', + 'vegas', + 'weekend', + '4th of july' + ]), + Emoji( + name: 'sun behind small cloud', + char: '\u{1F324}\u{FE0F}', + shortName: 'white_sun_small_cloud', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.skyWeather, + keywords: [ + 'cloud', + 'sun', + 'uc7', + 'weather', + 'sun', + 'sky', + 'cloud', + 'sunshine', + 'sunny', + 'eclipse', + 'solar', + 'solareclipse', + 'shiny', + 'clouds', + 'nuage' + ]), + Emoji( + name: 'sun behind cloud', + char: '\u{26C5}', + shortName: 'partly_sunny', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.skyWeather, + keywords: [ + 'cloud', + 'sun', + 'uc5', + 'weather', + 'sun', + 'sky', + 'cloud', + 'sunshine', + 'sunny', + 'eclipse', + 'solar', + 'solareclipse', + 'shiny', + 'clouds', + 'nuage' + ]), + Emoji( + name: 'sun behind large cloud', + char: '\u{1F325}\u{FE0F}', + shortName: 'white_sun_cloud', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.skyWeather, + keywords: [ + 'cloud', + 'sun', + 'uc7', + 'weather', + 'sun', + 'sky', + 'cloud', + 'cold', + 'sunshine', + 'sunny', + 'eclipse', + 'solar', + 'solareclipse', + 'shiny', + 'clouds', + 'nuage', + 'chilly', + 'chilled', + 'brisk', + 'freezing', + 'frostbite', + 'icicles' + ]), + Emoji( + name: 'cloud', + char: '\u{2601}\u{FE0F}', + shortName: 'cloud', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.skyWeather, + keywords: [ + 'weather', + 'uc1', + 'weather', + 'sky', + 'cloud', + 'cold', + 'dream', + 'clouds', + 'nuage', + 'chilly', + 'chilled', + 'brisk', + 'freezing', + 'frostbite', + 'icicles', + 'dreams' + ]), + Emoji( + name: 'sun behind rain cloud', + char: '\u{1F326}\u{FE0F}', + shortName: 'white_sun_rain_cloud', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.skyWeather, + keywords: [ + 'cloud', + 'rain', + 'sun', + 'uc7', + 'weather', + 'sun', + 'sky', + 'cloud', + 'rain', + 'cold', + 'sunshine', + 'sunny', + 'eclipse', + 'solar', + 'solareclipse', + 'shiny', + 'clouds', + 'nuage', + 'chilly', + 'chilled', + 'brisk', + 'freezing', + 'frostbite', + 'icicles' + ]), + Emoji( + name: 'cloud with rain', + char: '\u{1F327}\u{FE0F}', + shortName: 'cloud_rain', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.skyWeather, + keywords: [ + 'cloud', + 'rain', + 'uc7', + 'weather', + 'winter', + 'sky', + 'cloud', + 'rain', + 'cold', + 'clouds', + 'nuage', + 'chilly', + 'chilled', + 'brisk', + 'freezing', + 'frostbite', + 'icicles' + ]), + Emoji( + name: 'cloud with lightning and rain', + char: '\u{26C8}\u{FE0F}', + shortName: 'thunder_cloud_rain', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.skyWeather, + keywords: [ + 'cloud', + 'rain', + 'thunder', + 'uc5', + 'weather', + 'sky', + 'cloud', + 'rain', + 'cold', + 'lightning', + 'storm', + 'clouds', + 'nuage', + 'chilly', + 'chilled', + 'brisk', + 'freezing', + 'frostbite', + 'icicles' + ]), + Emoji( + name: 'cloud with lightning', + char: '\u{1F329}\u{FE0F}', + shortName: 'cloud_lightning', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.skyWeather, + keywords: [ + 'cloud', + 'lightning', + 'uc7', + 'weather', + 'halloween', + 'sky', + 'cloud', + 'rain', + 'cold', + 'lightning', + 'storm', + 'samhain', + 'clouds', + 'nuage', + 'chilly', + 'chilled', + 'brisk', + 'freezing', + 'frostbite', + 'icicles' + ]), + Emoji( + name: 'cloud with snow', + char: '\u{1F328}\u{FE0F}', + shortName: 'cloud_snow', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.skyWeather, + keywords: [ + 'cloud', + 'cold', + 'snow', + 'uc7', + 'weather', + 'winter', + 'sky', + 'cloud', + 'snow', + 'cold', + 'clouds', + 'nuage', + 'freeze', + 'frozen', + 'frost', + 'ice cube', + 'chilly', + 'chilled', + 'brisk', + 'freezing', + 'frostbite', + 'icicles' + ]), + Emoji( + name: 'snowflake', + char: '\u{2744}\u{FE0F}', + shortName: 'snowflake', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.skyWeather, + keywords: [ + 'cold', + 'snow', + 'uc1', + 'weather', + 'winter', + 'sky', + 'snow', + 'christmas', + 'cold', + 'freeze', + 'frozen', + 'frost', + 'ice cube', + 'navidad', + 'xmas', + 'noel', + 'merry christmas', + 'chilly', + 'chilled', + 'brisk', + 'freezing', + 'frostbite', + 'icicles' + ]), + Emoji( + name: 'snowman', + char: '\u{2603}\u{FE0F}', + shortName: 'snowman2', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.skyWeather, + keywords: [ + 'cold', + 'snow', + 'uc1', + 'weather', + 'holidays', + 'winter', + 'snow', + 'christmas', + 'cold', + 'holiday', + 'freeze', + 'frozen', + 'frost', + 'ice cube', + 'navidad', + 'xmas', + 'noel', + 'merry christmas', + 'chilly', + 'chilled', + 'brisk', + 'freezing', + 'frostbite', + 'icicles' + ]), + Emoji( + name: 'snowman without snow', + char: '\u{26C4}', + shortName: 'snowman', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.skyWeather, + keywords: [ + 'cold', + 'snow', + 'snowman', + 'uc5', + 'weather', + 'winter', + 'snow', + 'christmas', + 'cold', + 'freeze', + 'frozen', + 'frost', + 'ice cube', + 'navidad', + 'xmas', + 'noel', + 'merry christmas', + 'chilly', + 'chilled', + 'brisk', + 'freezing', + 'frostbite', + 'icicles' + ]), + Emoji( + name: 'wind face', + char: '\u{1F32C}\u{FE0F}', + shortName: 'wind_blowing_face', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.skyWeather, + keywords: [ + 'blow', + 'cloud', + 'face', + 'wind', + 'uc7', + 'weather', + 'winter', + 'smoking', + 'cold', + 'power', + 'dream', + 'autumn', + 'breathe', + 'smoke', + 'cigarette', + 'puff', + 'chilly', + 'chilled', + 'brisk', + 'freezing', + 'frostbite', + 'icicles', + 'dreams', + 'fall', + 'sigh', + 'inhale' + ]), + Emoji( + name: 'dashing away', + char: '\u{1F4A8}', + shortName: 'dash', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.emotion, + keywords: [ + 'comic', + 'dash', + 'running', + 'uc6', + 'cloud', + 'smoking', + 'cold', + 'clouds', + 'nuage', + 'smoke', + 'cigarette', + 'puff', + 'chilly', + 'chilled', + 'brisk', + 'freezing', + 'frostbite', + 'icicles' + ]), + Emoji( + name: 'droplet', + char: '\u{1F4A7}', + shortName: 'droplet', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.skyWeather, + keywords: [ + 'cold', + 'comic', + 'drop', + 'sweat', + 'uc6', + 'weather', + 'sky', + 'rain', + 'sweat', + 'drip', + 'anime', + 'water', + 'manga', + 'water drop' + ]), + Emoji( + name: 'sweat droplets', + char: '\u{1F4A6}', + shortName: 'sweat_drops', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.emotion, + keywords: [ + 'comic', + 'splashing', + 'sweat', + 'uc6', + 'rain', + 'stressed', + 'sweat', + 'clean', + 'drip', + 'porn', + 'anime', + 'water', + 'manga', + 'water drop' + ]), + Emoji( + name: 'umbrella with rain drops', + char: '\u{2614}', + shortName: 'umbrella', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.skyWeather, + keywords: [ + 'clothing', + 'drop', + 'rain', + 'umbrella', + 'uc4', + 'weather', + 'sky', + 'rain', + 'cold', + 'umbrella', + 'england', + 'chilly', + 'chilled', + 'brisk', + 'freezing', + 'frostbite', + 'icicles', + 'united kingdom', + 'london', + 'uk' + ]), + Emoji( + name: 'umbrella', + char: '\u{2602}\u{FE0F}', + shortName: 'umbrella2', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.skyWeather, + keywords: [ + 'clothing', + 'rain', + 'uc1', + 'weather', + 'sky', + 'umbrella', + 'summer', + 'weekend' + ]), + Emoji( + name: 'water wave', + char: '\u{1F30A}', + shortName: 'ocean', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.skyWeather, + keywords: [ + 'ocean', + 'water', + 'wave', + 'uc6', + 'weather', + 'boat', + 'tropical', + 'swim', + 'hawaii', + 'storm', + 'mermaid', + 'california', + 'florida', + 'scuba', + 'waves', + 'ocean', + 'boats', + 'boating', + 'swimming', + 'swimmer', + 'aloha', + 'kawaii', + 'maui', + 'moana', + 'merboy', + 'mergirl', + 'merman', + 'merperson', + 'selkie', + 'undine', + 'atargatis', + 'siren', + 'snorkel', + 'sea' + ]), + Emoji( + name: 'fog', + char: '\u{1F32B}\u{FE0F}', + shortName: 'fog', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.skyWeather, + keywords: [ + 'cloud', + 'uc7', + 'weather', + 'sky', + 'cold', + 'steam', + 'chilly', + 'chilled', + 'brisk', + 'freezing', + 'frostbite', + 'icicles', + 'steaming', + 'piping' + ]), + Emoji( + name: 'green apple', + char: '\u{1F34F}', + shortName: 'green_apple', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodFruit, + keywords: [ + 'apple', + 'fruit', + 'green', + 'uc6', + 'food', + 'fruit', + 'classroom', + 'apples', + 'diet', + 'snacks', + 'picnic', + 'vegetarian', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'snack' + ]), + Emoji( + name: 'red apple', + char: '\u{1F34E}', + shortName: 'apple', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodFruit, + keywords: [ + 'apple', + 'fruit', + 'red', + 'uc6', + 'food', + 'fruit', + 'classroom', + 'creationism', + 'new york', + 'apples', + 'diet', + 'snacks', + 'picnic', + 'vegetarian', + 'snow white', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'adam & eve', + 'adam and eve', + 'snack' + ]), + Emoji( + name: 'pear', + char: '\u{1F350}', + shortName: 'pear', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodFruit, + keywords: [ + 'fruit', + 'uc6', + 'food', + 'fruit', + 'diet', + 'picnic', + 'vegetarian', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger' + ]), + Emoji( + name: 'tangerine', + char: '\u{1F34A}', + shortName: 'tangerine', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodFruit, + keywords: [ + 'fruit', + 'orange', + 'uc6', + 'food', + 'fruit', + 'breakfast', + 'diet', + 'donald trump', + 'florida', + 'citrus', + 'picnic', + 'orange', + 'vegetarian', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'petit dejeuner', + 'trump', + 'juice', + 'lime' + ]), + Emoji( + name: 'lemon', + char: '\u{1F34B}', + shortName: 'lemon', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodFruit, + keywords: [ + 'citrus', + 'fruit', + 'uc6', + 'food', + 'fruit', + 'diet', + 'citrus', + 'vegetarian', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'juice', + 'lime' + ]), + Emoji( + name: 'banana', + char: '\u{1F34C}', + shortName: 'banana', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodFruit, + keywords: [ + 'fruit', + 'uc6', + 'food', + 'fruit', + 'penis', + 'breakfast', + 'monkey', + 'diet', + 'snacks', + 'picnic', + 'vegetarian', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'dick', + 'petit dejeuner', + 'progi', + 'ape', + 'primate', + 'snack' + ]), + Emoji( + name: 'watermelon', + char: '\u{1F349}', + shortName: 'watermelon', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodFruit, + keywords: [ + 'fruit', + 'uc6', + 'food', + 'fruit', + 'diet', + 'summer', + 'picnic', + 'vegetarian', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'weekend' + ]), + Emoji( + name: 'grapes', + char: '\u{1F347}', + shortName: 'grapes', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodFruit, + keywords: [ + 'fruit', + 'grape', + 'uc6', + 'food', + 'fruit', + 'paris', + 'diet', + 'snacks', + 'picnic', + 'vegetarian', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'french', + 'france', + 'snack' + ]), + Emoji( + name: 'blueberries', + char: '\u{1FAD0}', + shortName: 'blueberries', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodFruit, + keywords: [ + 'uc13', + 'food', + 'fruit', + 'breakfast', + 'diet', + 'vegetarian', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'petit dejeuner' + ]), + Emoji( + name: 'strawberry', + char: '\u{1F353}', + shortName: 'strawberry', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodFruit, + keywords: [ + 'berry', + 'fruit', + 'uc6', + 'food', + 'fruit', + 'diet', + 'picnic', + 'vegetarian', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger' + ]), + Emoji( + name: 'melon', + char: '\u{1F348}', + shortName: 'melon', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodFruit, + keywords: [ + 'fruit', + 'uc6', + 'food', + 'fruit', + 'boobs', + 'diet', + 'porn', + 'vegetarian', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'boob', + 'tits', + 'tit', + 'breast' + ]), + Emoji( + name: 'cherries', + char: '\u{1F352}', + shortName: 'cherries', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodFruit, + keywords: [ + 'cherry', + 'fruit', + 'uc6', + 'food', + 'fruit', + 'sex', + 'vagina', + 'pussy', + 'diet', + 'porn', + 'picnic', + 'vegetarian', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'fuck', + 'fucking', + 'horny', + 'humping', + 'condom' + ]), + Emoji( + name: 'peach', + char: '\u{1F351}', + shortName: 'peach', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodFruit, + keywords: [ + 'fruit', + 'uc6', + 'food', + 'fruit', + 'butt', + 'sex', + 'vagina', + 'pussy', + 'diet', + 'porn', + 'picnic', + 'vegetarian', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'ass', + 'booty', + 'fuck', + 'fucking', + 'horny', + 'humping', + 'condom' + ]), + Emoji( + name: 'mango', + char: '\u{1F96D}', + shortName: 'mango', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodFruit, + keywords: [ + 'uc11', + 'food', + 'fruit', + 'tropical', + 'thai', + 'diet', + 'vegetarian', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'pattaya' + ]), + Emoji( + name: 'pineapple', + char: '\u{1F34D}', + shortName: 'pineapple', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodFruit, + keywords: [ + 'fruit', + 'uc6', + 'food', + 'fruit', + 'tropical', + 'hawaii', + 'diet', + 'pineapple', + 'picnic', + 'vegetarian', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'aloha', + 'kawaii', + 'maui', + 'moana', + 'pinapple' + ]), + Emoji( + name: 'coconut', + char: '\u{1F965}', + shortName: 'coconut', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodFruit, + keywords: [ + 'palm', + 'piña colada', + 'uc10', + 'food', + 'fruit', + 'thai', + 'coconut', + 'diet', + 'vegetarian', + 'keto', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'pattaya' + ]), + Emoji( + name: 'kiwi fruit', + char: '\u{1F95D}', + shortName: 'kiwi', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodFruit, + keywords: [ + 'food', + 'fruit', + 'kiwi', + 'uc9', + 'food', + 'fruit', + 'breakfast', + 'diet', + 'vegetarian', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'petit dejeuner' + ]), + Emoji( + name: 'tomato', + char: '\u{1F345}', + shortName: 'tomato', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodFruit, + keywords: [ + 'fruit', + 'vegetable', + 'uc6', + 'food', + 'fruit', + 'vegetables', + 'diet', + 'picnic', + 'vegetarian', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'vegetable', + 'veggie', + 'legume' + ]), + Emoji( + name: 'eggplant', + char: '\u{1F346}', + shortName: 'eggplant', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodVegetable, + keywords: [ + 'aubergine', + 'vegetable', + 'uc6', + 'food', + 'vegetables', + 'penis', + 'sex', + 'diet', + 'porn', + 'vegetarian', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'vegetable', + 'veggie', + 'legume', + 'dick', + 'fuck', + 'fucking', + 'horny', + 'humping' + ]), + Emoji( + name: 'avocado', + char: '\u{1F951}', + shortName: 'avocado', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodVegetable, + keywords: [ + 'avocado', + 'food', + 'fruit', + 'uc9', + 'food', + 'fruit', + 'vegetables', + 'california', + 'diet', + 'avocado', + 'picnic', + 'vegetarian', + 'keto', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'vegetable', + 'veggie', + 'legume', + 'avacado' + ]), + Emoji( + name: 'olive', + char: '\u{1FAD2}', + shortName: 'olive', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodFruit, + keywords: [ + 'uc13', + 'food', + 'italian', + 'vegetarian', + 'appetizer', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'italy', + 'italie', + 'apéro', + 'entrée' + ]), + Emoji( + name: 'broccoli', + char: '\u{1F966}', + shortName: 'broccoli', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodVegetable, + keywords: [ + 'wild cabbage', + 'uc10', + 'food', + 'vegetables', + 'diet', + 'vegetarian', + 'keto', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'vegetable', + 'veggie', + 'legume' + ]), + Emoji( + name: 'leafy green', + char: '\u{1F96C}', + shortName: 'leafy_green', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodVegetable, + keywords: [ + 'uc11', + 'food', + 'vegetables', + 'diet', + 'lettuce', + 'vegetarian', + 'keto', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'vegetable', + 'veggie', + 'legume' + ]), + Emoji( + name: 'bell pepper', + char: '\u{1FAD1}', + shortName: 'bell_pepper', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodVegetable, + keywords: [ + 'uc13', + 'food', + 'diet', + 'vegetarian', + 'appetizer', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'apéro', + 'entrée' + ]), + Emoji( + name: 'cucumber', + char: '\u{1F952}', + shortName: 'cucumber', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodVegetable, + keywords: [ + 'cucumber', + 'food', + 'pickle', + 'vegetable', + 'uc9', + 'food', + 'fruit', + 'vegetables', + 'penis', + 'diet', + 'pickle', + 'picnic', + 'vegetarian', + 'appetizer', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'vegetable', + 'veggie', + 'legume', + 'dick', + 'apéro', + 'entrée' + ]), + Emoji( + name: 'hot pepper', + char: '\u{1F336}\u{FE0F}', + shortName: 'hot_pepper', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodVegetable, + keywords: [ + 'hot', + 'pepper', + 'uc7', + 'food', + 'vegetables', + 'mexican', + 'hot', + 'chili', + 'diet', + 'texas', + 'vegetarian', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'vegetable', + 'veggie', + 'legume', + 'mexico', + 'cinco de mayo', + 'español', + 'heat', + 'warm', + 'caliente', + 'chaud', + 'heiß' + ]), + Emoji( + name: 'ear of corn', + char: '\u{1F33D}', + shortName: 'corn', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodVegetable, + keywords: [ + 'corn', + 'ear', + 'maize', + 'maze', + 'uc6', + 'food', + 'vegetables', + 'diet', + 'farm', + 'picnic', + 'independence day', + 'thanksgiving', + 'vegetarian', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'vegetable', + 'veggie', + 'legume', + '4th of july' + ]), + Emoji( + name: 'carrot', + char: '\u{1F955}', + shortName: 'carrot', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodVegetable, + keywords: [ + 'carrot', + 'food', + 'vegetable', + 'uc9', + 'food', + 'vegetables', + 'penis', + 'diet', + 'vegetarian', + 'appetizer', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'vegetable', + 'veggie', + 'legume', + 'dick', + 'apéro', + 'entrée' + ]), + Emoji( + name: 'garlic', + char: '\u{1F9C4}', + shortName: 'garlic', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodVegetable, + keywords: [ + 'uc12', + 'food', + 'vegetables', + 'diet', + 'vampire', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'vegetable', + 'veggie', + 'legume', + 'dracula' + ]), + Emoji( + name: 'onion', + char: '\u{1F9C5}', + shortName: 'onion', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodVegetable, + keywords: [ + 'uc12', + 'food', + 'cry', + 'vegetables', + 'diet', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'crying', + 'weeping', + 'weep', + 'sob', + 'sobbing', + 'tear', + 'tears', + 'bawling', + 'vegetable', + 'veggie', + 'legume' + ]), + Emoji( + name: 'potato', + char: '\u{1F954}', + shortName: 'potato', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodVegetable, + keywords: [ + 'food', + 'potato', + 'vegetable', + 'uc9', + 'food', + 'vegetables', + 'carbs', + 'vegetarian', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'vegetable', + 'veggie', + 'legume', + 'carbohydrates' + ]), + Emoji( + name: 'roasted sweet potato', + char: '\u{1F360}', + shortName: 'sweet_potato', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodAsian, + keywords: [ + 'potato', + 'roasted', + 'sweet', + 'uc6', + 'food', + 'vegetables', + 'diet', + 'yam', + 'carbs', + 'thanksgiving', + 'vegetarian', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'vegetable', + 'veggie', + 'legume', + 'carbohydrates' + ]), + Emoji( + name: 'croissant', + char: '\u{1F950}', + shortName: 'croissant', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodPrepared, + keywords: [ + 'bread', + 'crescent roll', + 'croissant', + 'food', + 'french', + 'uc9', + 'food', + 'breakfast', + 'paris', + 'bake', + 'picnic', + 'carbs', + 'pastry', + 'vegetarian', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'petit dejeuner', + 'french', + 'france', + 'baking', + 'carbohydrates', + 'pastries', + 'pâtisserie' + ]), + Emoji( + name: 'bagel', + char: '\u{1F96F}', + shortName: 'bagel', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodPrepared, + keywords: [ + 'uc11', + 'food', + 'new york', + 'breakfast', + 'bake', + 'carbs', + 'vegetarian', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'petit dejeuner', + 'baking', + 'carbohydrates' + ]), + Emoji( + name: 'bread', + char: '\u{1F35E}', + shortName: 'bread', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodPrepared, + keywords: [ + 'loaf', + 'uc6', + 'food', + 'sandwich', + 'breakfast', + 'bake', + 'toast', + 'carbs', + 'vegetarian', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'sanwiches', + 'petit dejeuner', + 'baking', + 'carbohydrates' + ]), + Emoji( + name: 'baguette bread', + char: '\u{1F956}', + shortName: 'french_bread', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodPrepared, + keywords: [ + 'baguette', + 'bread', + 'food', + 'french', + 'uc9', + 'food', + 'penis', + 'paris', + 'bake', + 'picnic', + 'carbs', + 'vegetarian', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'dick', + 'french', + 'france', + 'baking', + 'carbohydrates' + ]), + Emoji( + name: 'flatbread', + char: '\u{1FAD3}', + shortName: 'flatbread', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodPrepared, + keywords: [ + 'uc13', + 'food', + 'carbs', + 'vegetarian', + 'pita', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'carbohydrates', + 'naan', + 'tortilla', + 'chepati', + 'focaccia', + 'fry bread', + 'lavash', + 'matzah', + 'roti' + ]), + Emoji( + name: 'pretzel', + char: '\u{1F968}', + shortName: 'pretzel', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodPrepared, + keywords: [ + 'uc10', + 'food', + 'bake', + 'snacks', + 'carbs', + 'german', + 'vegetarian', + 'appetizer', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'baking', + 'snack', + 'carbohydrates', + 'oktoberfest', + 'octoberfest', + 'bratwurst', + 'apéro', + 'entrée' + ]), + Emoji( + name: 'cheese wedge', + char: '\u{1F9C0}', + shortName: 'cheese', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodPrepared, + keywords: [ + 'cheese', + 'uc8', + 'food', + 'paris', + 'picnic', + 'cheese', + 'vegetarian', + 'keto', + 'appetizer', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'french', + 'france', + 'apéro', + 'entrée' + ]), + Emoji( + name: 'egg', + char: '\u{1F95A}', + shortName: 'egg', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodPrepared, + keywords: [ + 'egg', + 'food', + 'uc9', + 'food', + 'breakfast', + 'easter', + 'diet', + 'eggs', + 'vegetarian', + 'keto', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'petit dejeuner' + ]), + Emoji( + name: 'cooking', + char: '\u{1F373}', + shortName: 'cooking', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodPrepared, + keywords: [ + 'egg', + 'frying', + 'pan', + 'uc6', + 'food', + 'breakfast', + 'eggs', + 'restaurant', + 'vegetarian', + 'keto', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'petit dejeuner' + ]), + Emoji( + name: 'butter', + char: '\u{1F9C8}', + shortName: 'butter', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodPrepared, + keywords: [ + 'uc12', + 'food', + 'breakfast', + 'condiment', + 'butter', + 'keto', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'petit dejeuner', + 'condiments', + 'seasoning', + 'topping', + 'margarine', + 'ghee' + ]), + Emoji( + name: 'pancakes', + char: '\u{1F95E}', + shortName: 'pancakes', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodPrepared, + keywords: [ + 'crêpe', + 'food', + 'hotcake', + 'pancake', + 'uc9', + 'food', + 'breakfast', + 'carbs', + 'restaurant', + 'vegetarian', + 'pancake', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'petit dejeuner', + 'carbohydrates', + 'pannenkoeken', + 'maple syrup', + 'pfannkuchen', + 'panqueques', + 'crêpes' + ]), + Emoji( + name: 'waffle', + char: '\u{1F9C7}', + shortName: 'waffle', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodPrepared, + keywords: [ + 'uc12', + 'food', + 'breakfast', + 'carbs', + 'waffles', + 'vegetarian', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'petit dejeuner', + 'carbohydrates', + 'eggo', + 'gaufre', + 'gofre', + 'waffel', + 'wafel' + ]), + Emoji( + name: 'bacon', + char: '\u{1F953}', + shortName: 'bacon', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodPrepared, + keywords: [ + 'bacon', + 'food', + 'meat', + 'uc9', + 'food', + 'breakfast', + 'pig', + 'meat', + 'restaurant', + 'keto', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'petit dejeuner', + 'pork' + ]), + Emoji( + name: 'cut of meat', + char: '\u{1F969}', + shortName: 'cut_of_meat', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodPrepared, + keywords: [ + 'chop', + 'lambchop', + 'porkchop', + 'steak', + 'uc10', + 'food', + 'texas', + 'dinner', + 'steak', + 'meat', + 'restaurant', + 'keto', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'lunch' + ]), + Emoji( + name: 'poultry leg', + char: '\u{1F357}', + shortName: 'poultry_leg', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodPrepared, + keywords: [ + 'bone', + 'chicken', + 'leg', + 'poultry', + 'uc6', + 'food', + 'chicken leg', + 'disney', + 'viking', + 'dinner', + 'meat', + 'thanksgiving', + 'restaurant', + 'keto', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'cartoon', + 'knight', + 'lunch' + ]), + Emoji( + name: 'meat on bone', + char: '\u{1F356}', + shortName: 'meat_on_bone', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodPrepared, + keywords: [ + 'bone', + 'meat', + 'uc6', + 'food', + 'beef', + 'brazil', + 'viking', + 'dinner', + 'meat', + 'thanksgiving', + 'restaurant', + 'keto', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'brasil', + 'bresil', + 'knight', + 'lunch' + ]), + Emoji( + name: 'hot dog', + char: '\u{1F32D}', + shortName: 'hotdog', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodPrepared, + keywords: [ + 'frankfurter', + 'hotdog', + 'sausage', + 'uc8', + 'food', + 'america', + 'new york', + 'sandwich', + 'dinner', + 'franks', + 'independence day', + 'german', + 'restaurant', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'usa', + 'united states', + 'united states of america', + 'american', + 'sanwiches', + 'lunch', + 'sausage', + 'hot dog', + '4th of july', + 'oktoberfest', + 'octoberfest', + 'bratwurst' + ]), + Emoji( + name: 'hamburger', + char: '\u{1F354}', + shortName: 'hamburger', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodPrepared, + keywords: [ + 'burger', + 'uc6', + 'food', + 'america', + 'boys night', + 'sandwich', + 'beef', + 'mcdonalds', + 'dinner', + 'burger', + 'cheese', + 'independence day', + 'restaurant', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'usa', + 'united states', + 'united states of america', + 'american', + 'guys night', + 'sanwiches', + 'ronald mcdonald', + 'macdo', + 'lunch', + 'cheeseburger', + 'cheese burger', + '4th of july' + ]), + Emoji( + name: 'french fries', + char: '\u{1F35F}', + shortName: 'fries', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodPrepared, + keywords: [ + 'french', + 'fries', + 'uc6', + 'food', + 'america', + 'chips', + 'mcdonalds', + 'dinner', + 'carbs', + 'restaurant', + 'vegetarian', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'usa', + 'united states', + 'united states of america', + 'american', + 'ronald mcdonald', + 'macdo', + 'lunch', + 'carbohydrates' + ]), + Emoji( + name: 'pizza', + char: '\u{1F355}', + shortName: 'pizza', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodPrepared, + keywords: [ + 'cheese', + 'slice', + 'uc6', + 'food', + 'italian', + 'boys night', + 'new york', + 'dinner', + 'cheese', + 'carbs', + 'restaurant', + 'keto', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'italy', + 'italie', + 'guys night', + 'lunch', + 'carbohydrates' + ]), + Emoji( + name: 'sandwich', + char: '\u{1F96A}', + shortName: 'sandwich', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodPrepared, + keywords: [ + 'bread', + 'uc10', + 'food', + 'sandwich', + 'dinner', + 'restaurant', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'sanwiches', + 'lunch' + ]), + Emoji( + name: 'stuffed flatbread', + char: '\u{1F959}', + shortName: 'stuffed_flatbread', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodPrepared, + keywords: [ + 'falafel', + 'flatbread', + 'food', + 'gyro', + 'kebab', + 'stuffed', + 'uc9', + 'food', + 'sandwich', + 'dinner', + 'german', + 'restaurant', + 'vegetarian', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'sanwiches', + 'lunch', + 'oktoberfest', + 'octoberfest', + 'bratwurst' + ]), + Emoji( + name: 'falafel', + char: '\u{1F9C6}', + shortName: 'falafel', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodPrepared, + keywords: [ + 'uc12', + 'food', + 'dinner', + 'meatball', + 'felafel', + 'vegetarian', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'lunch', + 'chickpeas', + 'fava beans', + 'levantine', + 'meze' + ]), + Emoji( + name: 'taco', + char: '\u{1F32E}', + shortName: 'taco', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodPrepared, + keywords: [ + 'mexican', + 'uc8', + 'food', + 'mexican', + 'vagina', + 'tacos', + 'hola', + 'pussy', + 'porn', + 'texas', + 'dinner', + 'restaurant', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'mexico', + 'cinco de mayo', + 'español', + 'condom', + 'lunch' + ]), + Emoji( + name: 'burrito', + char: '\u{1F32F}', + shortName: 'burrito', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodPrepared, + keywords: [ + 'mexican', + 'wrap', + 'uc8', + 'food', + 'mexican', + 'hola', + 'texas', + 'dinner', + 'restaurant', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'mexico', + 'cinco de mayo', + 'español', + 'lunch' + ]), + Emoji( + name: 'tamale', + char: '\u{1FAD4}', + shortName: 'tamale', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodPrepared, + keywords: [ + 'uc13', + 'food', + 'dinner', + 'tamal', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'lunch', + 'chuchito', + 'pastelle', + 'pasteles', + 'hallaca', + 'zacahuil', + 'corunda', + 'bollo', + 'humita', + 'binaki', + 'masa', + 'dukunu', + 'paches' + ]), + Emoji( + name: 'green salad', + char: '\u{1F957}', + shortName: 'salad', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodPrepared, + keywords: [ + 'food', + 'green', + 'salad', + 'uc9', + 'food', + 'vegetables', + 'diet', + 'dinner', + 'lettuce', + 'picnic', + 'restaurant', + 'vegetarian', + 'keto', + 'appetizer', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'vegetable', + 'veggie', + 'legume', + 'lunch', + 'apéro', + 'entrée' + ]), + Emoji( + name: 'shallow pan of food', + char: '\u{1F958}', + shortName: 'shallow_pan_of_food', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodPrepared, + keywords: [ + 'casserole', + 'food', + 'paella', + 'pan', + 'shallow', + 'uc9', + 'food', + 'mexican', + 'barcelona', + 'beef', + 'brazil', + 'dinner', + 'restaurant', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'mexico', + 'cinco de mayo', + 'español', + 'españa', + 'spanish', + 'brasil', + 'bresil', + 'lunch' + ]), + Emoji( + name: 'fondue', + char: '\u{1FAD5}', + shortName: 'fondue', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodPrepared, + keywords: [ + 'uc13', + 'food', + 'dinner', + 'cheese', + 'vegetarian', + 'shabu', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'lunch', + 'hot pot' + ]), + Emoji( + name: 'canned food', + char: '\u{1F96B}', + shortName: 'canned_food', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodPrepared, + keywords: [ + 'can', + 'uc10', + 'food', + 'soup', + 'dinner', + 'restaurant', + 'vegetarian', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'lunch' + ]), + Emoji( + name: 'spaghetti', + char: '\u{1F35D}', + shortName: 'spaghetti', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodAsian, + keywords: [ + 'pasta', + 'uc6', + 'food', + 'noodles', + 'pasta', + 'italian', + 'dinner', + 'meatball', + 'restaurant', + 'vegetarian', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'noodle', + 'pâtes', + 'italy', + 'italie', + 'lunch' + ]), + Emoji( + name: 'steaming bowl', + char: '\u{1F35C}', + shortName: 'ramen', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodAsian, + keywords: [ + 'bowl', + 'noodle', + 'ramen', + 'steaming', + 'uc6', + 'food', + 'noodles', + 'ramen', + 'pasta', + 'japan', + 'steam', + 'thai', + 'chinese', + 'soup', + 'dinner', + 'restaurant', + 'vegetarian', + 'bone broth', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'noodle', + 'pâtes', + 'japanese', + 'ninja', + 'steaming', + 'piping', + 'pattaya', + 'chinois', + 'asian', + 'chine', + 'lunch' + ]), + Emoji( + name: 'pot of food', + char: '\u{1F372}', + shortName: 'stew', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodPrepared, + keywords: [ + 'pot', + 'stew', + 'uc6', + 'food', + 'steam', + 'thai', + 'brazil', + 'soup', + 'dinner', + 'stew', + 'thanksgiving', + 'restaurant', + 'bone broth', + 'shabu', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'steaming', + 'piping', + 'pattaya', + 'brasil', + 'bresil', + 'lunch', + 'braise', + 'hot pot' + ]), + Emoji( + name: 'curry rice', + char: '\u{1F35B}', + shortName: 'curry', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodAsian, + keywords: [ + 'curry', + 'rice', + 'uc6', + 'food', + 'japan', + 'thai', + 'dinner', + 'restaurant', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'japanese', + 'ninja', + 'pattaya', + 'lunch' + ]), + Emoji( + name: 'sushi', + char: '\u{1F363}', + shortName: 'sushi', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodAsian, + keywords: [ + 'sushi', + 'uc6', + 'food', + 'sushi', + 'japan', + 'california', + 'diet', + 'seafood', + 'dinner', + 'restaurant', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'japanese', + 'ninja', + 'lunch' + ]), + Emoji( + name: 'bento box', + char: '\u{1F371}', + shortName: 'bento', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodAsian, + keywords: [ + 'bento', + 'box', + 'uc6', + 'food', + 'sushi', + 'japan', + 'dinner', + 'restaurant', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'japanese', + 'ninja', + 'lunch' + ]), + Emoji( + name: 'dumpling', + char: '\u{1F95F}', + shortName: 'dumpling', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodAsian, + keywords: [ + 'empanada', + 'gyōza', + 'jiaozi', + 'pierogi', + 'potsticker', + 'uc10', + 'food', + 'chinese', + 'dinner', + 'dumpling', + 'pastry', + 'restaurant', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'chinois', + 'asian', + 'chine', + 'lunch', + 'Empanada', + 'Gyōza', + 'Pierogi', + 'pastries', + 'pâtisserie' + ]), + Emoji( + name: 'oyster', + char: '\u{1F9AA}', + shortName: 'oyster', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodMarine, + keywords: [ + 'uc12', + 'animal', + 'food', + 'seafood', + 'dinner', + 'ocean', + 'crustacean', + 'half shell', + 'animals', + 'animal kingdom', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'lunch', + 'sea', + 'pearl' + ]), + Emoji( + name: 'fried shrimp', + char: '\u{1F364}', + shortName: 'fried_shrimp', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodAsian, + keywords: [ + 'fried', + 'prawn', + 'shrimp', + 'tempura', + 'uc6', + 'food', + 'japan', + 'prawn', + 'seafood', + 'dinner', + 'crustacean', + 'restaurant', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'japanese', + 'ninja', + 'lunch' + ]), + Emoji( + name: 'rice ball', + char: '\u{1F359}', + shortName: 'rice_ball', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodAsian, + keywords: [ + 'Japanese', + 'ball', + 'rice', + 'uc6', + 'food', + 'sushi', + 'japan', + 'snacks', + 'dinner', + 'restaurant', + 'vegetarian', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'japanese', + 'ninja', + 'snack', + 'lunch' + ]), + Emoji( + name: 'cooked rice', + char: '\u{1F35A}', + shortName: 'rice', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodAsian, + keywords: [ + 'cooked', + 'rice', + 'uc6', + 'food', + 'sushi', + 'japan', + 'thai', + 'chinese', + 'brazil', + 'dinner', + 'carbs', + 'restaurant', + 'vegetarian', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'japanese', + 'ninja', + 'pattaya', + 'chinois', + 'asian', + 'chine', + 'brasil', + 'bresil', + 'lunch', + 'carbohydrates' + ]), + Emoji( + name: 'rice cracker', + char: '\u{1F358}', + shortName: 'rice_cracker', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodAsian, + keywords: [ + 'cracker', + 'rice', + 'uc6', + 'food', + 'sushi', + 'chinese', + 'vegetarian', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'chinois', + 'asian', + 'chine' + ]), + Emoji( + name: 'fish cake with swirl', + char: '\u{1F365}', + shortName: 'fish_cake', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodAsian, + keywords: [ + 'cake', + 'fish', + 'pastry', + 'swirl', + 'uc6', + 'food', + 'sushi', + 'japan', + 'seafood', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'japanese', + 'ninja' + ]), + Emoji( + name: 'fortune cookie', + char: '\u{1F960}', + shortName: 'fortune_cookie', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodAsian, + keywords: [ + 'prophecy', + 'uc10', + 'food', + 'luck', + 'sugar', + 'cookie', + 'chinese', + 'bake', + 'carbs', + 'pastry', + 'restaurant', + 'vegetarian', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'good luck', + 'lucky', + 'junk food', + 'dessert', + 'sweets', + 'cookies', + 'chinois', + 'asian', + 'chine', + 'baking', + 'carbohydrates', + 'pastries', + 'pâtisserie' + ]), + Emoji( + name: 'moon cake', + char: '\u{1F96E}', + shortName: 'moon_cake', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodAsian, + keywords: [ + 'uc11', + 'food', + 'cake', + 'celebrate', + 'sugar', + 'chinese', + 'bake', + 'carbs', + 'pastry', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'cupcake', + 'celebration', + 'event', + 'celebrating', + 'festa', + 'parties', + 'events', + 'new years', + 'new year', + 'fiesta', + 'fete', + 'newyear', + 'party', + 'festive', + 'festival', + 'yolo', + 'festejar', + 'junk food', + 'dessert', + 'sweets', + 'chinois', + 'asian', + 'chine', + 'baking', + 'carbohydrates', + 'pastries', + 'pâtisserie' + ]), + Emoji( + name: 'oden', + char: '\u{1F362}', + shortName: 'oden', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodAsian, + keywords: [ + 'kebab', + 'seafood', + 'skewer', + 'stick', + 'uc6', + 'food', + 'japan', + 'dinner', + 'restaurant', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'japanese', + 'ninja', + 'lunch' + ]), + Emoji( + name: 'dango', + char: '\u{1F361}', + shortName: 'dango', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodAsian, + keywords: [ + 'Japanese', + 'dessert', + 'skewer', + 'stick', + 'sweet', + 'uc6', + 'food', + 'japan', + 'sugar', + 'restaurant', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'japanese', + 'ninja', + 'junk food', + 'dessert', + 'sweets' + ]), + Emoji( + name: 'shaved ice', + char: '\u{1F367}', + shortName: 'shaved_ice', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodSweet, + keywords: [ + 'dessert', + 'ice', + 'shaved', + 'sweet', + 'uc6', + 'food', + 'ice cream', + 'hawaii', + 'sugar', + 'disney', + 'summer', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'aloha', + 'kawaii', + 'maui', + 'moana', + 'junk food', + 'dessert', + 'sweets', + 'cartoon', + 'weekend' + ]), + Emoji( + name: 'ice cream', + char: '\u{1F368}', + shortName: 'ice_cream', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodSweet, + keywords: [ + 'cream', + 'dessert', + 'ice', + 'sweet', + 'uc6', + 'food', + 'ice cream', + 'sugar', + 'summer', + 'restaurant', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'junk food', + 'dessert', + 'sweets', + 'weekend' + ]), + Emoji( + name: 'soft ice cream', + char: '\u{1F366}', + shortName: 'icecream', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodSweet, + keywords: [ + 'cream', + 'dessert', + 'ice', + 'icecream', + 'soft', + 'sweet', + 'uc6', + 'food', + 'italian', + 'ice cream', + 'sugar', + 'summer', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'italy', + 'italie', + 'junk food', + 'dessert', + 'sweets', + 'weekend' + ]), + Emoji( + name: 'pie', + char: '\u{1F967}', + shortName: 'pie', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodSweet, + keywords: [ + 'uc10', + 'food', + 'sugar', + 'bake', + 'carbs', + 'pastry', + 'quiche', + 'thanksgiving', + 'restaurant', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'junk food', + 'dessert', + 'sweets', + 'baking', + 'carbohydrates', + 'pastries', + 'pâtisserie', + 'tart' + ]), + Emoji( + name: 'cupcake', + char: '\u{1F9C1}', + shortName: 'cupcake', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodSweet, + keywords: [ + 'uc11', + 'food', + 'birthday', + 'happy birthday', + 'cake', + 'pink', + 'celebrate', + 'sugar', + 'bake', + 'carbs', + 'pastry', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'birth', + 'cumpleaños', + 'anniversaire', + 'bday', + 'Bon anniversaire', + 'joyeux anniversaire', + 'buon compleanno', + 'feliz cumpleaños', + 'alles Gute zum Geburtstag', + 'feliz Aniversário', + 'Gratulerer med dagen', + 'cupcake', + 'rose', + 'celebration', + 'event', + 'celebrating', + 'festa', + 'parties', + 'events', + 'new years', + 'new year', + 'fiesta', + 'fete', + 'newyear', + 'party', + 'festive', + 'festival', + 'yolo', + 'festejar', + 'junk food', + 'dessert', + 'sweets', + 'baking', + 'carbohydrates', + 'pastries', + 'pâtisserie' + ]), + Emoji( + name: 'shortcake', + char: '\u{1F370}', + shortName: 'cake', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodSweet, + keywords: [ + 'cake', + 'dessert', + 'pastry', + 'slice', + 'sweet', + 'uc6', + 'food', + 'cake', + 'sugar', + 'bake', + 'carbs', + 'pastry', + 'restaurant', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'cupcake', + 'junk food', + 'dessert', + 'sweets', + 'baking', + 'carbohydrates', + 'pastries', + 'pâtisserie' + ]), + Emoji( + name: 'birthday cake', + char: '\u{1F382}', + shortName: 'birthday', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodSweet, + keywords: [ + 'birthday', + 'cake', + 'celebration', + 'dessert', + 'pastry', + 'sweet', + 'uc6', + 'food', + 'holidays', + 'birthday', + 'happy birthday', + 'cake', + 'celebrate', + 'sugar', + 'facebook', + 'bake', + 'pastry', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'holiday', + 'birth', + 'cumpleaños', + 'anniversaire', + 'bday', + 'Bon anniversaire', + 'joyeux anniversaire', + 'buon compleanno', + 'feliz cumpleaños', + 'alles Gute zum Geburtstag', + 'feliz Aniversário', + 'Gratulerer med dagen', + 'cupcake', + 'celebration', + 'event', + 'celebrating', + 'festa', + 'parties', + 'events', + 'new years', + 'new year', + 'fiesta', + 'fete', + 'newyear', + 'party', + 'festive', + 'festival', + 'yolo', + 'festejar', + 'junk food', + 'dessert', + 'sweets', + 'baking', + 'pastries', + 'pâtisserie' + ]), + Emoji( + name: 'custard', + char: '\u{1F36E}', + shortName: 'custard', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodSweet, + keywords: [ + 'dessert', + 'pudding', + 'sweet', + 'uc6', + 'food', + 'sugar', + 'bake', + 'restaurant', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'junk food', + 'dessert', + 'sweets', + 'baking' + ]), + Emoji( + name: 'lollipop', + char: '\u{1F36D}', + shortName: 'lollipop', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodSweet, + keywords: [ + 'candy', + 'dessert', + 'sweet', + 'uc6', + 'food', + 'halloween', + 'candy', + 'sugar', + 'disney', + 'snacks', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'samhain', + 'candy cane', + 'candycane', + 'lolipop', + 'bonbon', + 'junk food', + 'dessert', + 'sweets', + 'cartoon', + 'snack' + ]), + Emoji( + name: 'candy', + char: '\u{1F36C}', + shortName: 'candy', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodSweet, + keywords: [ + 'dessert', + 'sweet', + 'uc6', + 'food', + 'halloween', + 'candy', + 'sugar', + 'snacks', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'samhain', + 'candy cane', + 'candycane', + 'lolipop', + 'bonbon', + 'junk food', + 'dessert', + 'sweets', + 'snack' + ]), + Emoji( + name: 'chocolate bar', + char: '\u{1F36B}', + shortName: 'chocolate_bar', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodSweet, + keywords: [ + 'bar', + 'chocolate', + 'dessert', + 'sweet', + 'uc6', + 'food', + 'halloween', + 'love', + 'girls night', + 'candy', + 'sugar', + 'easter', + 'cocoa', + 'snacks', + 'rich', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'samhain', + 'i love you', + 'te amo', + "je t'aime", + 'anniversary', + 'lovin', + 'amour', + 'aimer', + 'amor', + 'valentines day', + 'enamour', + 'lovey', + 'ladies night', + 'girls only', + 'girlfriend', + 'candy cane', + 'candycane', + 'lolipop', + 'bonbon', + 'junk food', + 'dessert', + 'sweets', + 'hot chocolate', + 'snack', + 'grand', + 'expensive', + 'fancy' + ]), + Emoji( + name: 'popcorn', + char: '\u{1F37F}', + shortName: 'popcorn', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodPrepared, + keywords: [ + 'popcorn', + 'uc8', + 'food', + 'celebrate', + 'snacks', + 'carbs', + 'vegetarian', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'celebration', + 'event', + 'celebrating', + 'festa', + 'parties', + 'events', + 'new years', + 'new year', + 'fiesta', + 'fete', + 'newyear', + 'party', + 'festive', + 'festival', + 'yolo', + 'festejar', + 'snack', + 'carbohydrates' + ]), + Emoji( + name: 'doughnut', + char: '\u{1F369}', + shortName: 'doughnut', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodSweet, + keywords: [ + 'dessert', + 'donut', + 'sweet', + 'uc6', + 'food', + 'sex', + 'vagina', + 'breakfast', + 'doughnut', + 'sugar', + 'bake', + 'carbs', + 'pastry', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'fuck', + 'fucking', + 'horny', + 'humping', + 'petit dejeuner', + 'donut', + 'junk food', + 'dessert', + 'sweets', + 'baking', + 'carbohydrates', + 'pastries', + 'pâtisserie' + ]), + Emoji( + name: 'cookie', + char: '\u{1F36A}', + shortName: 'cookie', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodSweet, + keywords: [ + 'dessert', + 'sweet', + 'uc6', + 'food', + 'christmas', + 'vagina', + 'sugar', + 'cookie', + 'bake', + 'snacks', + 'carbs', + 'pastry', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'navidad', + 'xmas', + 'noel', + 'merry christmas', + 'junk food', + 'dessert', + 'sweets', + 'cookies', + 'baking', + 'snack', + 'carbohydrates', + 'pastries', + 'pâtisserie' + ]), + Emoji( + name: 'chestnut', + char: '\u{1F330}', + shortName: 'chestnut', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodVegetable, + keywords: [ + 'plant', + 'uc6', + 'food', + 'nature', + 'plant', + 'christmas', + 'nut', + 'keto', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'plants', + 'navidad', + 'xmas', + 'noel', + 'merry christmas', + 'nuts' + ]), + Emoji( + name: 'peanuts', + char: '\u{1F95C}', + shortName: 'peanuts', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodVegetable, + keywords: [ + 'food', + 'nut', + 'peanut', + 'vegetable', + 'uc9', + 'food', + 'vegetables', + 'squirrel', + 'nut', + 'snacks', + 'picnic', + 'appetizer', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'vegetable', + 'veggie', + 'legume', + 'nuts', + 'snack', + 'apéro', + 'entrée' + ]), + Emoji( + name: 'honey pot', + char: '\u{1F36F}', + shortName: 'honey_pot', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodSweet, + keywords: [ + 'honey', + 'honeypot', + 'pot', + 'sweet', + 'uc6', + 'food', + 'vagina', + 'breakfast', + 'sugar', + 'condiment', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'petit dejeuner', + 'junk food', + 'dessert', + 'sweets', + 'condiments', + 'seasoning', + 'topping' + ]), + Emoji( + name: 'glass of milk', + char: '\u{1F95B}', + shortName: 'milk', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.drink, + keywords: [ + 'drink', + 'glass', + 'milk', + 'uc9', + 'drink', + 'christmas', + 'dinner', + 'restaurant', + 'drinks', + 'beverage', + 'navidad', + 'xmas', + 'noel', + 'merry christmas', + 'lunch' + ]), + Emoji( + name: 'baby bottle', + char: '\u{1F37C}', + shortName: 'baby_bottle', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.drink, + keywords: [ + 'baby', + 'bottle', + 'drink', + 'milk', + 'uc6', + 'food', + 'drink', + 'baby', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'drinks', + 'beverage', + 'kid', + 'babies', + 'infant', + 'infants', + 'crying kid', + 'bebe', + 'little', + 'petite', + 'bambino' + ]), + Emoji( + name: 'hot beverage', + char: '\u{2615}', + shortName: 'coffee', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.drink, + keywords: [ + 'beverage', + 'coffee', + 'drink', + 'hot', + 'steaming', + 'tea', + 'uc4', + 'drink', + 'caffeine', + 'steam', + 'morning', + 'coffee', + 'breakfast', + 'cocoa', + 'diet', + 'restaurant', + 'keto', + 'drinks', + 'beverage', + 'decaffeinated', + 'decaf', + 'steaming', + 'piping', + 'good morning', + 'starbucks', + 'petit dejeuner', + 'hot chocolate' + ]), + Emoji( + name: 'teacup without handle', + char: '\u{1F375}', + shortName: 'tea', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.drink, + keywords: [ + 'beverage', + 'cup', + 'drink', + 'tea', + 'teacup', + 'uc6', + 'drink', + 'japan', + 'caffeine', + 'steam', + 'morning', + 'tea', + 'breakfast', + 'england', + 'chinese', + 'diet', + 'restaurant', + 'keto', + 'drinks', + 'beverage', + 'japanese', + 'ninja', + 'decaffeinated', + 'decaf', + 'steaming', + 'piping', + 'good morning', + 'iced tea', + 'petit dejeuner', + 'united kingdom', + 'london', + 'uk', + 'chinois', + 'asian', + 'chine' + ]), + Emoji( + name: 'teapot', + char: '\u{1FAD6}', + shortName: 'teapot', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.drink, + keywords: [ + 'uc13', + 'japan', + 'breakfast', + 'chinese', + 'kettle', + 'japanese', + 'ninja', + 'petit dejeuner', + 'chinois', + 'asian', + 'chine', + 'teakettle', + 'caldron', + 'boiler', + 'théière', + 'teiera', + 'tetera', + 'infuser', + 'kyūsu', + 'tetsubin', + 'yixing' + ]), + Emoji( + name: 'mate', + char: '\u{1F9C9}', + shortName: 'mate', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.drink, + keywords: [ + 'uc12', + 'drink', + 'caffeine', + 'tea', + 'breakfast', + 'yerba', + 'drinks', + 'beverage', + 'decaffeinated', + 'decaf', + 'iced tea', + 'petit dejeuner', + 'chimarrão', + 'cimarrón', + 'maté' + ]), + Emoji( + name: 'bubble tea', + char: '\u{1F9CB}', + shortName: 'bubble_tea', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.drink, + keywords: [ + 'uc13', + 'drink', + 'japan', + 'thai', + 'chinese', + 'drinks', + 'beverage', + 'japanese', + 'ninja', + 'pattaya', + 'chinois', + 'asian', + 'chine' + ]), + Emoji( + name: 'beverage box', + char: '\u{1F9C3}', + shortName: 'beverage_box', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.drink, + keywords: [ + 'uc12', + 'drink', + 'apples', + 'citrus', + 'snacks', + 'drinks', + 'beverage', + 'juice', + 'lime', + 'snack' + ]), + Emoji( + name: 'cup with straw', + char: '\u{1F964}', + shortName: 'cup_with_straw', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.drink, + keywords: [ + 'uc10', + 'drink', + 'dinner', + 'soda', + 'restaurant', + 'keto', + 'drinks', + 'beverage', + 'lunch', + 'cola', + 'milkshake', + 'soft drink', + 'sippy cup', + 'coke', + 'pepsi' + ]), + Emoji( + name: 'sake', + char: '\u{1F376}', + shortName: 'sake', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.drink, + keywords: [ + 'bar', + 'beverage', + 'bottle', + 'cup', + 'drink', + 'uc6', + 'drink', + 'japan', + 'alcohol', + 'sake', + 'girls night', + 'chinese', + 'dinner', + 'restaurant', + 'drinks', + 'beverage', + 'japanese', + 'ninja', + 'liquor', + 'booze', + 'ladies night', + 'girls only', + 'girlfriend', + 'chinois', + 'asian', + 'chine', + 'lunch' + ]), + Emoji( + name: 'beer mug', + char: '\u{1F37A}', + shortName: 'beer', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.drink, + keywords: [ + 'bar', + 'beer', + 'drink', + 'mug', + 'uc6', + 'drink', + 'japan', + 'alcohol', + 'beer', + 'cocktail', + 'friend', + 'irish', + 'dinner', + 'german', + 'restaurant', + 'drinks', + 'beverage', + 'japanese', + 'ninja', + 'liquor', + 'booze', + 'martini', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'saint patricks day', + 'st patricks day', + 'leprechaun', + 'lunch', + 'oktoberfest', + 'octoberfest', + 'bratwurst' + ]), + Emoji( + name: 'clinking beer mugs', + char: '\u{1F37B}', + shortName: 'beers', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.drink, + keywords: [ + 'bar', + 'beer', + 'clink', + 'drink', + 'mug', + 'uc6', + 'drink', + 'alcohol', + 'cheers', + 'beer', + 'cocktail', + 'thank you', + 'girls night', + 'boys night', + 'harry potter', + 'friend', + 'celebrate', + 'irish', + 'toast', + 'german', + 'restaurant', + 'drinks', + 'beverage', + 'liquor', + 'booze', + 'gān bēi', + 'Na zdravi', + 'Proost', + 'Prost', + 'Sláinte', + 'Cin cin', + 'Kanpai', + 'Na zdrowie', + 'Saúde', + 'На здоровье', + 'Salud', + 'Skål', + 'Sei gesund', + 'santé', + 'martini', + 'thanks', + 'thankful', + 'praise', + 'gracias', + 'merci', + 'thankyou', + 'acceptable', + 'ladies night', + 'girls only', + 'girlfriend', + 'guys night', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'celebration', + 'event', + 'celebrating', + 'festa', + 'parties', + 'events', + 'new years', + 'new year', + 'fiesta', + 'fete', + 'newyear', + 'party', + 'festive', + 'festival', + 'yolo', + 'festejar', + 'saint patricks day', + 'st patricks day', + 'leprechaun', + 'oktoberfest', + 'octoberfest', + 'bratwurst' + ]), + Emoji( + name: 'clinking glasses', + char: '\u{1F942}', + shortName: 'champagne_glass', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.drink, + keywords: [ + 'celebrate', + 'clink', + 'drink', + 'glass', + 'uc9', + 'drink', + 'alcohol', + 'cheers', + 'girls night', + 'friend', + 'celebrate', + 'toast', + 'restaurant', + 'drinks', + 'beverage', + 'liquor', + 'booze', + 'gān bēi', + 'Na zdravi', + 'Proost', + 'Prost', + 'Sláinte', + 'Cin cin', + 'Kanpai', + 'Na zdrowie', + 'Saúde', + 'На здоровье', + 'Salud', + 'Skål', + 'Sei gesund', + 'santé', + 'ladies night', + 'girls only', + 'girlfriend', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'celebration', + 'event', + 'celebrating', + 'festa', + 'parties', + 'events', + 'new years', + 'new year', + 'fiesta', + 'fete', + 'newyear', + 'party', + 'festive', + 'festival', + 'yolo', + 'festejar' + ]), + Emoji( + name: 'wine glass', + char: '\u{1F377}', + shortName: 'wine_glass', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.drink, + keywords: [ + 'bar', + 'beverage', + 'drink', + 'glass', + 'wine', + 'uc6', + 'drink', + 'italian', + 'christmas', + 'alcohol', + 'cocktail', + 'girls night', + 'australia', + 'paris', + 'rich', + 'dinner', + 'picnic', + 'thanksgiving', + 'restaurant', + 'drinks', + 'beverage', + 'italy', + 'italie', + 'navidad', + 'xmas', + 'noel', + 'merry christmas', + 'liquor', + 'booze', + 'martini', + 'ladies night', + 'girls only', + 'girlfriend', + 'french', + 'france', + 'grand', + 'expensive', + 'fancy', + 'lunch' + ]), + Emoji( + name: 'tumbler glass', + char: '\u{1F943}', + shortName: 'tumbler_glass', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.drink, + keywords: [ + 'glass', + 'liquor', + 'shot', + 'tumbler', + 'whisky', + 'uc9', + 'drink', + 'japan', + 'alcohol', + 'cocktail', + 'boys night', + 'whisky', + 'irish', + 'scotland', + 'las vegas', + 'dinner', + 'shot', + 'restaurant', + 'keto', + 'drinks', + 'beverage', + 'japanese', + 'ninja', + 'liquor', + 'booze', + 'martini', + 'guys night', + 'whiskey', + 'scotch', + 'saint patricks day', + 'st patricks day', + 'leprechaun', + 'scottish', + 'vegas', + 'lunch' + ]), + Emoji( + name: 'cocktail glass', + char: '\u{1F378}', + shortName: 'cocktail', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.drink, + keywords: [ + 'bar', + 'cocktail', + 'drink', + 'glass', + 'uc6', + 'drink', + 'alcohol', + 'cocktail', + 'girls night', + 'las vegas', + 'dinner', + 'restaurant', + 'drinks', + 'beverage', + 'liquor', + 'booze', + 'martini', + 'ladies night', + 'girls only', + 'girlfriend', + 'vegas', + 'lunch' + ]), + Emoji( + name: 'tropical drink', + char: '\u{1F379}', + shortName: 'tropical_drink', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.drink, + keywords: [ + 'bar', + 'drink', + 'tropical', + 'uc6', + 'drink', + 'alcohol', + 'tropical', + 'cocktail', + 'tea', + 'citrus', + 'summer', + 'dinner', + 'restaurant', + 'drinks', + 'beverage', + 'liquor', + 'booze', + 'martini', + 'iced tea', + 'juice', + 'lime', + 'weekend', + 'lunch' + ]), + Emoji( + name: 'bottle with popping cork', + char: '\u{1F37E}', + shortName: 'champagne', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.drink, + keywords: [ + 'bar', + 'bottle', + 'cork', + 'drink', + 'popping', + 'uc8', + 'drink', + 'holidays', + 'alcohol', + 'cheers', + 'celebrate', + 'paris', + 'rich', + 'toast', + 'picnic', + 'restaurant', + 'drinks', + 'beverage', + 'holiday', + 'liquor', + 'booze', + 'gān bēi', + 'Na zdravi', + 'Proost', + 'Prost', + 'Sláinte', + 'Cin cin', + 'Kanpai', + 'Na zdrowie', + 'Saúde', + 'На здоровье', + 'Salud', + 'Skål', + 'Sei gesund', + 'santé', + 'celebration', + 'event', + 'celebrating', + 'festa', + 'parties', + 'events', + 'new years', + 'new year', + 'fiesta', + 'fete', + 'newyear', + 'party', + 'festive', + 'festival', + 'yolo', + 'festejar', + 'french', + 'france', + 'grand', + 'expensive', + 'fancy' + ]), + Emoji( + name: 'ice', + char: '\u{1F9CA}', + shortName: 'ice_cube', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.drink, + keywords: [ + 'uc12', + 'snow', + 'cold', + 'igloo', + 'cubo de hielo', + 'freeze', + 'frozen', + 'frost', + 'ice cube', + 'chilly', + 'chilled', + 'brisk', + 'freezing', + 'frostbite', + 'icicles', + 'glaçon', + 'cubetto di ghiaccio' + ]), + Emoji( + name: 'spoon', + char: '\u{1F944}', + shortName: 'spoon', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.dishware, + keywords: [ + 'spoon', + 'tableware', + 'uc9', + 'food', + 'cutlery', + 'steel', + 'utensils', + 'restaurant', + 'dishes', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'dish', + 'metal' + ]), + Emoji( + name: 'fork and knife', + char: '\u{1F374}', + shortName: 'fork_and_knife', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.dishware, + keywords: [ + 'cooking', + 'fork', + 'knife', + 'uc6', + 'food', + 'christmas', + 'cutlery', + 'dinner', + 'steel', + 'picnic', + 'independence day', + 'utensils', + 'restaurant', + 'dishes', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'navidad', + 'xmas', + 'noel', + 'merry christmas', + 'dish', + 'lunch', + 'metal', + '4th of july' + ]), + Emoji( + name: 'fork and knife with plate', + char: '\u{1F37D}\u{FE0F}', + shortName: 'fork_knife_plate', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.dishware, + keywords: [ + 'cooking', + 'fork', + 'knife', + 'plate', + 'uc7', + 'food', + 'cutlery', + 'diet', + 'dinner', + 'picnic', + 'utensils', + 'restaurant', + 'dishes', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'dish', + 'lunch' + ]), + Emoji( + name: 'bowl with spoon', + char: '\u{1F963}', + shortName: 'bowl_with_spoon', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodPrepared, + keywords: [ + 'uc10', + 'food', + 'breakfast', + 'soup', + 'dinner', + 'cereal', + 'restaurant', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'petit dejeuner', + 'lunch' + ]), + Emoji( + name: 'takeout box', + char: '\u{1F961}', + shortName: 'takeout_box', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodAsian, + keywords: [ + 'oyster pail', + 'uc10', + 'food', + 'chinese', + 'dinner', + 'oyster pail', + 'restaurant', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'chinois', + 'asian', + 'chine', + 'lunch' + ]), + Emoji( + name: 'chopsticks', + char: '\u{1F962}', + shortName: 'chopsticks', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.dishware, + keywords: [ + 'uc10', + 'food', + 'sushi', + 'japan', + 'chinese', + 'utensils', + 'restaurant', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'japanese', + 'ninja', + 'chinois', + 'asian', + 'chine' + ]), + Emoji( + name: 'salt', + char: '\u{1F9C2}', + shortName: 'salt', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.foodPrepared, + keywords: [ + 'uc11', + 'condiment', + 'restaurant', + 'condiments', + 'seasoning', + 'topping' + ]), + Emoji( + name: 'soccer ball', + char: '\u{26BD}', + shortName: 'soccer', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.sport, + keywords: [ + 'ball', + 'football', + 'soccer', + 'uc5', + 'sport', + 'game', + 'ball', + 'football', + 'soccer', + 'play', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'games', + 'gaming', + 'balls', + 'ballon', + 'soccer ball', + 'world cup' + ]), + Emoji( + name: 'basketball', + char: '\u{1F3C0}', + shortName: 'basketball', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.sport, + keywords: [ + 'ball', + 'hoop', + 'uc6', + 'sport', + 'game', + 'ball', + 'basketball', + 'play', + 'throw', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'games', + 'gaming', + 'balls', + 'ballon' + ]), + Emoji( + name: 'american football', + char: '\u{1F3C8}', + shortName: 'football', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.sport, + keywords: [ + 'american', + 'ball', + 'football', + 'uc6', + 'sport', + 'america', + 'game', + 'ball', + 'football', + 'play', + 'texas', + 'throw', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'usa', + 'united states', + 'united states of america', + 'american', + 'games', + 'gaming', + 'balls', + 'ballon' + ]), + Emoji( + name: 'baseball', + char: '\u{26BE}', + shortName: 'baseball', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.sport, + keywords: [ + 'ball', + 'uc5', + 'sport', + 'game', + 'ball', + 'play', + 'throw', + 'activity', + 'independence day', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'games', + 'gaming', + 'balls', + 'ballon', + '4th of july' + ]), + Emoji( + name: 'softball', + char: '\u{1F94E}', + shortName: 'softball', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.sport, + keywords: [ + 'uc11', + 'sport', + 'game', + 'ball', + 'play', + 'fun', + 'throw', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'games', + 'gaming', + 'balls', + 'ballon' + ]), + Emoji( + name: 'tennis', + char: '\u{1F3BE}', + shortName: 'tennis', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.sport, + keywords: [ + 'ball', + 'racquet', + 'uc6', + 'sport', + 'game', + 'ball', + 'tennis', + 'play', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'games', + 'gaming', + 'balls', + 'ballon', + 'tennis ball', + 'tennis racquet' + ]), + Emoji( + name: 'volleyball', + char: '\u{1F3D0}', + shortName: 'volleyball', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.sport, + keywords: [ + 'ball', + 'game', + 'uc8', + 'sport', + 'game', + 'ball', + 'volley ball', + 'play', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'games', + 'gaming', + 'balls', + 'ballon' + ]), + Emoji( + name: 'rugby football', + char: '\u{1F3C9}', + shortName: 'rugby_football', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.sport, + keywords: [ + 'ball', + 'football', + 'rugby', + 'uc6', + 'sport', + 'game', + 'ball', + 'football', + 'play', + 'throw', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'games', + 'gaming', + 'balls', + 'ballon' + ]), + Emoji( + name: 'flying disc', + char: '\u{1F94F}', + shortName: 'flying_disc', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.sport, + keywords: [ + 'uc11', + 'sport', + 'game', + 'play', + 'fun', + 'throw', + 'activity', + 'frisbee', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'games', + 'gaming', + 'disque-volant', + 'boomerang' + ]), + Emoji( + name: 'boomerang', + char: '\u{1FA83}', + shortName: 'boomerang', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.tool, + keywords: [ + 'uc13', + 'sport', + 'game', + 'play', + 'fun', + 'throw', + 'hunt', + 'activity', + 'boumerang', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'games', + 'gaming', + 'airfoil', + 'aerofoil' + ]), + Emoji( + name: 'pool 8 ball', + char: '\u{1F3B1}', + shortName: '8ball', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.game, + keywords: [ + '8', + '8 ball', + 'ball', + 'billiard', + 'eight', + 'game', + 'uc6', + 'sport', + 'game', + 'ball', + 'billiards', + 'luck', + 'boys night', + 'play', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'games', + 'gaming', + 'balls', + 'ballon', + 'billiards ball', + '8 ball', + '8ball', + 'pool', + 'eight ball', + 'good luck', + 'lucky', + 'guys night' + ]), + Emoji( + name: 'yo-yo', + char: '\u{1FA80}', + shortName: 'yo_yo', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.game, + keywords: [ + 'uc12', + 'game', + 'play', + 'activity', + 'yoyo', + 'toy', + 'stringed', + 'games', + 'gaming', + 'fluctuate' + ]), + Emoji( + name: 'ping pong', + char: '\u{1F3D3}', + shortName: 'ping_pong', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.sport, + keywords: [ + 'ball', + 'bat', + 'game', + 'paddle', + 'ping pong', + 'table tennis', + 'uc8', + 'sport', + 'game', + 'ball', + 'ping pong', + 'play', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'games', + 'gaming', + 'balls', + 'ballon', + 'table tennis', + 'ping pong ball', + 'ping pong paddle', + 'paddle' + ]), + Emoji( + name: 'badminton', + char: '\u{1F3F8}', + shortName: 'badminton', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.sport, + keywords: [ + 'birdie', + 'game', + 'racquet', + 'shuttlecock', + 'uc8', + 'sport', + 'game', + 'play', + 'fun', + 'activity', + 'stringed', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'games', + 'gaming' + ]), + Emoji( + name: 'ice hockey', + char: '\u{1F3D2}', + shortName: 'hockey', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.sport, + keywords: [ + 'game', + 'hockey', + 'ice', + 'puck', + 'stick', + 'uc8', + 'sport', + 'game', + 'hockey', + 'play', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'games', + 'gaming', + 'field hockey' + ]), + Emoji( + name: 'field hockey', + char: '\u{1F3D1}', + shortName: 'field_hockey', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.sport, + keywords: [ + 'ball', + 'field', + 'game', + 'hockey', + 'stick', + 'uc8', + 'sport', + 'game', + 'ball', + 'hockey', + 'play', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'games', + 'gaming', + 'balls', + 'ballon', + 'field hockey' + ]), + Emoji( + name: 'lacrosse', + char: '\u{1F94D}', + shortName: 'lacrosse', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.sport, + keywords: [ + 'uc11', + 'sport', + 'game', + 'ball', + 'play', + 'fun', + 'throw', + 'activity', + 'stringed', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'games', + 'gaming', + 'balls', + 'ballon' + ]), + Emoji( + name: 'cricket game', + char: '\u{1F3CF}', + shortName: 'cricket_game', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.sport, + keywords: [ + 'ball', + 'bat', + 'game', + 'uc8', + 'sport', + 'game', + 'ball', + 'cricket', + 'play', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'games', + 'gaming', + 'balls', + 'ballon', + 'cricket bat', + 'cricket ball' + ]), + Emoji( + name: 'goal net', + char: '\u{1F945}', + shortName: 'goal', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.sport, + keywords: [ + 'goal', + 'net', + 'uc9', + 'sport', + 'football', + 'soccer', + 'play', + 'fun', + 'activity', + 'stringed', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'soccer ball', + 'world cup' + ]), + Emoji( + name: 'flag in hole', + char: '\u{26F3}', + shortName: 'golf', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.sport, + keywords: [ + 'golf', + 'hole', + 'uc5', + 'sport', + 'game', + 'ball', + 'vacation', + 'golf', + 'play', + 'florida', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'games', + 'gaming', + 'balls', + 'ballon', + 'golfing', + 'golfer' + ]), + Emoji( + name: 'kite', + char: '\u{1FA81}', + shortName: 'kite', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.game, + keywords: [ + 'uc12', + 'sport', + 'fly', + 'vacation', + 'fun', + 'activity', + 'toy', + 'kite', + 'stringed', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'flight', + 'flying', + 'flights', + 'avion' + ]), + Emoji( + name: 'bow and arrow', + char: '\u{1F3F9}', + shortName: 'bow_and_arrow', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.tool, + keywords: [ + 'Sagittarius', + 'archer', + 'archery', + 'arrow', + 'bow', + 'tool', + 'weapon', + 'zodiac', + 'uc8', + 'sport', + 'weapon', + 'arrow', + 'game', + 'play', + 'target', + 'minecraft', + 'activity', + 'stringed', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'weapons', + 'arrows', + 'games', + 'gaming' + ]), + Emoji( + name: 'fishing pole', + char: '\u{1F3A3}', + shortName: 'fishing_pole_and_fish', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.sport, + keywords: [ + 'fish', + 'pole', + 'uc6', + 'sport', + 'vacation', + 'fishing', + 'florida', + 'fun', + 'activity', + 'stringed', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'fish', + 'fishing pole', + 'fishing rod' + ]), + Emoji( + name: 'diving mask', + char: '\u{1F93F}', + shortName: 'diving_mask', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.sport, + keywords: [ + 'uc12', + 'sport', + 'glasses', + 'vacation', + 'swim', + 'scuba', + 'fun', + 'activity', + 'mask', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'eyeglasses', + 'eye glasses', + 'swimming', + 'swimmer', + 'snorkel' + ]), + Emoji( + name: 'boxing glove', + char: '\u{1F94A}', + shortName: 'boxing_glove', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.sport, + keywords: [ + 'boxing', + 'glove', + 'uc9', + 'sport', + 'fight', + 'gloves', + 'hit', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'punch', + 'pow', + 'bam' + ]), + Emoji( + name: 'martial arts uniform', + char: '\u{1F94B}', + shortName: 'martial_arts_uniform', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.sport, + keywords: [ + 'judo', + 'karate', + 'martial arts', + 'taekwondo', + 'uniform', + 'uc9', + 'sport', + 'fight', + 'karate', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout' + ]), + Emoji( + name: 'running shirt', + char: '\u{1F3BD}', + shortName: 'running_shirt_with_sash', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.sport, + keywords: [ + 'athletics', + 'running', + 'sash', + 'shirt', + 'uc6', + 'sport', + 'award', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'awards', + 'prize', + 'prizes', + 'trophy', + 'trophies', + 'spot', + 'best', + 'champion', + 'hero' + ]), + Emoji( + name: 'skateboard', + char: '\u{1F6F9}', + shortName: 'skateboard', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportGround, + keywords: [ + 'uc11', + 'sport', + 'fun', + 'activity', + 'boosted', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'boosted board' + ]), + Emoji( + name: 'roller skate', + char: '\u{1F6FC}', + shortName: 'roller_skate', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportGround, + keywords: [ + 'uc13', + 'sport', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout' + ]), + Emoji( + name: 'sled', + char: '\u{1F6F7}', + shortName: 'sled', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.sport, + keywords: [ + 'uc10', + 'sport', + 'winter', + 'christmas', + 'fun', + 'activity', + 'sleigh', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'navidad', + 'xmas', + 'noel', + 'merry christmas', + 'sledge', + 'toboggan' + ]), + Emoji( + name: 'ice skate', + char: '\u{26F8}\u{FE0F}', + shortName: 'ice_skate', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.sport, + keywords: [ + 'ice', + 'skate', + 'uc5', + 'sport', + 'winter', + 'cold', + 'ice skating', + 'disney', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'chilly', + 'chilled', + 'brisk', + 'freezing', + 'frostbite', + 'icicles', + 'cartoon' + ]), + Emoji( + name: 'curling stone', + char: '\u{1F94C}', + shortName: 'curling_stone', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.sport, + keywords: [ + 'game', + 'rock', + 'uc10', + 'sport', + 'winter', + 'game', + 'play', + 'activity', + 'iron', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'games', + 'gaming' + ]), + Emoji( + name: 'skis', + char: '\u{1F3BF}', + shortName: 'ski', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.sport, + keywords: [ + 'ski', + 'snow', + 'uc6', + 'sport', + 'winter', + 'cold', + 'skiing', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'chilly', + 'chilled', + 'brisk', + 'freezing', + 'frostbite', + 'icicles', + 'ski', + 'snow skiing', + 'ski boot' + ]), + Emoji( + name: 'skier', + char: '\u{26F7}\u{FE0F}', + shortName: 'skier', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'ski', + 'snow', + 'uc5', + 'sport', + 'winter', + 'vacation', + 'cold', + 'skiing', + 'paris', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'chilly', + 'chilled', + 'brisk', + 'freezing', + 'frostbite', + 'icicles', + 'ski', + 'snow skiing', + 'ski boot', + 'french', + 'france' + ]), + Emoji( + name: 'snowboarder', + char: '\u{1F3C2}', + shortName: 'snowboarder', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'ski', + 'snow', + 'snowboard', + 'uc6', + 'sport', + 'diversity', + 'winter', + 'vacation', + 'cold', + 'snowboarding', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'chilly', + 'chilled', + 'brisk', + 'freezing', + 'frostbite', + 'icicles', + 'snowboarder' + ]), + Emoji( + name: 'snowboarder: light skin tone', + char: '\u{1F3C2}\u{1F3FB}', + shortName: 'snowboarder_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'light skin tone', + 'ski', + 'snow', + 'snowboard', + 'uc8', + 'sport', + 'diversity', + 'winter', + 'vacation', + 'cold', + 'snowboarding', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'chilly', + 'chilled', + 'brisk', + 'freezing', + 'frostbite', + 'icicles', + 'snowboarder' + ], + modifiable: true), + Emoji( + name: 'snowboarder: medium-light skin tone', + char: '\u{1F3C2}\u{1F3FC}', + shortName: 'snowboarder_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'medium-light skin tone', + 'ski', + 'snow', + 'snowboard', + 'uc8', + 'sport', + 'diversity', + 'winter', + 'vacation', + 'cold', + 'snowboarding', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'chilly', + 'chilled', + 'brisk', + 'freezing', + 'frostbite', + 'icicles', + 'snowboarder' + ], + modifiable: true), + Emoji( + name: 'snowboarder: medium skin tone', + char: '\u{1F3C2}\u{1F3FD}', + shortName: 'snowboarder_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'medium skin tone', + 'ski', + 'snow', + 'snowboard', + 'uc8', + 'sport', + 'diversity', + 'winter', + 'vacation', + 'cold', + 'snowboarding', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'chilly', + 'chilled', + 'brisk', + 'freezing', + 'frostbite', + 'icicles', + 'snowboarder' + ], + modifiable: true), + Emoji( + name: 'snowboarder: medium-dark skin tone', + char: '\u{1F3C2}\u{1F3FE}', + shortName: 'snowboarder_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'medium-dark skin tone', + 'ski', + 'snow', + 'snowboard', + 'uc8', + 'sport', + 'diversity', + 'winter', + 'vacation', + 'cold', + 'snowboarding', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'chilly', + 'chilled', + 'brisk', + 'freezing', + 'frostbite', + 'icicles', + 'snowboarder' + ], + modifiable: true), + Emoji( + name: 'snowboarder: dark skin tone', + char: '\u{1F3C2}\u{1F3FF}', + shortName: 'snowboarder_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'dark skin tone', + 'ski', + 'snow', + 'snowboard', + 'uc8', + 'sport', + 'diversity', + 'winter', + 'vacation', + 'cold', + 'snowboarding', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'chilly', + 'chilled', + 'brisk', + 'freezing', + 'frostbite', + 'icicles', + 'snowboarder' + ], + modifiable: true), + Emoji( + name: 'parachute', + char: '\u{1FA82}', + shortName: 'parachute', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportAir, + keywords: [ + 'uc12', + 'sport', + 'fly', + 'vacation', + 'airplane', + 'fun', + 'activity', + 'kite', + 'skydive', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'flight', + 'flying', + 'flights', + 'avion', + 'airline', + 'aircraft', + 'airforce', + 'air force', + 'airport', + 'hang-glide', + 'parasail' + ]), + Emoji( + name: 'person lifting weights', + char: '\u{1F3CB}', + shortName: 'person_lifting_weights', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'lifter', + 'weight', + 'uc7', + 'sport', + 'diversity', + 'flex', + 'weight lifting', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'strong', + 'weight lifter' + ]), + Emoji( + name: 'person lifting weights: light skin tone', + char: '\u{1F3CB}\u{1F3FB}', + shortName: 'person_lifting_weights_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'lifter', + 'light skin tone', + 'weight', + 'uc8', + 'sport', + 'diversity', + 'flex', + 'weight lifting', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'strong', + 'weight lifter' + ], + modifiable: true), + Emoji( + name: 'person lifting weights: medium-light skin tone', + char: '\u{1F3CB}\u{1F3FC}', + shortName: 'person_lifting_weights_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'lifter', + 'medium-light skin tone', + 'weight', + 'uc8', + 'sport', + 'diversity', + 'flex', + 'weight lifting', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'strong', + 'weight lifter' + ], + modifiable: true), + Emoji( + name: 'person lifting weights: medium skin tone', + char: '\u{1F3CB}\u{1F3FD}', + shortName: 'person_lifting_weights_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'lifter', + 'medium skin tone', + 'weight', + 'uc8', + 'sport', + 'diversity', + 'flex', + 'weight lifting', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'strong', + 'weight lifter' + ], + modifiable: true), + Emoji( + name: 'person lifting weights: medium-dark skin tone', + char: '\u{1F3CB}\u{1F3FE}', + shortName: 'person_lifting_weights_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'lifter', + 'medium-dark skin tone', + 'weight', + 'uc8', + 'sport', + 'diversity', + 'flex', + 'weight lifting', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'strong', + 'weight lifter' + ], + modifiable: true), + Emoji( + name: 'person lifting weights: dark skin tone', + char: '\u{1F3CB}\u{1F3FF}', + shortName: 'person_lifting_weights_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'dark skin tone', + 'lifter', + 'weight', + 'uc8', + 'sport', + 'diversity', + 'flex', + 'weight lifting', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'strong', + 'weight lifter' + ], + modifiable: true), + Emoji( + name: 'woman lifting weights', + char: '\u{1F3CB}\u{FE0F}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_lifting_weights', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'weight lifter', + 'woman', + 'uc7', + 'sport', + 'diversity', + 'flex', + 'weight lifting', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'strong', + 'weight lifter' + ]), + Emoji( + name: 'woman lifting weights: light skin tone', + char: '\u{1F3CB}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_lifting_weights_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'light skin tone', + 'weight lifter', + 'woman', + 'uc8', + 'sport', + 'diversity', + 'flex', + 'weight lifting', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'strong', + 'weight lifter' + ], + modifiable: true), + Emoji( + name: 'woman lifting weights: medium-light skin tone', + char: '\u{1F3CB}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_lifting_weights_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'medium-light skin tone', + 'weight lifter', + 'woman', + 'uc8', + 'sport', + 'diversity', + 'flex', + 'weight lifting', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'strong', + 'weight lifter' + ], + modifiable: true), + Emoji( + name: 'woman lifting weights: medium skin tone', + char: '\u{1F3CB}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_lifting_weights_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'medium skin tone', + 'weight lifter', + 'woman', + 'uc8', + 'sport', + 'diversity', + 'flex', + 'weight lifting', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'strong', + 'weight lifter' + ], + modifiable: true), + Emoji( + name: 'woman lifting weights: medium-dark skin tone', + char: '\u{1F3CB}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_lifting_weights_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'medium-dark skin tone', + 'weight lifter', + 'woman', + 'uc8', + 'sport', + 'diversity', + 'flex', + 'weight lifting', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'strong', + 'weight lifter' + ], + modifiable: true), + Emoji( + name: 'woman lifting weights: dark skin tone', + char: '\u{1F3CB}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_lifting_weights_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'dark skin tone', + 'weight lifter', + 'woman', + 'uc8', + 'sport', + 'diversity', + 'flex', + 'weight lifting', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'strong', + 'weight lifter' + ], + modifiable: true), + Emoji( + name: 'man lifting weights', + char: '\u{1F3CB}\u{FE0F}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_lifting_weights', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'man', + 'weight lifter', + 'uc7', + 'sport', + 'diversity', + 'flex', + 'weight lifting', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'strong', + 'weight lifter' + ]), + Emoji( + name: 'man lifting weights: light skin tone', + char: '\u{1F3CB}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_lifting_weights_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'light skin tone', + 'man', + 'weight lifter', + 'uc8', + 'sport', + 'diversity', + 'flex', + 'weight lifting', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'strong', + 'weight lifter' + ], + modifiable: true), + Emoji( + name: 'man lifting weights: medium-light skin tone', + char: '\u{1F3CB}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_lifting_weights_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'man', + 'medium-light skin tone', + 'weight lifter', + 'uc8', + 'sport', + 'diversity', + 'flex', + 'weight lifting', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'strong', + 'weight lifter' + ], + modifiable: true), + Emoji( + name: 'man lifting weights: medium skin tone', + char: '\u{1F3CB}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_lifting_weights_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'man', + 'medium skin tone', + 'weight lifter', + 'uc8', + 'sport', + 'diversity', + 'flex', + 'weight lifting', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'strong', + 'weight lifter' + ], + modifiable: true), + Emoji( + name: 'man lifting weights: medium-dark skin tone', + char: '\u{1F3CB}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_lifting_weights_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'man', + 'medium-dark skin tone', + 'weight lifter', + 'uc8', + 'sport', + 'diversity', + 'flex', + 'weight lifting', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'strong', + 'weight lifter' + ], + modifiable: true), + Emoji( + name: 'man lifting weights: dark skin tone', + char: '\u{1F3CB}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_lifting_weights_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'dark skin tone', + 'man', + 'weight lifter', + 'uc8', + 'sport', + 'diversity', + 'flex', + 'weight lifting', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'strong', + 'weight lifter' + ], + modifiable: true), + Emoji( + name: 'people wrestling', + char: '\u{1F93C}', + shortName: 'people_wrestling', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'wrestle', + 'wrestler', + 'uc9', + 'sport', + 'fight', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout' + ]), + Emoji( + name: 'women wrestling', + char: '\u{1F93C}\u{200D}\u{2640}\u{FE0F}', + shortName: 'women_wrestling', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'women', + 'wrestle', + 'uc9', + 'sport', + 'fight', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout' + ]), + Emoji( + name: 'men wrestling', + char: '\u{1F93C}\u{200D}\u{2642}\u{FE0F}', + shortName: 'men_wrestling', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'men', + 'wrestle', + 'uc9', + 'sport', + 'fight', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout' + ]), + Emoji( + name: 'person cartwheeling', + char: '\u{1F938}', + shortName: 'person_doing_cartwheel', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'cartwheel', + 'gymnastics', + 'uc9', + 'sport', + 'circus', + 'gymnast', + 'yoga', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'circus tent', + 'clown', + 'clowns', + 'gymnastics', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum' + ]), + Emoji( + name: 'person cartwheeling: light skin tone', + char: '\u{1F938}\u{1F3FB}', + shortName: 'person_doing_cartwheel_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'cartwheel', + 'gymnastics', + 'light skin tone', + 'uc9', + 'sport', + 'circus', + 'gymnast', + 'yoga', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'circus tent', + 'clown', + 'clowns', + 'gymnastics', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum' + ], + modifiable: true), + Emoji( + name: 'person cartwheeling: medium-light skin tone', + char: '\u{1F938}\u{1F3FC}', + shortName: 'person_doing_cartwheel_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'cartwheel', + 'gymnastics', + 'medium-light skin tone', + 'uc9', + 'sport', + 'circus', + 'gymnast', + 'yoga', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'circus tent', + 'clown', + 'clowns', + 'gymnastics', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum' + ], + modifiable: true), + Emoji( + name: 'person cartwheeling: medium skin tone', + char: '\u{1F938}\u{1F3FD}', + shortName: 'person_doing_cartwheel_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'cartwheel', + 'gymnastics', + 'medium skin tone', + 'uc9', + 'sport', + 'circus', + 'gymnast', + 'yoga', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'circus tent', + 'clown', + 'clowns', + 'gymnastics', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum' + ], + modifiable: true), + Emoji( + name: 'person cartwheeling: medium-dark skin tone', + char: '\u{1F938}\u{1F3FE}', + shortName: 'person_doing_cartwheel_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'cartwheel', + 'gymnastics', + 'medium-dark skin tone', + 'uc9', + 'sport', + 'circus', + 'gymnast', + 'yoga', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'circus tent', + 'clown', + 'clowns', + 'gymnastics', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum' + ], + modifiable: true), + Emoji( + name: 'person cartwheeling: dark skin tone', + char: '\u{1F938}\u{1F3FF}', + shortName: 'person_doing_cartwheel_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'cartwheel', + 'dark skin tone', + 'gymnastics', + 'uc9', + 'sport', + 'circus', + 'gymnast', + 'yoga', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'circus tent', + 'clown', + 'clowns', + 'gymnastics', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum' + ], + modifiable: true), + Emoji( + name: 'woman cartwheeling', + char: '\u{1F938}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_cartwheeling', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'cartwheel', + 'gymnastics', + 'woman', + 'uc9', + 'sport', + 'diversity', + 'circus', + 'gymnast', + 'yoga', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'circus tent', + 'clown', + 'clowns', + 'gymnastics', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum' + ]), + Emoji( + name: 'woman cartwheeling: light skin tone', + char: '\u{1F938}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_cartwheeling_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'cartwheel', + 'gymnastics', + 'light skin tone', + 'woman', + 'uc9', + 'sport', + 'diversity', + 'circus', + 'gymnast', + 'yoga', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'circus tent', + 'clown', + 'clowns', + 'gymnastics', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum' + ], + modifiable: true), + Emoji( + name: 'woman cartwheeling: medium-light skin tone', + char: '\u{1F938}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_cartwheeling_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'cartwheel', + 'gymnastics', + 'medium-light skin tone', + 'woman', + 'uc9', + 'sport', + 'diversity', + 'circus', + 'gymnast', + 'yoga', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'circus tent', + 'clown', + 'clowns', + 'gymnastics', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum' + ], + modifiable: true), + Emoji( + name: 'woman cartwheeling: medium skin tone', + char: '\u{1F938}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_cartwheeling_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'cartwheel', + 'gymnastics', + 'medium skin tone', + 'woman', + 'uc9', + 'sport', + 'diversity', + 'circus', + 'gymnast', + 'yoga', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'circus tent', + 'clown', + 'clowns', + 'gymnastics', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum' + ], + modifiable: true), + Emoji( + name: 'woman cartwheeling: medium-dark skin tone', + char: '\u{1F938}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_cartwheeling_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'cartwheel', + 'gymnastics', + 'medium-dark skin tone', + 'woman', + 'uc9', + 'sport', + 'diversity', + 'circus', + 'gymnast', + 'yoga', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'circus tent', + 'clown', + 'clowns', + 'gymnastics', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum' + ], + modifiable: true), + Emoji( + name: 'woman cartwheeling: dark skin tone', + char: '\u{1F938}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_cartwheeling_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'cartwheel', + 'dark skin tone', + 'gymnastics', + 'woman', + 'uc9', + 'sport', + 'diversity', + 'circus', + 'gymnast', + 'yoga', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'circus tent', + 'clown', + 'clowns', + 'gymnastics', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum' + ], + modifiable: true), + Emoji( + name: 'man cartwheeling', + char: '\u{1F938}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_cartwheeling', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'cartwheel', + 'gymnastics', + 'man', + 'uc9', + 'sport', + 'diversity', + 'circus', + 'gymnast', + 'yoga', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'circus tent', + 'clown', + 'clowns', + 'gymnastics', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum' + ]), + Emoji( + name: 'man cartwheeling: light skin tone', + char: '\u{1F938}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_cartwheeling_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'cartwheel', + 'gymnastics', + 'light skin tone', + 'man', + 'uc9', + 'sport', + 'diversity', + 'circus', + 'gymnast', + 'yoga', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'circus tent', + 'clown', + 'clowns', + 'gymnastics', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum' + ], + modifiable: true), + Emoji( + name: 'man cartwheeling: medium-light skin tone', + char: '\u{1F938}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_cartwheeling_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'cartwheel', + 'gymnastics', + 'man', + 'medium-light skin tone', + 'uc9', + 'sport', + 'diversity', + 'circus', + 'gymnast', + 'yoga', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'circus tent', + 'clown', + 'clowns', + 'gymnastics', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum' + ], + modifiable: true), + Emoji( + name: 'man cartwheeling: medium skin tone', + char: '\u{1F938}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_cartwheeling_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'cartwheel', + 'gymnastics', + 'man', + 'medium skin tone', + 'uc9', + 'sport', + 'diversity', + 'circus', + 'gymnast', + 'yoga', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'circus tent', + 'clown', + 'clowns', + 'gymnastics', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum' + ], + modifiable: true), + Emoji( + name: 'man cartwheeling: medium-dark skin tone', + char: '\u{1F938}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_cartwheeling_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'cartwheel', + 'gymnastics', + 'man', + 'medium-dark skin tone', + 'uc9', + 'sport', + 'diversity', + 'circus', + 'gymnast', + 'yoga', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'circus tent', + 'clown', + 'clowns', + 'gymnastics', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum' + ], + modifiable: true), + Emoji( + name: 'man cartwheeling: dark skin tone', + char: '\u{1F938}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_cartwheeling_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'cartwheel', + 'dark skin tone', + 'gymnastics', + 'man', + 'uc9', + 'sport', + 'diversity', + 'circus', + 'gymnast', + 'yoga', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'circus tent', + 'clown', + 'clowns', + 'gymnastics', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum' + ], + modifiable: true), + Emoji( + name: 'person bouncing ball', + char: '\u{26F9}', + shortName: 'person_bouncing_ball', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'ball', + 'uc5', + 'sport', + 'diversity', + 'ball', + 'basketball', + 'play', + 'fame', + 'fun', + 'throw', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon', + 'famous', + 'celebrity' + ]), + Emoji( + name: 'person bouncing ball: light skin tone', + char: '\u{26F9}\u{1F3FB}', + shortName: 'person_bouncing_ball_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'ball', + 'light skin tone', + 'uc8', + 'sport', + 'diversity', + 'ball', + 'basketball', + 'play', + 'fame', + 'fun', + 'throw', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon', + 'famous', + 'celebrity' + ], + modifiable: true), + Emoji( + name: 'person bouncing ball: medium-light skin tone', + char: '\u{26F9}\u{1F3FC}', + shortName: 'person_bouncing_ball_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'ball', + 'medium-light skin tone', + 'uc8', + 'sport', + 'diversity', + 'ball', + 'basketball', + 'play', + 'fame', + 'fun', + 'throw', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon', + 'famous', + 'celebrity' + ], + modifiable: true), + Emoji( + name: 'person bouncing ball: medium skin tone', + char: '\u{26F9}\u{1F3FD}', + shortName: 'person_bouncing_ball_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'ball', + 'medium skin tone', + 'uc8', + 'sport', + 'diversity', + 'ball', + 'basketball', + 'play', + 'fame', + 'fun', + 'throw', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon', + 'famous', + 'celebrity' + ], + modifiable: true), + Emoji( + name: 'person bouncing ball: medium-dark skin tone', + char: '\u{26F9}\u{1F3FE}', + shortName: 'person_bouncing_ball_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'ball', + 'medium-dark skin tone', + 'uc8', + 'sport', + 'diversity', + 'ball', + 'basketball', + 'play', + 'fame', + 'fun', + 'throw', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon', + 'famous', + 'celebrity' + ], + modifiable: true), + Emoji( + name: 'person bouncing ball: dark skin tone', + char: '\u{26F9}\u{1F3FF}', + shortName: 'person_bouncing_ball_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'ball', + 'dark skin tone', + 'uc8', + 'sport', + 'diversity', + 'ball', + 'basketball', + 'play', + 'fame', + 'fun', + 'throw', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon', + 'famous', + 'celebrity' + ], + modifiable: true), + Emoji( + name: 'woman bouncing ball', + char: '\u{26F9}\u{FE0F}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_bouncing_ball', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'ball', + 'woman', + 'uc5', + 'sport', + 'diversity', + 'ball', + 'basketball', + 'play', + 'fame', + 'fun', + 'throw', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon', + 'famous', + 'celebrity' + ]), + Emoji( + name: 'woman bouncing ball: light skin tone', + char: '\u{26F9}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_bouncing_ball_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'ball', + 'light skin tone', + 'woman', + 'uc8', + 'sport', + 'diversity', + 'ball', + 'basketball', + 'play', + 'fame', + 'fun', + 'throw', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon', + 'famous', + 'celebrity' + ], + modifiable: true), + Emoji( + name: 'woman bouncing ball: medium-light skin tone', + char: '\u{26F9}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_bouncing_ball_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'ball', + 'medium-light skin tone', + 'woman', + 'uc8', + 'sport', + 'diversity', + 'ball', + 'basketball', + 'play', + 'fame', + 'fun', + 'throw', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon', + 'famous', + 'celebrity' + ], + modifiable: true), + Emoji( + name: 'woman bouncing ball: medium skin tone', + char: '\u{26F9}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_bouncing_ball_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'ball', + 'medium skin tone', + 'woman', + 'uc8', + 'sport', + 'diversity', + 'ball', + 'basketball', + 'play', + 'fame', + 'fun', + 'throw', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon', + 'famous', + 'celebrity' + ], + modifiable: true), + Emoji( + name: 'woman bouncing ball: medium-dark skin tone', + char: '\u{26F9}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_bouncing_ball_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'ball', + 'medium-dark skin tone', + 'woman', + 'uc8', + 'sport', + 'diversity', + 'ball', + 'basketball', + 'play', + 'fame', + 'fun', + 'throw', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon', + 'famous', + 'celebrity' + ], + modifiable: true), + Emoji( + name: 'woman bouncing ball: dark skin tone', + char: '\u{26F9}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_bouncing_ball_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'ball', + 'dark skin tone', + 'woman', + 'uc8', + 'sport', + 'diversity', + 'ball', + 'basketball', + 'play', + 'fame', + 'fun', + 'throw', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon', + 'famous', + 'celebrity' + ], + modifiable: true), + Emoji( + name: 'man bouncing ball', + char: '\u{26F9}\u{FE0F}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_bouncing_ball', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'ball', + 'man', + 'uc5', + 'sport', + 'ball', + 'basketball', + 'play', + 'fame', + 'fun', + 'throw', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'balls', + 'ballon', + 'famous', + 'celebrity' + ]), + Emoji( + name: 'man bouncing ball: light skin tone', + char: '\u{26F9}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_bouncing_ball_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'ball', + 'light skin tone', + 'man', + 'uc8', + 'sport', + 'ball', + 'basketball', + 'play', + 'fame', + 'fun', + 'throw', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'balls', + 'ballon', + 'famous', + 'celebrity' + ], + modifiable: true), + Emoji( + name: 'man bouncing ball: medium-light skin tone', + char: '\u{26F9}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_bouncing_ball_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'ball', + 'man', + 'medium-light skin tone', + 'uc8', + 'sport', + 'ball', + 'basketball', + 'play', + 'fame', + 'fun', + 'throw', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'balls', + 'ballon', + 'famous', + 'celebrity' + ], + modifiable: true), + Emoji( + name: 'man bouncing ball: medium skin tone', + char: '\u{26F9}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_bouncing_ball_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'ball', + 'man', + 'medium skin tone', + 'uc8', + 'sport', + 'ball', + 'basketball', + 'play', + 'fame', + 'fun', + 'throw', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'balls', + 'ballon', + 'famous', + 'celebrity' + ], + modifiable: true), + Emoji( + name: 'man bouncing ball: medium-dark skin tone', + char: '\u{26F9}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_bouncing_ball_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'ball', + 'man', + 'medium-dark skin tone', + 'uc8', + 'sport', + 'ball', + 'basketball', + 'play', + 'fame', + 'fun', + 'throw', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'balls', + 'ballon', + 'famous', + 'celebrity' + ], + modifiable: true), + Emoji( + name: 'man bouncing ball: dark skin tone', + char: '\u{26F9}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_bouncing_ball_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'ball', + 'dark skin tone', + 'man', + 'uc8', + 'sport', + 'ball', + 'basketball', + 'play', + 'fame', + 'fun', + 'throw', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'balls', + 'ballon', + 'famous', + 'celebrity' + ], + modifiable: true), + Emoji( + name: 'person fencing', + char: '\u{1F93A}', + shortName: 'person_fencing', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'fencer', + 'fencing', + 'sword', + 'uc9', + 'sport', + 'fight', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout' + ]), + Emoji( + name: 'person playing handball', + char: '\u{1F93E}', + shortName: 'person_playing_handball', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'ball', + 'handball', + 'uc9', + 'sport', + 'diversity', + 'ball', + 'volley ball', + 'play', + 'throw', + 'jump', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon' + ]), + Emoji( + name: 'person playing handball: light skin tone', + char: '\u{1F93E}\u{1F3FB}', + shortName: 'person_playing_handball_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'ball', + 'handball', + 'light skin tone', + 'uc9', + 'sport', + 'diversity', + 'ball', + 'volley ball', + 'play', + 'throw', + 'jump', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon' + ], + modifiable: true), + Emoji( + name: 'person playing handball: medium-light skin tone', + char: '\u{1F93E}\u{1F3FC}', + shortName: 'person_playing_handball_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'ball', + 'handball', + 'medium-light skin tone', + 'uc9', + 'sport', + 'diversity', + 'ball', + 'volley ball', + 'play', + 'throw', + 'jump', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon' + ], + modifiable: true), + Emoji( + name: 'person playing handball: medium skin tone', + char: '\u{1F93E}\u{1F3FD}', + shortName: 'person_playing_handball_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'ball', + 'handball', + 'medium skin tone', + 'uc9', + 'sport', + 'diversity', + 'ball', + 'volley ball', + 'play', + 'throw', + 'jump', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon' + ], + modifiable: true), + Emoji( + name: 'person playing handball: medium-dark skin tone', + char: '\u{1F93E}\u{1F3FE}', + shortName: 'person_playing_handball_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'ball', + 'handball', + 'medium-dark skin tone', + 'uc9', + 'sport', + 'diversity', + 'ball', + 'volley ball', + 'play', + 'throw', + 'jump', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon' + ], + modifiable: true), + Emoji( + name: 'person playing handball: dark skin tone', + char: '\u{1F93E}\u{1F3FF}', + shortName: 'person_playing_handball_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'ball', + 'dark skin tone', + 'handball', + 'uc9', + 'sport', + 'diversity', + 'ball', + 'volley ball', + 'play', + 'throw', + 'jump', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon' + ], + modifiable: true), + Emoji( + name: 'woman playing handball', + char: '\u{1F93E}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_playing_handball', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'handball', + 'woman', + 'uc9', + 'sport', + 'diversity', + 'ball', + 'volley ball', + 'play', + 'throw', + 'jump', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon' + ]), + Emoji( + name: 'woman playing handball: light skin tone', + char: '\u{1F93E}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_playing_handball_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'handball', + 'light skin tone', + 'woman', + 'uc9', + 'sport', + 'diversity', + 'ball', + 'volley ball', + 'play', + 'throw', + 'jump', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon' + ], + modifiable: true), + Emoji( + name: 'woman playing handball: medium-light skin tone', + char: '\u{1F93E}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_playing_handball_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'handball', + 'medium-light skin tone', + 'woman', + 'uc9', + 'sport', + 'diversity', + 'ball', + 'volley ball', + 'play', + 'throw', + 'jump', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon' + ], + modifiable: true), + Emoji( + name: 'woman playing handball: medium skin tone', + char: '\u{1F93E}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_playing_handball_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'handball', + 'medium skin tone', + 'woman', + 'uc9', + 'sport', + 'diversity', + 'ball', + 'volley ball', + 'play', + 'throw', + 'jump', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon' + ], + modifiable: true), + Emoji( + name: 'woman playing handball: medium-dark skin tone', + char: '\u{1F93E}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_playing_handball_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'handball', + 'medium-dark skin tone', + 'woman', + 'uc9', + 'sport', + 'diversity', + 'ball', + 'volley ball', + 'play', + 'throw', + 'jump', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon' + ], + modifiable: true), + Emoji( + name: 'woman playing handball: dark skin tone', + char: '\u{1F93E}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_playing_handball_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'dark skin tone', + 'handball', + 'woman', + 'uc9', + 'sport', + 'diversity', + 'ball', + 'volley ball', + 'play', + 'throw', + 'jump', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon' + ], + modifiable: true), + Emoji( + name: 'man playing handball', + char: '\u{1F93E}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_playing_handball', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'handball', + 'man', + 'uc9', + 'sport', + 'diversity', + 'ball', + 'volley ball', + 'play', + 'throw', + 'jump', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon' + ]), + Emoji( + name: 'man playing handball: light skin tone', + char: '\u{1F93E}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_playing_handball_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'handball', + 'light skin tone', + 'man', + 'uc9', + 'sport', + 'diversity', + 'ball', + 'volley ball', + 'play', + 'throw', + 'jump', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon' + ], + modifiable: true), + Emoji( + name: 'man playing handball: medium-light skin tone', + char: '\u{1F93E}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_playing_handball_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'handball', + 'man', + 'medium-light skin tone', + 'uc9', + 'sport', + 'diversity', + 'ball', + 'volley ball', + 'play', + 'throw', + 'jump', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon' + ], + modifiable: true), + Emoji( + name: 'man playing handball: medium skin tone', + char: '\u{1F93E}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_playing_handball_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'handball', + 'man', + 'medium skin tone', + 'uc9', + 'sport', + 'diversity', + 'ball', + 'volley ball', + 'play', + 'throw', + 'jump', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon' + ], + modifiable: true), + Emoji( + name: 'man playing handball: medium-dark skin tone', + char: '\u{1F93E}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_playing_handball_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'handball', + 'man', + 'medium-dark skin tone', + 'uc9', + 'sport', + 'diversity', + 'ball', + 'volley ball', + 'play', + 'throw', + 'jump', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon' + ], + modifiable: true), + Emoji( + name: 'man playing handball: dark skin tone', + char: '\u{1F93E}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_playing_handball_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'dark skin tone', + 'handball', + 'man', + 'uc9', + 'sport', + 'diversity', + 'ball', + 'volley ball', + 'play', + 'throw', + 'jump', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon' + ], + modifiable: true), + Emoji( + name: 'person golfing', + char: '\u{1F3CC}', + shortName: 'person_golfing', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'ball', + 'golf', + 'uc7', + 'sport', + 'diversity', + 'ball', + 'vacation', + 'golf', + 'play', + 'florida', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon', + 'golfing', + 'golfer' + ]), + Emoji( + name: 'person golfing: light skin tone', + char: '\u{1F3CC}\u{1F3FB}', + shortName: 'person_golfing_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'ball', + 'golf', + 'light skin tone', + 'uc8', + 'sport', + 'diversity', + 'ball', + 'vacation', + 'golf', + 'play', + 'florida', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon', + 'golfing', + 'golfer' + ], + modifiable: true), + Emoji( + name: 'person golfing: medium-light skin tone', + char: '\u{1F3CC}\u{1F3FC}', + shortName: 'person_golfing_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'ball', + 'golf', + 'medium-light skin tone', + 'uc8', + 'sport', + 'diversity', + 'ball', + 'vacation', + 'golf', + 'play', + 'florida', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon', + 'golfing', + 'golfer' + ], + modifiable: true), + Emoji( + name: 'person golfing: medium skin tone', + char: '\u{1F3CC}\u{1F3FD}', + shortName: 'person_golfing_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'ball', + 'golf', + 'medium skin tone', + 'uc8', + 'sport', + 'diversity', + 'ball', + 'vacation', + 'golf', + 'play', + 'florida', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon', + 'golfing', + 'golfer' + ], + modifiable: true), + Emoji( + name: 'person golfing: medium-dark skin tone', + char: '\u{1F3CC}\u{1F3FE}', + shortName: 'person_golfing_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'ball', + 'golf', + 'medium-dark skin tone', + 'uc8', + 'sport', + 'diversity', + 'ball', + 'vacation', + 'golf', + 'play', + 'florida', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon', + 'golfing', + 'golfer' + ], + modifiable: true), + Emoji( + name: 'person golfing: dark skin tone', + char: '\u{1F3CC}\u{1F3FF}', + shortName: 'person_golfing_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'ball', + 'dark skin tone', + 'golf', + 'uc8', + 'sport', + 'diversity', + 'ball', + 'vacation', + 'golf', + 'play', + 'florida', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon', + 'golfing', + 'golfer' + ], + modifiable: true), + Emoji( + name: 'woman golfing', + char: '\u{1F3CC}\u{FE0F}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_golfing', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'golf', + 'woman', + 'uc7', + 'sport', + 'diversity', + 'ball', + 'vacation', + 'golf', + 'play', + 'florida', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon', + 'golfing', + 'golfer' + ]), + Emoji( + name: 'woman golfing: light skin tone', + char: '\u{1F3CC}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_golfing_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'golf', + 'light skin tone', + 'woman', + 'uc8', + 'sport', + 'diversity', + 'ball', + 'vacation', + 'golf', + 'play', + 'florida', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon', + 'golfing', + 'golfer' + ], + modifiable: true), + Emoji( + name: 'woman golfing: medium-light skin tone', + char: '\u{1F3CC}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_golfing_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'golf', + 'medium-light skin tone', + 'woman', + 'uc8', + 'sport', + 'diversity', + 'ball', + 'vacation', + 'golf', + 'play', + 'florida', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon', + 'golfing', + 'golfer' + ], + modifiable: true), + Emoji( + name: 'woman golfing: medium skin tone', + char: '\u{1F3CC}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_golfing_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'golf', + 'medium skin tone', + 'woman', + 'uc8', + 'sport', + 'diversity', + 'ball', + 'vacation', + 'golf', + 'play', + 'florida', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon', + 'golfing', + 'golfer' + ], + modifiable: true), + Emoji( + name: 'woman golfing: medium-dark skin tone', + char: '\u{1F3CC}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_golfing_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'golf', + 'medium-dark skin tone', + 'woman', + 'uc8', + 'sport', + 'diversity', + 'ball', + 'vacation', + 'golf', + 'play', + 'florida', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon', + 'golfing', + 'golfer' + ], + modifiable: true), + Emoji( + name: 'woman golfing: dark skin tone', + char: '\u{1F3CC}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_golfing_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'dark skin tone', + 'golf', + 'woman', + 'uc8', + 'sport', + 'diversity', + 'ball', + 'vacation', + 'golf', + 'play', + 'florida', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon', + 'golfing', + 'golfer' + ], + modifiable: true), + Emoji( + name: 'man golfing', + char: '\u{1F3CC}\u{FE0F}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_golfing', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'golf', + 'man', + 'uc7', + 'sport', + 'diversity', + 'ball', + 'vacation', + 'golf', + 'play', + 'florida', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon', + 'golfing', + 'golfer' + ]), + Emoji( + name: 'man golfing: light skin tone', + char: '\u{1F3CC}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_golfing_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'golf', + 'light skin tone', + 'man', + 'uc8', + 'sport', + 'diversity', + 'ball', + 'vacation', + 'golf', + 'play', + 'florida', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon', + 'golfing', + 'golfer' + ], + modifiable: true), + Emoji( + name: 'man golfing: medium-light skin tone', + char: '\u{1F3CC}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_golfing_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'golf', + 'man', + 'medium-light skin tone', + 'uc8', + 'sport', + 'diversity', + 'ball', + 'vacation', + 'golf', + 'play', + 'florida', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon', + 'golfing', + 'golfer' + ], + modifiable: true), + Emoji( + name: 'man golfing: medium skin tone', + char: '\u{1F3CC}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_golfing_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'golf', + 'man', + 'medium skin tone', + 'uc8', + 'sport', + 'diversity', + 'ball', + 'vacation', + 'golf', + 'play', + 'florida', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon', + 'golfing', + 'golfer' + ], + modifiable: true), + Emoji( + name: 'man golfing: medium-dark skin tone', + char: '\u{1F3CC}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_golfing_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'golf', + 'man', + 'medium-dark skin tone', + 'uc8', + 'sport', + 'diversity', + 'ball', + 'vacation', + 'golf', + 'play', + 'florida', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon', + 'golfing', + 'golfer' + ], + modifiable: true), + Emoji( + name: 'man golfing: dark skin tone', + char: '\u{1F3CC}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_golfing_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'dark skin tone', + 'golf', + 'man', + 'uc8', + 'sport', + 'diversity', + 'ball', + 'vacation', + 'golf', + 'play', + 'florida', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon', + 'golfing', + 'golfer' + ], + modifiable: true), + Emoji( + name: 'horse racing', + char: '\u{1F3C7}', + shortName: 'horse_racing', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'horse', + 'jockey', + 'racehorse', + 'racing', + 'uc6', + 'sport', + 'diversity', + 'horse racing', + 'las vegas', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'horseback riding', + 'horse and rider', + 'horses', + 'horseshoe', + 'pony', + 'vegas' + ]), + Emoji( + name: 'horse racing: light skin tone', + char: '\u{1F3C7}\u{1F3FB}', + shortName: 'horse_racing_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'horse', + 'jockey', + 'light skin tone', + 'racehorse', + 'racing', + 'uc8', + 'sport', + 'diversity', + 'horse racing', + 'las vegas', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'horseback riding', + 'horse and rider', + 'horses', + 'horseshoe', + 'pony', + 'vegas' + ], + modifiable: true), + Emoji( + name: 'horse racing: medium-light skin tone', + char: '\u{1F3C7}\u{1F3FC}', + shortName: 'horse_racing_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'horse', + 'jockey', + 'medium-light skin tone', + 'racehorse', + 'racing', + 'uc8', + 'sport', + 'diversity', + 'horse racing', + 'las vegas', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'horseback riding', + 'horse and rider', + 'horses', + 'horseshoe', + 'pony', + 'vegas' + ], + modifiable: true), + Emoji( + name: 'horse racing: medium skin tone', + char: '\u{1F3C7}\u{1F3FD}', + shortName: 'horse_racing_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'horse', + 'jockey', + 'medium skin tone', + 'racehorse', + 'racing', + 'uc8', + 'sport', + 'diversity', + 'horse racing', + 'las vegas', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'horseback riding', + 'horse and rider', + 'horses', + 'horseshoe', + 'pony', + 'vegas' + ], + modifiable: true), + Emoji( + name: 'horse racing: medium-dark skin tone', + char: '\u{1F3C7}\u{1F3FE}', + shortName: 'horse_racing_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'horse', + 'jockey', + 'medium-dark skin tone', + 'racehorse', + 'racing', + 'uc8', + 'sport', + 'diversity', + 'horse racing', + 'las vegas', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'horseback riding', + 'horse and rider', + 'horses', + 'horseshoe', + 'pony', + 'vegas' + ], + modifiable: true), + Emoji( + name: 'horse racing: dark skin tone', + char: '\u{1F3C7}\u{1F3FF}', + shortName: 'horse_racing_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'dark skin tone', + 'horse', + 'jockey', + 'racehorse', + 'racing', + 'uc8', + 'sport', + 'diversity', + 'horse racing', + 'las vegas', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'horseback riding', + 'horse and rider', + 'horses', + 'horseshoe', + 'pony', + 'vegas' + ], + modifiable: true), + Emoji( + name: 'person in lotus position', + char: '\u{1F9D8}', + shortName: 'person_in_lotus_position', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personResting, + keywords: [ + 'uc10', + 'sport', + 'diversity', + 'vacation', + 'yoga', + 'california', + 'activity', + 'spa', + 'sit', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'relax', + 'sauna', + 'sitting', + 'kneel', + 'kneeling' + ]), + Emoji( + name: 'person in lotus position: light skin tone', + char: '\u{1F9D8}\u{1F3FB}', + shortName: 'person_in_lotus_position_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personResting, + keywords: [ + 'light skin tone', + 'meditation', + 'yoga', + 'uc10', + 'sport', + 'diversity', + 'vacation', + 'yoga', + 'california', + 'activity', + 'spa', + 'sit', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'relax', + 'sauna', + 'sitting', + 'kneel', + 'kneeling' + ], + modifiable: true), + Emoji( + name: 'person in lotus position: medium-light skin tone', + char: '\u{1F9D8}\u{1F3FC}', + shortName: 'person_in_lotus_position_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personResting, + keywords: [ + 'meditation', + 'medium-light skin tone', + 'yoga', + 'uc10', + 'sport', + 'diversity', + 'vacation', + 'yoga', + 'california', + 'activity', + 'spa', + 'sit', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'relax', + 'sauna', + 'sitting', + 'kneel', + 'kneeling' + ], + modifiable: true), + Emoji( + name: 'person in lotus position: medium skin tone', + char: '\u{1F9D8}\u{1F3FD}', + shortName: 'person_in_lotus_position_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personResting, + keywords: [ + 'meditation', + 'medium skin tone', + 'yoga', + 'uc10', + 'sport', + 'diversity', + 'vacation', + 'yoga', + 'california', + 'activity', + 'spa', + 'sit', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'relax', + 'sauna', + 'sitting', + 'kneel', + 'kneeling' + ], + modifiable: true), + Emoji( + name: 'person in lotus position: medium-dark skin tone', + char: '\u{1F9D8}\u{1F3FE}', + shortName: 'person_in_lotus_position_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personResting, + keywords: [ + 'meditation', + 'medium-dark skin tone', + 'yoga', + 'uc10', + 'sport', + 'diversity', + 'vacation', + 'yoga', + 'california', + 'activity', + 'spa', + 'sit', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'relax', + 'sauna', + 'sitting', + 'kneel', + 'kneeling' + ], + modifiable: true), + Emoji( + name: 'person in lotus position: dark skin tone', + char: '\u{1F9D8}\u{1F3FF}', + shortName: 'person_in_lotus_position_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personResting, + keywords: [ + 'dark skin tone', + 'meditation', + 'yoga', + 'uc10', + 'sport', + 'diversity', + 'vacation', + 'yoga', + 'california', + 'activity', + 'spa', + 'sit', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'relax', + 'sauna', + 'sitting', + 'kneel', + 'kneeling' + ], + modifiable: true), + Emoji( + name: 'woman in lotus position', + char: '\u{1F9D8}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_in_lotus_position', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personResting, + keywords: [ + 'meditation', + 'yoga', + 'uc10', + 'sport', + 'diversity', + 'women', + 'vacation', + 'yoga', + 'california', + 'activity', + 'spa', + 'sit', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'relax', + 'sauna', + 'sitting', + 'kneel', + 'kneeling' + ]), + Emoji( + name: 'woman in lotus position: light skin tone', + char: '\u{1F9D8}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_in_lotus_position_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personResting, + keywords: [ + 'light skin tone', + 'meditation', + 'yoga', + 'uc10', + 'sport', + 'diversity', + 'women', + 'vacation', + 'yoga', + 'california', + 'activity', + 'spa', + 'sit', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'relax', + 'sauna', + 'sitting', + 'kneel', + 'kneeling' + ], + modifiable: true), + Emoji( + name: 'woman in lotus position: medium-light skin tone', + char: '\u{1F9D8}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_in_lotus_position_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personResting, + keywords: [ + 'meditation', + 'medium-light skin tone', + 'yoga', + 'uc10', + 'sport', + 'diversity', + 'women', + 'vacation', + 'yoga', + 'california', + 'activity', + 'spa', + 'sit', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'relax', + 'sauna', + 'sitting', + 'kneel', + 'kneeling' + ], + modifiable: true), + Emoji( + name: 'woman in lotus position: medium skin tone', + char: '\u{1F9D8}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_in_lotus_position_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personResting, + keywords: [ + 'meditation', + 'medium skin tone', + 'yoga', + 'uc10', + 'sport', + 'diversity', + 'women', + 'vacation', + 'yoga', + 'california', + 'activity', + 'spa', + 'sit', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'relax', + 'sauna', + 'sitting', + 'kneel', + 'kneeling' + ], + modifiable: true), + Emoji( + name: 'woman in lotus position: medium-dark skin tone', + char: '\u{1F9D8}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_in_lotus_position_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personResting, + keywords: [ + 'meditation', + 'medium-dark skin tone', + 'yoga', + 'uc10', + 'sport', + 'diversity', + 'women', + 'vacation', + 'yoga', + 'california', + 'activity', + 'spa', + 'sit', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'relax', + 'sauna', + 'sitting', + 'kneel', + 'kneeling' + ], + modifiable: true), + Emoji( + name: 'woman in lotus position: dark skin tone', + char: '\u{1F9D8}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_in_lotus_position_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personResting, + keywords: [ + 'dark skin tone', + 'meditation', + 'yoga', + 'uc10', + 'sport', + 'diversity', + 'women', + 'vacation', + 'yoga', + 'california', + 'activity', + 'spa', + 'sit', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'woman', + 'female', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'relax', + 'sauna', + 'sitting', + 'kneel', + 'kneeling' + ], + modifiable: true), + Emoji( + name: 'man in lotus position', + char: '\u{1F9D8}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_in_lotus_position', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personResting, + keywords: [ + 'meditation', + 'yoga', + 'uc10', + 'sport', + 'diversity', + 'vacation', + 'yoga', + 'california', + 'activity', + 'spa', + 'sit', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'relax', + 'sauna', + 'sitting', + 'kneel', + 'kneeling' + ]), + Emoji( + name: 'man in lotus position: light skin tone', + char: '\u{1F9D8}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_in_lotus_position_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personResting, + keywords: [ + 'light skin tone', + 'meditation', + 'yoga', + 'uc10', + 'sport', + 'diversity', + 'vacation', + 'yoga', + 'california', + 'activity', + 'spa', + 'sit', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'relax', + 'sauna', + 'sitting', + 'kneel', + 'kneeling' + ], + modifiable: true), + Emoji( + name: 'man in lotus position: medium-light skin tone', + char: '\u{1F9D8}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_in_lotus_position_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personResting, + keywords: [ + 'meditation', + 'medium-light skin tone', + 'yoga', + 'uc10', + 'sport', + 'diversity', + 'vacation', + 'yoga', + 'california', + 'activity', + 'spa', + 'sit', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'relax', + 'sauna', + 'sitting', + 'kneel', + 'kneeling' + ], + modifiable: true), + Emoji( + name: 'man in lotus position: medium skin tone', + char: '\u{1F9D8}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_in_lotus_position_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personResting, + keywords: [ + 'meditation', + 'medium skin tone', + 'yoga', + 'uc10', + 'sport', + 'diversity', + 'vacation', + 'yoga', + 'california', + 'activity', + 'spa', + 'sit', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'relax', + 'sauna', + 'sitting', + 'kneel', + 'kneeling' + ], + modifiable: true), + Emoji( + name: 'man in lotus position: medium-dark skin tone', + char: '\u{1F9D8}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_in_lotus_position_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personResting, + keywords: [ + 'meditation', + 'medium-dark skin tone', + 'yoga', + 'uc10', + 'sport', + 'diversity', + 'vacation', + 'yoga', + 'california', + 'activity', + 'spa', + 'sit', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'relax', + 'sauna', + 'sitting', + 'kneel', + 'kneeling' + ], + modifiable: true), + Emoji( + name: 'man in lotus position: dark skin tone', + char: '\u{1F9D8}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_in_lotus_position_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personResting, + keywords: [ + 'dark skin tone', + 'meditation', + 'yoga', + 'uc10', + 'sport', + 'diversity', + 'vacation', + 'yoga', + 'california', + 'activity', + 'spa', + 'sit', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'meditation', + 'meditate', + 'zen', + 'om', + 'aum', + 'relax', + 'sauna', + 'sitting', + 'kneel', + 'kneeling' + ], + modifiable: true), + Emoji( + name: 'person surfing', + char: '\u{1F3C4}', + shortName: 'person_surfing', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'surfing', + 'uc6', + 'sport', + 'diversity', + 'tropical', + 'vacation', + 'hawaii', + 'california', + 'florida', + 'fun', + 'summer', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'aloha', + 'kawaii', + 'maui', + 'moana', + 'weekend' + ]), + Emoji( + name: 'person surfing: light skin tone', + char: '\u{1F3C4}\u{1F3FB}', + shortName: 'person_surfing_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'light skin tone', + 'surfing', + 'uc8', + 'sport', + 'diversity', + 'tropical', + 'vacation', + 'hawaii', + 'california', + 'florida', + 'fun', + 'summer', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'aloha', + 'kawaii', + 'maui', + 'moana', + 'weekend' + ], + modifiable: true), + Emoji( + name: 'person surfing: medium-light skin tone', + char: '\u{1F3C4}\u{1F3FC}', + shortName: 'person_surfing_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'medium-light skin tone', + 'surfing', + 'uc8', + 'sport', + 'diversity', + 'tropical', + 'vacation', + 'hawaii', + 'california', + 'florida', + 'fun', + 'summer', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'aloha', + 'kawaii', + 'maui', + 'moana', + 'weekend' + ], + modifiable: true), + Emoji( + name: 'person surfing: medium skin tone', + char: '\u{1F3C4}\u{1F3FD}', + shortName: 'person_surfing_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'medium skin tone', + 'surfing', + 'uc8', + 'sport', + 'diversity', + 'tropical', + 'vacation', + 'hawaii', + 'california', + 'florida', + 'fun', + 'summer', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'aloha', + 'kawaii', + 'maui', + 'moana', + 'weekend' + ], + modifiable: true), + Emoji( + name: 'person surfing: medium-dark skin tone', + char: '\u{1F3C4}\u{1F3FE}', + shortName: 'person_surfing_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'medium-dark skin tone', + 'surfing', + 'uc8', + 'sport', + 'diversity', + 'tropical', + 'vacation', + 'hawaii', + 'california', + 'florida', + 'fun', + 'summer', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'aloha', + 'kawaii', + 'maui', + 'moana', + 'weekend' + ], + modifiable: true), + Emoji( + name: 'person surfing: dark skin tone', + char: '\u{1F3C4}\u{1F3FF}', + shortName: 'person_surfing_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'dark skin tone', + 'surfing', + 'uc8', + 'sport', + 'diversity', + 'tropical', + 'vacation', + 'hawaii', + 'california', + 'florida', + 'fun', + 'summer', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'aloha', + 'kawaii', + 'maui', + 'moana', + 'weekend' + ], + modifiable: true), + Emoji( + name: 'woman surfing', + char: '\u{1F3C4}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_surfing', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'surfing', + 'woman', + 'uc6', + 'sport', + 'diversity', + 'tropical', + 'vacation', + 'hawaii', + 'california', + 'florida', + 'fun', + 'summer', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'aloha', + 'kawaii', + 'maui', + 'moana', + 'weekend' + ]), + Emoji( + name: 'woman surfing: light skin tone', + char: '\u{1F3C4}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_surfing_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'light skin tone', + 'surfing', + 'woman', + 'uc8', + 'sport', + 'diversity', + 'tropical', + 'vacation', + 'hawaii', + 'california', + 'florida', + 'fun', + 'summer', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'aloha', + 'kawaii', + 'maui', + 'moana', + 'weekend' + ], + modifiable: true), + Emoji( + name: 'woman surfing: medium-light skin tone', + char: '\u{1F3C4}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_surfing_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'medium-light skin tone', + 'surfing', + 'woman', + 'uc8', + 'sport', + 'diversity', + 'tropical', + 'vacation', + 'hawaii', + 'california', + 'florida', + 'fun', + 'summer', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'aloha', + 'kawaii', + 'maui', + 'moana', + 'weekend' + ], + modifiable: true), + Emoji( + name: 'woman surfing: medium skin tone', + char: '\u{1F3C4}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_surfing_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'medium skin tone', + 'surfing', + 'woman', + 'uc8', + 'sport', + 'diversity', + 'tropical', + 'vacation', + 'hawaii', + 'california', + 'florida', + 'fun', + 'summer', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'aloha', + 'kawaii', + 'maui', + 'moana', + 'weekend' + ], + modifiable: true), + Emoji( + name: 'woman surfing: medium-dark skin tone', + char: '\u{1F3C4}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_surfing_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'medium-dark skin tone', + 'surfing', + 'woman', + 'uc8', + 'sport', + 'diversity', + 'tropical', + 'vacation', + 'hawaii', + 'california', + 'florida', + 'fun', + 'summer', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'aloha', + 'kawaii', + 'maui', + 'moana', + 'weekend' + ], + modifiable: true), + Emoji( + name: 'woman surfing: dark skin tone', + char: '\u{1F3C4}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_surfing_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'dark skin tone', + 'surfing', + 'woman', + 'uc8', + 'sport', + 'diversity', + 'tropical', + 'vacation', + 'hawaii', + 'california', + 'florida', + 'fun', + 'summer', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'aloha', + 'kawaii', + 'maui', + 'moana', + 'weekend' + ], + modifiable: true), + Emoji( + name: 'man surfing', + char: '\u{1F3C4}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_surfing', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'man', + 'surfing', + 'uc6', + 'sport', + 'diversity', + 'tropical', + 'vacation', + 'hawaii', + 'california', + 'florida', + 'fun', + 'summer', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'aloha', + 'kawaii', + 'maui', + 'moana', + 'weekend' + ]), + Emoji( + name: 'man surfing: light skin tone', + char: '\u{1F3C4}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_surfing_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'light skin tone', + 'man', + 'surfing', + 'uc8', + 'sport', + 'diversity', + 'tropical', + 'vacation', + 'hawaii', + 'california', + 'florida', + 'fun', + 'summer', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'aloha', + 'kawaii', + 'maui', + 'moana', + 'weekend' + ], + modifiable: true), + Emoji( + name: 'man surfing: medium-light skin tone', + char: '\u{1F3C4}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_surfing_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'man', + 'medium-light skin tone', + 'surfing', + 'uc8', + 'sport', + 'diversity', + 'tropical', + 'vacation', + 'hawaii', + 'california', + 'florida', + 'fun', + 'summer', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'aloha', + 'kawaii', + 'maui', + 'moana', + 'weekend' + ], + modifiable: true), + Emoji( + name: 'man surfing: medium skin tone', + char: '\u{1F3C4}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_surfing_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'man', + 'medium skin tone', + 'surfing', + 'uc8', + 'sport', + 'diversity', + 'tropical', + 'vacation', + 'hawaii', + 'california', + 'florida', + 'fun', + 'summer', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'aloha', + 'kawaii', + 'maui', + 'moana', + 'weekend' + ], + modifiable: true), + Emoji( + name: 'man surfing: medium-dark skin tone', + char: '\u{1F3C4}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_surfing_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'man', + 'medium-dark skin tone', + 'surfing', + 'uc8', + 'sport', + 'diversity', + 'tropical', + 'vacation', + 'hawaii', + 'california', + 'florida', + 'fun', + 'summer', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'aloha', + 'kawaii', + 'maui', + 'moana', + 'weekend' + ], + modifiable: true), + Emoji( + name: 'man surfing: dark skin tone', + char: '\u{1F3C4}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_surfing_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'dark skin tone', + 'man', + 'surfing', + 'uc8', + 'sport', + 'diversity', + 'tropical', + 'vacation', + 'hawaii', + 'california', + 'florida', + 'fun', + 'summer', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'aloha', + 'kawaii', + 'maui', + 'moana', + 'weekend' + ], + modifiable: true), + Emoji( + name: 'person swimming', + char: '\u{1F3CA}', + shortName: 'person_swimming', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'swim', + 'uc6', + 'sport', + 'diversity', + 'vacation', + 'swim', + 'scuba', + 'summer', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'swimming', + 'swimmer', + 'snorkel', + 'weekend' + ]), + Emoji( + name: 'person swimming: light skin tone', + char: '\u{1F3CA}\u{1F3FB}', + shortName: 'person_swimming_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'light skin tone', + 'swim', + 'uc8', + 'sport', + 'diversity', + 'vacation', + 'swim', + 'scuba', + 'summer', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'swimming', + 'swimmer', + 'snorkel', + 'weekend' + ], + modifiable: true), + Emoji( + name: 'person swimming: medium-light skin tone', + char: '\u{1F3CA}\u{1F3FC}', + shortName: 'person_swimming_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'medium-light skin tone', + 'swim', + 'uc8', + 'sport', + 'diversity', + 'vacation', + 'swim', + 'scuba', + 'summer', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'swimming', + 'swimmer', + 'snorkel', + 'weekend' + ], + modifiable: true), + Emoji( + name: 'person swimming: medium skin tone', + char: '\u{1F3CA}\u{1F3FD}', + shortName: 'person_swimming_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'medium skin tone', + 'swim', + 'uc8', + 'sport', + 'diversity', + 'vacation', + 'swim', + 'scuba', + 'summer', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'swimming', + 'swimmer', + 'snorkel', + 'weekend' + ], + modifiable: true), + Emoji( + name: 'person swimming: medium-dark skin tone', + char: '\u{1F3CA}\u{1F3FE}', + shortName: 'person_swimming_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'medium-dark skin tone', + 'swim', + 'uc8', + 'sport', + 'diversity', + 'vacation', + 'swim', + 'scuba', + 'summer', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'swimming', + 'swimmer', + 'snorkel', + 'weekend' + ], + modifiable: true), + Emoji( + name: 'person swimming: dark skin tone', + char: '\u{1F3CA}\u{1F3FF}', + shortName: 'person_swimming_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'dark skin tone', + 'swim', + 'uc8', + 'sport', + 'diversity', + 'vacation', + 'swim', + 'scuba', + 'summer', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'swimming', + 'swimmer', + 'snorkel', + 'weekend' + ], + modifiable: true), + Emoji( + name: 'woman swimming', + char: '\u{1F3CA}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_swimming', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'swim', + 'woman', + 'uc6', + 'sport', + 'diversity', + 'vacation', + 'swim', + 'scuba', + 'summer', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'swimming', + 'swimmer', + 'snorkel', + 'weekend' + ]), + Emoji( + name: 'woman swimming: light skin tone', + char: '\u{1F3CA}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_swimming_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'light skin tone', + 'swim', + 'woman', + 'uc8', + 'sport', + 'diversity', + 'vacation', + 'swim', + 'scuba', + 'summer', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'swimming', + 'swimmer', + 'snorkel', + 'weekend' + ], + modifiable: true), + Emoji( + name: 'woman swimming: medium-light skin tone', + char: '\u{1F3CA}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_swimming_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'medium-light skin tone', + 'swim', + 'woman', + 'uc8', + 'sport', + 'diversity', + 'vacation', + 'swim', + 'scuba', + 'summer', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'swimming', + 'swimmer', + 'snorkel', + 'weekend' + ], + modifiable: true), + Emoji( + name: 'woman swimming: medium skin tone', + char: '\u{1F3CA}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_swimming_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'medium skin tone', + 'swim', + 'woman', + 'uc8', + 'sport', + 'diversity', + 'vacation', + 'swim', + 'scuba', + 'summer', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'swimming', + 'swimmer', + 'snorkel', + 'weekend' + ], + modifiable: true), + Emoji( + name: 'woman swimming: medium-dark skin tone', + char: '\u{1F3CA}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_swimming_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'medium-dark skin tone', + 'swim', + 'woman', + 'uc8', + 'sport', + 'diversity', + 'vacation', + 'swim', + 'scuba', + 'summer', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'swimming', + 'swimmer', + 'snorkel', + 'weekend' + ], + modifiable: true), + Emoji( + name: 'woman swimming: dark skin tone', + char: '\u{1F3CA}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_swimming_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'dark skin tone', + 'swim', + 'woman', + 'uc8', + 'sport', + 'diversity', + 'vacation', + 'swim', + 'scuba', + 'summer', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'swimming', + 'swimmer', + 'snorkel', + 'weekend' + ], + modifiable: true), + Emoji( + name: 'man swimming', + char: '\u{1F3CA}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_swimming', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'man', + 'swim', + 'uc6', + 'sport', + 'diversity', + 'vacation', + 'swim', + 'scuba', + 'summer', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'swimming', + 'swimmer', + 'snorkel', + 'weekend' + ]), + Emoji( + name: 'man swimming: light skin tone', + char: '\u{1F3CA}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_swimming_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'light skin tone', + 'man', + 'swim', + 'uc8', + 'sport', + 'diversity', + 'vacation', + 'swim', + 'scuba', + 'summer', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'swimming', + 'swimmer', + 'snorkel', + 'weekend' + ], + modifiable: true), + Emoji( + name: 'man swimming: medium-light skin tone', + char: '\u{1F3CA}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_swimming_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'man', + 'medium-light skin tone', + 'swim', + 'uc8', + 'sport', + 'diversity', + 'vacation', + 'swim', + 'scuba', + 'summer', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'swimming', + 'swimmer', + 'snorkel', + 'weekend' + ], + modifiable: true), + Emoji( + name: 'man swimming: medium skin tone', + char: '\u{1F3CA}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_swimming_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'man', + 'medium skin tone', + 'swim', + 'uc8', + 'sport', + 'diversity', + 'vacation', + 'swim', + 'scuba', + 'summer', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'swimming', + 'swimmer', + 'snorkel', + 'weekend' + ], + modifiable: true), + Emoji( + name: 'man swimming: medium-dark skin tone', + char: '\u{1F3CA}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_swimming_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'man', + 'medium-dark skin tone', + 'swim', + 'uc8', + 'sport', + 'diversity', + 'vacation', + 'swim', + 'scuba', + 'summer', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'swimming', + 'swimmer', + 'snorkel', + 'weekend' + ], + modifiable: true), + Emoji( + name: 'man swimming: dark skin tone', + char: '\u{1F3CA}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_swimming_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'dark skin tone', + 'man', + 'swim', + 'uc8', + 'sport', + 'diversity', + 'vacation', + 'swim', + 'scuba', + 'summer', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'swimming', + 'swimmer', + 'snorkel', + 'weekend' + ], + modifiable: true), + Emoji( + name: 'person playing water polo', + char: '\u{1F93D}', + shortName: 'person_playing_water_polo', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'polo', + 'water', + 'uc9', + 'sport', + 'diversity', + 'ball', + 'play', + 'throw', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon' + ]), + Emoji( + name: 'person playing water polo: light skin tone', + char: '\u{1F93D}\u{1F3FB}', + shortName: 'person_playing_water_polo_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'light skin tone', + 'polo', + 'water', + 'uc9', + 'sport', + 'diversity', + 'ball', + 'play', + 'throw', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon' + ], + modifiable: true), + Emoji( + name: 'person playing water polo: medium-light skin tone', + char: '\u{1F93D}\u{1F3FC}', + shortName: 'person_playing_water_polo_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'medium-light skin tone', + 'polo', + 'water', + 'uc9', + 'sport', + 'diversity', + 'ball', + 'play', + 'throw', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon' + ], + modifiable: true), + Emoji( + name: 'person playing water polo: medium skin tone', + char: '\u{1F93D}\u{1F3FD}', + shortName: 'person_playing_water_polo_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'medium skin tone', + 'polo', + 'water', + 'uc9', + 'sport', + 'diversity', + 'ball', + 'play', + 'throw', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon' + ], + modifiable: true), + Emoji( + name: 'person playing water polo: medium-dark skin tone', + char: '\u{1F93D}\u{1F3FE}', + shortName: 'person_playing_water_polo_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'medium-dark skin tone', + 'polo', + 'water', + 'uc9', + 'sport', + 'diversity', + 'ball', + 'play', + 'throw', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon' + ], + modifiable: true), + Emoji( + name: 'person playing water polo: dark skin tone', + char: '\u{1F93D}\u{1F3FF}', + shortName: 'person_playing_water_polo_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'dark skin tone', + 'polo', + 'water', + 'uc9', + 'sport', + 'diversity', + 'ball', + 'play', + 'throw', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon' + ], + modifiable: true), + Emoji( + name: 'woman playing water polo', + char: '\u{1F93D}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_playing_water_polo', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'water polo', + 'woman', + 'uc9', + 'sport', + 'diversity', + 'ball', + 'play', + 'throw', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon' + ]), + Emoji( + name: 'woman playing water polo: light skin tone', + char: '\u{1F93D}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_playing_water_polo_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'light skin tone', + 'water polo', + 'woman', + 'uc9', + 'sport', + 'diversity', + 'ball', + 'play', + 'throw', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon' + ], + modifiable: true), + Emoji( + name: 'woman playing water polo: medium-light skin tone', + char: '\u{1F93D}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_playing_water_polo_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'medium-light skin tone', + 'water polo', + 'woman', + 'uc9', + 'sport', + 'diversity', + 'ball', + 'play', + 'throw', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon' + ], + modifiable: true), + Emoji( + name: 'woman playing water polo: medium skin tone', + char: '\u{1F93D}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_playing_water_polo_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'medium skin tone', + 'water polo', + 'woman', + 'uc9', + 'sport', + 'diversity', + 'ball', + 'play', + 'throw', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon' + ], + modifiable: true), + Emoji( + name: 'woman playing water polo: medium-dark skin tone', + char: '\u{1F93D}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_playing_water_polo_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'medium-dark skin tone', + 'water polo', + 'woman', + 'uc9', + 'sport', + 'diversity', + 'ball', + 'play', + 'throw', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon' + ], + modifiable: true), + Emoji( + name: 'woman playing water polo: dark skin tone', + char: '\u{1F93D}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_playing_water_polo_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'dark skin tone', + 'water polo', + 'woman', + 'uc9', + 'sport', + 'diversity', + 'ball', + 'play', + 'throw', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon' + ], + modifiable: true), + Emoji( + name: 'man playing water polo', + char: '\u{1F93D}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_playing_water_polo', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'man', + 'water polo', + 'uc9', + 'sport', + 'diversity', + 'ball', + 'play', + 'throw', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon' + ]), + Emoji( + name: 'man playing water polo: light skin tone', + char: '\u{1F93D}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_playing_water_polo_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'light skin tone', + 'man', + 'water polo', + 'uc9', + 'sport', + 'diversity', + 'ball', + 'play', + 'throw', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon' + ], + modifiable: true), + Emoji( + name: 'man playing water polo: medium-light skin tone', + char: '\u{1F93D}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_playing_water_polo_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'man', + 'medium-light skin tone', + 'water polo', + 'uc9', + 'sport', + 'diversity', + 'ball', + 'play', + 'throw', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon' + ], + modifiable: true), + Emoji( + name: 'man playing water polo: medium skin tone', + char: '\u{1F93D}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_playing_water_polo_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'man', + 'medium skin tone', + 'water polo', + 'uc9', + 'sport', + 'diversity', + 'ball', + 'play', + 'throw', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon' + ], + modifiable: true), + Emoji( + name: 'man playing water polo: medium-dark skin tone', + char: '\u{1F93D}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_playing_water_polo_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'man', + 'medium-dark skin tone', + 'water polo', + 'uc9', + 'sport', + 'diversity', + 'ball', + 'play', + 'throw', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon' + ], + modifiable: true), + Emoji( + name: 'man playing water polo: dark skin tone', + char: '\u{1F93D}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_playing_water_polo_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'dark skin tone', + 'man', + 'water polo', + 'uc9', + 'sport', + 'diversity', + 'ball', + 'play', + 'throw', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon' + ], + modifiable: true), + Emoji( + name: 'person rowing boat', + char: '\u{1F6A3}', + shortName: 'person_rowing_boat', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'boat', + 'rowboat', + 'uc6', + 'sport', + 'diversity', + 'boat', + 'rowing', + 'hawaii', + 'scuba', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'boats', + 'boating', + 'rowboat', + 'canoe', + 'aloha', + 'kawaii', + 'maui', + 'moana', + 'snorkel' + ]), + Emoji( + name: 'person rowing boat: light skin tone', + char: '\u{1F6A3}\u{1F3FB}', + shortName: 'person_rowing_boat_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'boat', + 'light skin tone', + 'rowboat', + 'uc8', + 'sport', + 'diversity', + 'boat', + 'rowing', + 'hawaii', + 'scuba', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'boats', + 'boating', + 'rowboat', + 'canoe', + 'aloha', + 'kawaii', + 'maui', + 'moana', + 'snorkel' + ], + modifiable: true), + Emoji( + name: 'person rowing boat: medium-light skin tone', + char: '\u{1F6A3}\u{1F3FC}', + shortName: 'person_rowing_boat_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'boat', + 'medium-light skin tone', + 'rowboat', + 'uc8', + 'sport', + 'diversity', + 'boat', + 'rowing', + 'hawaii', + 'scuba', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'boats', + 'boating', + 'rowboat', + 'canoe', + 'aloha', + 'kawaii', + 'maui', + 'moana', + 'snorkel' + ], + modifiable: true), + Emoji( + name: 'person rowing boat: medium skin tone', + char: '\u{1F6A3}\u{1F3FD}', + shortName: 'person_rowing_boat_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'boat', + 'medium skin tone', + 'rowboat', + 'uc8', + 'sport', + 'diversity', + 'boat', + 'rowing', + 'hawaii', + 'scuba', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'boats', + 'boating', + 'rowboat', + 'canoe', + 'aloha', + 'kawaii', + 'maui', + 'moana', + 'snorkel' + ], + modifiable: true), + Emoji( + name: 'person rowing boat: medium-dark skin tone', + char: '\u{1F6A3}\u{1F3FE}', + shortName: 'person_rowing_boat_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'boat', + 'medium-dark skin tone', + 'rowboat', + 'uc8', + 'sport', + 'diversity', + 'boat', + 'rowing', + 'hawaii', + 'scuba', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'boats', + 'boating', + 'rowboat', + 'canoe', + 'aloha', + 'kawaii', + 'maui', + 'moana', + 'snorkel' + ], + modifiable: true), + Emoji( + name: 'person rowing boat: dark skin tone', + char: '\u{1F6A3}\u{1F3FF}', + shortName: 'person_rowing_boat_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'boat', + 'dark skin tone', + 'rowboat', + 'uc8', + 'sport', + 'diversity', + 'boat', + 'rowing', + 'hawaii', + 'scuba', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'boats', + 'boating', + 'rowboat', + 'canoe', + 'aloha', + 'kawaii', + 'maui', + 'moana', + 'snorkel' + ], + modifiable: true), + Emoji( + name: 'woman rowing boat', + char: '\u{1F6A3}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_rowing_boat', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'boat', + 'rowboat', + 'woman', + 'uc6', + 'sport', + 'diversity', + 'boat', + 'rowing', + 'hawaii', + 'scuba', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'boats', + 'boating', + 'rowboat', + 'canoe', + 'aloha', + 'kawaii', + 'maui', + 'moana', + 'snorkel' + ]), + Emoji( + name: 'woman rowing boat: light skin tone', + char: '\u{1F6A3}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_rowing_boat_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'boat', + 'light skin tone', + 'rowboat', + 'woman', + 'uc8', + 'sport', + 'diversity', + 'boat', + 'rowing', + 'hawaii', + 'scuba', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'boats', + 'boating', + 'rowboat', + 'canoe', + 'aloha', + 'kawaii', + 'maui', + 'moana', + 'snorkel' + ], + modifiable: true), + Emoji( + name: 'woman rowing boat: medium-light skin tone', + char: '\u{1F6A3}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_rowing_boat_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'boat', + 'medium-light skin tone', + 'rowboat', + 'woman', + 'uc8', + 'sport', + 'diversity', + 'boat', + 'rowing', + 'hawaii', + 'scuba', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'boats', + 'boating', + 'rowboat', + 'canoe', + 'aloha', + 'kawaii', + 'maui', + 'moana', + 'snorkel' + ], + modifiable: true), + Emoji( + name: 'woman rowing boat: medium skin tone', + char: '\u{1F6A3}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_rowing_boat_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'boat', + 'medium skin tone', + 'rowboat', + 'woman', + 'uc8', + 'sport', + 'diversity', + 'boat', + 'rowing', + 'hawaii', + 'scuba', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'boats', + 'boating', + 'rowboat', + 'canoe', + 'aloha', + 'kawaii', + 'maui', + 'moana', + 'snorkel' + ], + modifiable: true), + Emoji( + name: 'woman rowing boat: medium-dark skin tone', + char: '\u{1F6A3}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_rowing_boat_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'boat', + 'medium-dark skin tone', + 'rowboat', + 'woman', + 'uc8', + 'sport', + 'diversity', + 'boat', + 'rowing', + 'hawaii', + 'scuba', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'boats', + 'boating', + 'rowboat', + 'canoe', + 'aloha', + 'kawaii', + 'maui', + 'moana', + 'snorkel' + ], + modifiable: true), + Emoji( + name: 'woman rowing boat: dark skin tone', + char: '\u{1F6A3}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_rowing_boat_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'boat', + 'dark skin tone', + 'rowboat', + 'woman', + 'uc8', + 'sport', + 'diversity', + 'boat', + 'rowing', + 'hawaii', + 'scuba', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'boats', + 'boating', + 'rowboat', + 'canoe', + 'aloha', + 'kawaii', + 'maui', + 'moana', + 'snorkel' + ], + modifiable: true), + Emoji( + name: 'man rowing boat', + char: '\u{1F6A3}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_rowing_boat', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'boat', + 'man', + 'rowboat', + 'uc6', + 'sport', + 'diversity', + 'boat', + 'rowing', + 'hawaii', + 'scuba', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'boats', + 'boating', + 'rowboat', + 'canoe', + 'aloha', + 'kawaii', + 'maui', + 'moana', + 'snorkel' + ]), + Emoji( + name: 'man rowing boat: light skin tone', + char: '\u{1F6A3}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_rowing_boat_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'boat', + 'light skin tone', + 'man', + 'rowboat', + 'uc8', + 'sport', + 'diversity', + 'boat', + 'rowing', + 'hawaii', + 'scuba', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'boats', + 'boating', + 'rowboat', + 'canoe', + 'aloha', + 'kawaii', + 'maui', + 'moana', + 'snorkel' + ], + modifiable: true), + Emoji( + name: 'man rowing boat: medium-light skin tone', + char: '\u{1F6A3}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_rowing_boat_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'boat', + 'man', + 'medium-light skin tone', + 'rowboat', + 'uc8', + 'sport', + 'diversity', + 'boat', + 'rowing', + 'hawaii', + 'scuba', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'boats', + 'boating', + 'rowboat', + 'canoe', + 'aloha', + 'kawaii', + 'maui', + 'moana', + 'snorkel' + ], + modifiable: true), + Emoji( + name: 'man rowing boat: medium skin tone', + char: '\u{1F6A3}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_rowing_boat_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'boat', + 'man', + 'medium skin tone', + 'rowboat', + 'uc8', + 'sport', + 'diversity', + 'boat', + 'rowing', + 'hawaii', + 'scuba', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'boats', + 'boating', + 'rowboat', + 'canoe', + 'aloha', + 'kawaii', + 'maui', + 'moana', + 'snorkel' + ], + modifiable: true), + Emoji( + name: 'man rowing boat: medium-dark skin tone', + char: '\u{1F6A3}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_rowing_boat_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'boat', + 'man', + 'medium-dark skin tone', + 'rowboat', + 'uc8', + 'sport', + 'diversity', + 'boat', + 'rowing', + 'hawaii', + 'scuba', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'boats', + 'boating', + 'rowboat', + 'canoe', + 'aloha', + 'kawaii', + 'maui', + 'moana', + 'snorkel' + ], + modifiable: true), + Emoji( + name: 'man rowing boat: dark skin tone', + char: '\u{1F6A3}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_rowing_boat_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'boat', + 'dark skin tone', + 'man', + 'rowboat', + 'uc8', + 'sport', + 'diversity', + 'boat', + 'rowing', + 'hawaii', + 'scuba', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'boats', + 'boating', + 'rowboat', + 'canoe', + 'aloha', + 'kawaii', + 'maui', + 'moana', + 'snorkel' + ], + modifiable: true), + Emoji( + name: 'person climbing', + char: '\u{1F9D7}', + shortName: 'person_climbing', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'climber', + 'uc10', + 'sport', + 'diversity', + 'fun', + 'activity', + 'rock climbing', + 'climb', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'climber' + ]), + Emoji( + name: 'person climbing: light skin tone', + char: '\u{1F9D7}\u{1F3FB}', + shortName: 'person_climbing_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'climber', + 'light skin tone', + 'uc10', + 'sport', + 'diversity', + 'fun', + 'activity', + 'rock climbing', + 'climb', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'climber' + ], + modifiable: true), + Emoji( + name: 'person climbing: medium-light skin tone', + char: '\u{1F9D7}\u{1F3FC}', + shortName: 'person_climbing_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'climber', + 'medium-light skin tone', + 'uc10', + 'sport', + 'diversity', + 'fun', + 'activity', + 'rock climbing', + 'climb', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'climber' + ], + modifiable: true), + Emoji( + name: 'person climbing: medium skin tone', + char: '\u{1F9D7}\u{1F3FD}', + shortName: 'person_climbing_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'climber', + 'medium skin tone', + 'uc10', + 'sport', + 'diversity', + 'fun', + 'activity', + 'rock climbing', + 'climb', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'climber' + ], + modifiable: true), + Emoji( + name: 'person climbing: medium-dark skin tone', + char: '\u{1F9D7}\u{1F3FE}', + shortName: 'person_climbing_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'climber', + 'medium-dark skin tone', + 'uc10', + 'sport', + 'diversity', + 'fun', + 'activity', + 'rock climbing', + 'climb', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'climber' + ], + modifiable: true), + Emoji( + name: 'person climbing: dark skin tone', + char: '\u{1F9D7}\u{1F3FF}', + shortName: 'person_climbing_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'climber', + 'dark skin tone', + 'uc10', + 'sport', + 'diversity', + 'fun', + 'activity', + 'rock climbing', + 'climb', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'climber' + ], + modifiable: true), + Emoji( + name: 'woman climbing', + char: '\u{1F9D7}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_climbing', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'climber', + 'uc10', + 'sport', + 'diversity', + 'fun', + 'activity', + 'rock climbing', + 'climb', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'climber' + ]), + Emoji( + name: 'woman climbing: light skin tone', + char: '\u{1F9D7}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_climbing_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'climber', + 'light skin tone', + 'uc10', + 'sport', + 'diversity', + 'fun', + 'activity', + 'rock climbing', + 'climb', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'climber' + ], + modifiable: true), + Emoji( + name: 'woman climbing: medium-light skin tone', + char: '\u{1F9D7}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_climbing_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'climber', + 'medium-light skin tone', + 'uc10', + 'sport', + 'diversity', + 'fun', + 'activity', + 'rock climbing', + 'climb', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'climber' + ], + modifiable: true), + Emoji( + name: 'woman climbing: medium skin tone', + char: '\u{1F9D7}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_climbing_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'climber', + 'medium skin tone', + 'uc10', + 'sport', + 'diversity', + 'fun', + 'activity', + 'rock climbing', + 'climb', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'climber' + ], + modifiable: true), + Emoji( + name: 'woman climbing: medium-dark skin tone', + char: '\u{1F9D7}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_climbing_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'climber', + 'medium-dark skin tone', + 'uc10', + 'sport', + 'diversity', + 'fun', + 'activity', + 'rock climbing', + 'climb', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'climber' + ], + modifiable: true), + Emoji( + name: 'woman climbing: dark skin tone', + char: '\u{1F9D7}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_climbing_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'climber', + 'dark skin tone', + 'uc10', + 'sport', + 'diversity', + 'fun', + 'activity', + 'rock climbing', + 'climb', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'climber' + ], + modifiable: true), + Emoji( + name: 'man climbing', + char: '\u{1F9D7}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_climbing', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'climber', + 'uc10', + 'sport', + 'diversity', + 'fun', + 'activity', + 'rock climbing', + 'climb', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'climber' + ]), + Emoji( + name: 'man climbing: light skin tone', + char: '\u{1F9D7}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_climbing_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'climber', + 'light skin tone', + 'uc10', + 'sport', + 'diversity', + 'fun', + 'activity', + 'rock climbing', + 'climb', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'climber' + ], + modifiable: true), + Emoji( + name: 'man climbing: medium-light skin tone', + char: '\u{1F9D7}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_climbing_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'climber', + 'medium-light skin tone', + 'uc10', + 'sport', + 'diversity', + 'fun', + 'activity', + 'rock climbing', + 'climb', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'climber' + ], + modifiable: true), + Emoji( + name: 'man climbing: medium skin tone', + char: '\u{1F9D7}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_climbing_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'climber', + 'medium skin tone', + 'uc10', + 'sport', + 'diversity', + 'fun', + 'activity', + 'rock climbing', + 'climb', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'climber' + ], + modifiable: true), + Emoji( + name: 'man climbing: medium-dark skin tone', + char: '\u{1F9D7}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_climbing_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'climber', + 'medium-dark skin tone', + 'uc10', + 'sport', + 'diversity', + 'fun', + 'activity', + 'rock climbing', + 'climb', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'climber' + ], + modifiable: true), + Emoji( + name: 'man climbing: dark skin tone', + char: '\u{1F9D7}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_climbing_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personActivity, + keywords: [ + 'climber', + 'dark skin tone', + 'uc10', + 'sport', + 'diversity', + 'fun', + 'activity', + 'rock climbing', + 'climb', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'climber' + ], + modifiable: true), + Emoji( + name: 'person mountain biking', + char: '\u{1F6B5}', + shortName: 'person_mountain_biking', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'bicycle', + 'bicyclist', + 'bike', + 'cyclist', + 'mountain', + 'uc6', + 'sport', + 'diversity', + 'bike', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'bikes', + 'bicycle', + 'bicycling' + ]), + Emoji( + name: 'person mountain biking: light skin tone', + char: '\u{1F6B5}\u{1F3FB}', + shortName: 'person_mountain_biking_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'bicycle', + 'bicyclist', + 'bike', + 'cyclist', + 'light skin tone', + 'mountain', + 'uc8', + 'sport', + 'diversity', + 'bike', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'bikes', + 'bicycle', + 'bicycling' + ], + modifiable: true), + Emoji( + name: 'person mountain biking: medium-light skin tone', + char: '\u{1F6B5}\u{1F3FC}', + shortName: 'person_mountain_biking_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'bicycle', + 'bicyclist', + 'bike', + 'cyclist', + 'medium-light skin tone', + 'mountain', + 'uc8', + 'sport', + 'diversity', + 'bike', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'bikes', + 'bicycle', + 'bicycling' + ], + modifiable: true), + Emoji( + name: 'person mountain biking: medium skin tone', + char: '\u{1F6B5}\u{1F3FD}', + shortName: 'person_mountain_biking_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'bicycle', + 'bicyclist', + 'bike', + 'cyclist', + 'medium skin tone', + 'mountain', + 'uc8', + 'sport', + 'diversity', + 'bike', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'bikes', + 'bicycle', + 'bicycling' + ], + modifiable: true), + Emoji( + name: 'person mountain biking: medium-dark skin tone', + char: '\u{1F6B5}\u{1F3FE}', + shortName: 'person_mountain_biking_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'bicycle', + 'bicyclist', + 'bike', + 'cyclist', + 'medium-dark skin tone', + 'mountain', + 'uc8', + 'sport', + 'diversity', + 'bike', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'bikes', + 'bicycle', + 'bicycling' + ], + modifiable: true), + Emoji( + name: 'person mountain biking: dark skin tone', + char: '\u{1F6B5}\u{1F3FF}', + shortName: 'person_mountain_biking_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'bicycle', + 'bicyclist', + 'bike', + 'cyclist', + 'dark skin tone', + 'mountain', + 'uc8', + 'sport', + 'diversity', + 'bike', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'bikes', + 'bicycle', + 'bicycling' + ], + modifiable: true), + Emoji( + name: 'woman mountain biking', + char: '\u{1F6B5}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_mountain_biking', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'bicycle', + 'bike', + 'biking', + 'cyclist', + 'mountain', + 'woman', + 'uc6', + 'sport', + 'diversity', + 'bike', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'bikes', + 'bicycle', + 'bicycling' + ]), + Emoji( + name: 'woman mountain biking: light skin tone', + char: '\u{1F6B5}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_mountain_biking_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'bicycle', + 'bike', + 'biking', + 'cyclist', + 'light skin tone', + 'mountain', + 'woman', + 'uc8', + 'sport', + 'diversity', + 'bike', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'bikes', + 'bicycle', + 'bicycling' + ], + modifiable: true), + Emoji( + name: 'woman mountain biking: medium-light skin tone', + char: '\u{1F6B5}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_mountain_biking_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'bicycle', + 'bike', + 'biking', + 'cyclist', + 'medium-light skin tone', + 'mountain', + 'woman', + 'uc8', + 'sport', + 'diversity', + 'bike', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'bikes', + 'bicycle', + 'bicycling' + ], + modifiable: true), + Emoji( + name: 'woman mountain biking: medium skin tone', + char: '\u{1F6B5}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_mountain_biking_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'bicycle', + 'bike', + 'biking', + 'cyclist', + 'medium skin tone', + 'mountain', + 'woman', + 'uc8', + 'sport', + 'diversity', + 'bike', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'bikes', + 'bicycle', + 'bicycling' + ], + modifiable: true), + Emoji( + name: 'woman mountain biking: medium-dark skin tone', + char: '\u{1F6B5}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_mountain_biking_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'bicycle', + 'bike', + 'biking', + 'cyclist', + 'medium-dark skin tone', + 'mountain', + 'woman', + 'uc8', + 'sport', + 'diversity', + 'bike', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'bikes', + 'bicycle', + 'bicycling' + ], + modifiable: true), + Emoji( + name: 'woman mountain biking: dark skin tone', + char: '\u{1F6B5}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_mountain_biking_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'bicycle', + 'bike', + 'biking', + 'cyclist', + 'dark skin tone', + 'mountain', + 'woman', + 'uc8', + 'sport', + 'diversity', + 'bike', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'bikes', + 'bicycle', + 'bicycling' + ], + modifiable: true), + Emoji( + name: 'man mountain biking', + char: '\u{1F6B5}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_mountain_biking', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'bicycle', + 'bike', + 'cyclist', + 'man', + 'mountain', + 'uc6', + 'sport', + 'diversity', + 'bike', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'bikes', + 'bicycle', + 'bicycling' + ]), + Emoji( + name: 'man mountain biking: light skin tone', + char: '\u{1F6B5}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_mountain_biking_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'bicycle', + 'bike', + 'cyclist', + 'light skin tone', + 'man', + 'mountain', + 'uc8', + 'sport', + 'diversity', + 'bike', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'bikes', + 'bicycle', + 'bicycling' + ], + modifiable: true), + Emoji( + name: 'man mountain biking: medium-light skin tone', + char: '\u{1F6B5}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_mountain_biking_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'bicycle', + 'bike', + 'cyclist', + 'man', + 'medium-light skin tone', + 'mountain', + 'uc8', + 'sport', + 'diversity', + 'bike', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'bikes', + 'bicycle', + 'bicycling' + ], + modifiable: true), + Emoji( + name: 'man mountain biking: medium skin tone', + char: '\u{1F6B5}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_mountain_biking_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'bicycle', + 'bike', + 'cyclist', + 'man', + 'medium skin tone', + 'mountain', + 'uc8', + 'sport', + 'diversity', + 'bike', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'bikes', + 'bicycle', + 'bicycling' + ], + modifiable: true), + Emoji( + name: 'man mountain biking: medium-dark skin tone', + char: '\u{1F6B5}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_mountain_biking_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'bicycle', + 'bike', + 'cyclist', + 'man', + 'medium-dark skin tone', + 'mountain', + 'uc8', + 'sport', + 'diversity', + 'bike', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'bikes', + 'bicycle', + 'bicycling' + ], + modifiable: true), + Emoji( + name: 'man mountain biking: dark skin tone', + char: '\u{1F6B5}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_mountain_biking_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'bicycle', + 'bike', + 'cyclist', + 'dark skin tone', + 'man', + 'mountain', + 'uc8', + 'sport', + 'diversity', + 'bike', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'bikes', + 'bicycle', + 'bicycling' + ], + modifiable: true), + Emoji( + name: 'person biking', + char: '\u{1F6B4}', + shortName: 'person_biking', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'bicycle', + 'biking', + 'cyclist', + 'uc6', + 'sport', + 'diversity', + 'bike', + 'fame', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'bikes', + 'bicycle', + 'bicycling', + 'famous', + 'celebrity' + ]), + Emoji( + name: 'person biking: light skin tone', + char: '\u{1F6B4}\u{1F3FB}', + shortName: 'person_biking_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'bicycle', + 'biking', + 'cyclist', + 'light skin tone', + 'uc8', + 'sport', + 'diversity', + 'bike', + 'fame', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'bikes', + 'bicycle', + 'bicycling', + 'famous', + 'celebrity' + ], + modifiable: true), + Emoji( + name: 'person biking: medium-light skin tone', + char: '\u{1F6B4}\u{1F3FC}', + shortName: 'person_biking_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'bicycle', + 'biking', + 'cyclist', + 'medium-light skin tone', + 'uc8', + 'sport', + 'diversity', + 'bike', + 'fame', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'bikes', + 'bicycle', + 'bicycling', + 'famous', + 'celebrity' + ], + modifiable: true), + Emoji( + name: 'person biking: medium skin tone', + char: '\u{1F6B4}\u{1F3FD}', + shortName: 'person_biking_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'bicycle', + 'biking', + 'cyclist', + 'medium skin tone', + 'uc8', + 'sport', + 'diversity', + 'bike', + 'fame', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'bikes', + 'bicycle', + 'bicycling', + 'famous', + 'celebrity' + ], + modifiable: true), + Emoji( + name: 'person biking: medium-dark skin tone', + char: '\u{1F6B4}\u{1F3FE}', + shortName: 'person_biking_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'bicycle', + 'biking', + 'cyclist', + 'medium-dark skin tone', + 'uc8', + 'sport', + 'diversity', + 'bike', + 'fame', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'bikes', + 'bicycle', + 'bicycling', + 'famous', + 'celebrity' + ], + modifiable: true), + Emoji( + name: 'person biking: dark skin tone', + char: '\u{1F6B4}\u{1F3FF}', + shortName: 'person_biking_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'bicycle', + 'biking', + 'cyclist', + 'dark skin tone', + 'uc8', + 'sport', + 'diversity', + 'bike', + 'fame', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'bikes', + 'bicycle', + 'bicycling', + 'famous', + 'celebrity' + ], + modifiable: true), + Emoji( + name: 'woman biking', + char: '\u{1F6B4}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_biking', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'bicycle', + 'biking', + 'cyclist', + 'woman', + 'uc6', + 'sport', + 'diversity', + 'bike', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'bikes', + 'bicycle', + 'bicycling' + ]), + Emoji( + name: 'woman biking: light skin tone', + char: '\u{1F6B4}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_biking_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'bicycle', + 'biking', + 'cyclist', + 'light skin tone', + 'woman', + 'uc8', + 'sport', + 'diversity', + 'bike', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'bikes', + 'bicycle', + 'bicycling' + ], + modifiable: true), + Emoji( + name: 'woman biking: medium-light skin tone', + char: '\u{1F6B4}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_biking_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'bicycle', + 'biking', + 'cyclist', + 'medium-light skin tone', + 'woman', + 'uc8', + 'sport', + 'diversity', + 'bike', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'bikes', + 'bicycle', + 'bicycling' + ], + modifiable: true), + Emoji( + name: 'woman biking: medium skin tone', + char: '\u{1F6B4}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_biking_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'bicycle', + 'biking', + 'cyclist', + 'medium skin tone', + 'woman', + 'uc8', + 'sport', + 'diversity', + 'bike', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'bikes', + 'bicycle', + 'bicycling' + ], + modifiable: true), + Emoji( + name: 'woman biking: medium-dark skin tone', + char: '\u{1F6B4}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_biking_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'bicycle', + 'biking', + 'cyclist', + 'medium-dark skin tone', + 'woman', + 'uc8', + 'sport', + 'diversity', + 'bike', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'bikes', + 'bicycle', + 'bicycling' + ], + modifiable: true), + Emoji( + name: 'woman biking: dark skin tone', + char: '\u{1F6B4}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_biking_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'bicycle', + 'biking', + 'cyclist', + 'dark skin tone', + 'woman', + 'uc8', + 'sport', + 'diversity', + 'bike', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'bikes', + 'bicycle', + 'bicycling' + ], + modifiable: true), + Emoji( + name: 'man biking', + char: '\u{1F6B4}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_biking', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'bicycle', + 'biking', + 'cyclist', + 'man', + 'uc6', + 'sport', + 'diversity', + 'bike', + 'fame', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'bikes', + 'bicycle', + 'bicycling', + 'famous', + 'celebrity' + ]), + Emoji( + name: 'man biking: light skin tone', + char: '\u{1F6B4}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_biking_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'bicycle', + 'biking', + 'cyclist', + 'light skin tone', + 'man', + 'uc8', + 'sport', + 'diversity', + 'bike', + 'fame', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'bikes', + 'bicycle', + 'bicycling', + 'famous', + 'celebrity' + ], + modifiable: true), + Emoji( + name: 'man biking: medium-light skin tone', + char: '\u{1F6B4}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_biking_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'bicycle', + 'biking', + 'cyclist', + 'man', + 'medium-light skin tone', + 'uc8', + 'sport', + 'diversity', + 'bike', + 'fame', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'bikes', + 'bicycle', + 'bicycling', + 'famous', + 'celebrity' + ], + modifiable: true), + Emoji( + name: 'man biking: medium skin tone', + char: '\u{1F6B4}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_biking_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'bicycle', + 'biking', + 'cyclist', + 'man', + 'medium skin tone', + 'uc8', + 'sport', + 'diversity', + 'bike', + 'fame', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'bikes', + 'bicycle', + 'bicycling', + 'famous', + 'celebrity' + ], + modifiable: true), + Emoji( + name: 'man biking: medium-dark skin tone', + char: '\u{1F6B4}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_biking_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'bicycle', + 'biking', + 'cyclist', + 'man', + 'medium-dark skin tone', + 'uc8', + 'sport', + 'diversity', + 'bike', + 'fame', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'bikes', + 'bicycle', + 'bicycling', + 'famous', + 'celebrity' + ], + modifiable: true), + Emoji( + name: 'man biking: dark skin tone', + char: '\u{1F6B4}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_biking_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'bicycle', + 'biking', + 'cyclist', + 'dark skin tone', + 'man', + 'uc8', + 'sport', + 'diversity', + 'bike', + 'fame', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'bikes', + 'bicycle', + 'bicycling', + 'famous', + 'celebrity' + ], + modifiable: true), + Emoji( + name: 'trophy', + char: '\u{1F3C6}', + shortName: 'trophy', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.awardMedal, + keywords: [ + 'prize', + 'uc6', + 'sport', + 'game', + 'award', + 'football', + 'soccer', + 'perfect', + 'win', + 'harry potter', + 'nerd', + 'fame', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'games', + 'gaming', + 'awards', + 'prize', + 'prizes', + 'trophy', + 'trophies', + 'spot', + 'best', + 'champion', + 'hero', + 'soccer ball', + 'world cup', + 'perfecto', + 'perfection', + 'superb', + 'flawless', + 'excellent', + 'supreme', + 'super', + 'great', + 'winning', + 'killing it', + 'crushing it', + 'victory', + 'victorious', + 'success', + 'successful', + 'winner', + 'smart', + 'geek', + 'serious', + 'famous', + 'celebrity' + ]), + Emoji( + name: '1st place medal', + char: '\u{1F947}', + shortName: 'first_place', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.awardMedal, + keywords: [ + 'first', + 'gold', + 'medal', + 'uc9', + 'sport', + 'award', + 'win', + 'medal', + 'gymnast', + 'fame', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'awards', + 'prize', + 'prizes', + 'trophy', + 'trophies', + 'spot', + 'best', + 'champion', + 'hero', + 'winning', + 'killing it', + 'crushing it', + 'victory', + 'victorious', + 'success', + 'successful', + 'winner', + 'medals', + 'gold medal', + 'silver medal', + 'bronze medal', + 'gymnastics', + 'famous', + 'celebrity' + ]), + Emoji( + name: '2nd place medal', + char: '\u{1F948}', + shortName: 'second_place', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.awardMedal, + keywords: [ + 'medal', + 'second', + 'silver', + 'uc9', + 'sport', + 'award', + 'win', + 'medal', + 'gymnast', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'awards', + 'prize', + 'prizes', + 'trophy', + 'trophies', + 'spot', + 'best', + 'champion', + 'hero', + 'winning', + 'killing it', + 'crushing it', + 'victory', + 'victorious', + 'success', + 'successful', + 'winner', + 'medals', + 'gold medal', + 'silver medal', + 'bronze medal', + 'gymnastics' + ]), + Emoji( + name: '3rd place medal', + char: '\u{1F949}', + shortName: 'third_place', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.awardMedal, + keywords: [ + 'bronze', + 'medal', + 'third', + 'uc9', + 'sport', + 'award', + 'win', + 'medal', + 'gymnast', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'awards', + 'prize', + 'prizes', + 'trophy', + 'trophies', + 'spot', + 'best', + 'champion', + 'hero', + 'winning', + 'killing it', + 'crushing it', + 'victory', + 'victorious', + 'success', + 'successful', + 'winner', + 'medals', + 'gold medal', + 'silver medal', + 'bronze medal', + 'gymnastics' + ]), + Emoji( + name: 'sports medal', + char: '\u{1F3C5}', + shortName: 'medal', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.awardMedal, + keywords: [ + 'medal', + 'uc7', + 'sport', + 'award', + 'perfect', + 'win', + 'medal', + 'gymnast', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'awards', + 'prize', + 'prizes', + 'trophy', + 'trophies', + 'spot', + 'best', + 'champion', + 'hero', + 'perfecto', + 'perfection', + 'superb', + 'flawless', + 'excellent', + 'supreme', + 'super', + 'great', + 'winning', + 'killing it', + 'crushing it', + 'victory', + 'victorious', + 'success', + 'successful', + 'winner', + 'medals', + 'gold medal', + 'silver medal', + 'bronze medal', + 'gymnastics' + ]), + Emoji( + name: 'military medal', + char: '\u{1F396}\u{FE0F}', + shortName: 'military_medal', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.awardMedal, + keywords: [ + 'celebration', + 'medal', + 'military', + 'uc7', + 'award', + 'medal', + 'gymnast', + 'activity', + 'awards', + 'prize', + 'prizes', + 'trophy', + 'trophies', + 'spot', + 'best', + 'champion', + 'hero', + 'medals', + 'gold medal', + 'silver medal', + 'bronze medal', + 'gymnastics' + ]), + Emoji( + name: 'rosette', + char: '\u{1F3F5}\u{FE0F}', + shortName: 'rosette', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.plantFlower, + keywords: ['plant', 'uc7', 'tropical', 'activity']), + Emoji( + name: 'reminder ribbon', + char: '\u{1F397}\u{FE0F}', + shortName: 'reminder_ribbon', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.event, + keywords: [ + 'celebration', + 'reminder', + 'ribbon', + 'uc7', + 'award', + 'hope', + 'activity', + 'important', + 'awards', + 'prize', + 'prizes', + 'trophy', + 'trophies', + 'spot', + 'best', + 'champion', + 'hero', + 'swear', + 'promise' + ]), + Emoji( + name: 'ticket', + char: '\u{1F3AB}', + shortName: 'ticket', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.event, + keywords: [ + 'admission', + 'uc6', + 'theatre', + 'instruments', + 'movie', + 'amusement park', + 'circus', + 'pink', + 'disney', + 'discount', + 'las vegas', + 'activity', + 'opera', + 'theater', + 'craft', + 'drama', + 'monet', + 'music', + 'instrument', + 'singing', + 'concert', + 'jaz', + 'listen', + 'singer', + 'song', + 'musique', + 'movies', + 'cinema', + 'film', + 'films', + 'video', + 'videos', + 'theme park', + 'circus tent', + 'clown', + 'clowns', + 'rose', + 'cartoon', + 'sale', + 'bargain', + 'vegas' + ]), + Emoji( + name: 'admission tickets', + char: '\u{1F39F}\u{FE0F}', + shortName: 'tickets', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.event, + keywords: [ + 'admission', + 'ticket', + 'uc7', + 'theatre', + 'instruments', + 'movie', + 'amusement park', + 'circus', + 'disney', + 'activity', + 'theater', + 'craft', + 'drama', + 'monet', + 'music', + 'instrument', + 'singing', + 'concert', + 'jaz', + 'listen', + 'singer', + 'song', + 'musique', + 'movies', + 'cinema', + 'film', + 'films', + 'video', + 'videos', + 'theme park', + 'circus tent', + 'clown', + 'clowns', + 'cartoon' + ]), + Emoji( + name: 'circus tent', + char: '\u{1F3AA}', + shortName: 'circus_tent', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeOther, + keywords: [ + 'circus', + 'tent', + 'uc6', + 'amusement park', + 'circus', + 'magic', + 'activity', + 'independence day', + 'opera', + 'theme park', + 'circus tent', + 'clown', + 'clowns', + 'spell', + 'genie', + 'magical', + '4th of july' + ]), + Emoji( + name: 'person juggling', + char: '\u{1F939}', + shortName: 'person_juggling', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'balance', + 'juggle', + 'multitask', + 'skill', + 'uc9', + 'diversity', + 'ball', + 'circus', + 'throw', + 'activity', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon', + 'circus tent', + 'clown', + 'clowns' + ]), + Emoji( + name: 'person juggling: light skin tone', + char: '\u{1F939}\u{1F3FB}', + shortName: 'person_juggling_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'balance', + 'juggle', + 'light skin tone', + 'multitask', + 'skill', + 'uc9', + 'diversity', + 'ball', + 'circus', + 'throw', + 'activity', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon', + 'circus tent', + 'clown', + 'clowns' + ], + modifiable: true), + Emoji( + name: 'person juggling: medium-light skin tone', + char: '\u{1F939}\u{1F3FC}', + shortName: 'person_juggling_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'balance', + 'juggle', + 'medium-light skin tone', + 'multitask', + 'skill', + 'uc9', + 'diversity', + 'ball', + 'circus', + 'throw', + 'activity', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon', + 'circus tent', + 'clown', + 'clowns' + ], + modifiable: true), + Emoji( + name: 'person juggling: medium skin tone', + char: '\u{1F939}\u{1F3FD}', + shortName: 'person_juggling_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'balance', + 'juggle', + 'medium skin tone', + 'multitask', + 'skill', + 'uc9', + 'diversity', + 'ball', + 'circus', + 'throw', + 'activity', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon', + 'circus tent', + 'clown', + 'clowns' + ], + modifiable: true), + Emoji( + name: 'person juggling: medium-dark skin tone', + char: '\u{1F939}\u{1F3FE}', + shortName: 'person_juggling_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'balance', + 'juggle', + 'medium-dark skin tone', + 'multitask', + 'skill', + 'uc9', + 'diversity', + 'ball', + 'circus', + 'throw', + 'activity', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon', + 'circus tent', + 'clown', + 'clowns' + ], + modifiable: true), + Emoji( + name: 'person juggling: dark skin tone', + char: '\u{1F939}\u{1F3FF}', + shortName: 'person_juggling_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'balance', + 'dark skin tone', + 'juggle', + 'multitask', + 'skill', + 'uc9', + 'diversity', + 'ball', + 'circus', + 'throw', + 'activity', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon', + 'circus tent', + 'clown', + 'clowns' + ], + modifiable: true), + Emoji( + name: 'woman juggling', + char: '\u{1F939}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_juggling', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'juggling', + 'multitask', + 'woman', + 'uc9', + 'diversity', + 'ball', + 'circus', + 'throw', + 'activity', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon', + 'circus tent', + 'clown', + 'clowns' + ]), + Emoji( + name: 'woman juggling: light skin tone', + char: '\u{1F939}\u{1F3FB}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_juggling_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'juggling', + 'light skin tone', + 'multitask', + 'woman', + 'uc9', + 'diversity', + 'ball', + 'circus', + 'throw', + 'activity', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon', + 'circus tent', + 'clown', + 'clowns' + ], + modifiable: true), + Emoji( + name: 'woman juggling: medium-light skin tone', + char: '\u{1F939}\u{1F3FC}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_juggling_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'juggling', + 'medium-light skin tone', + 'multitask', + 'woman', + 'uc9', + 'diversity', + 'ball', + 'circus', + 'throw', + 'activity', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon', + 'circus tent', + 'clown', + 'clowns' + ], + modifiable: true), + Emoji( + name: 'woman juggling: medium skin tone', + char: '\u{1F939}\u{1F3FD}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_juggling_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'juggling', + 'medium skin tone', + 'multitask', + 'woman', + 'uc9', + 'diversity', + 'ball', + 'circus', + 'throw', + 'activity', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon', + 'circus tent', + 'clown', + 'clowns' + ], + modifiable: true), + Emoji( + name: 'woman juggling: medium-dark skin tone', + char: '\u{1F939}\u{1F3FE}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_juggling_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'juggling', + 'medium-dark skin tone', + 'multitask', + 'woman', + 'uc9', + 'diversity', + 'ball', + 'circus', + 'throw', + 'activity', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon', + 'circus tent', + 'clown', + 'clowns' + ], + modifiable: true), + Emoji( + name: 'woman juggling: dark skin tone', + char: '\u{1F939}\u{1F3FF}\u{200D}\u{2640}\u{FE0F}', + shortName: 'woman_juggling_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'dark skin tone', + 'juggling', + 'multitask', + 'woman', + 'uc9', + 'diversity', + 'ball', + 'circus', + 'throw', + 'activity', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon', + 'circus tent', + 'clown', + 'clowns' + ], + modifiable: true), + Emoji( + name: 'man juggling', + char: '\u{1F939}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_juggling', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'juggling', + 'man', + 'multitask', + 'uc9', + 'diversity', + 'ball', + 'circus', + 'throw', + 'activity', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon', + 'circus tent', + 'clown', + 'clowns' + ]), + Emoji( + name: 'man juggling: light skin tone', + char: '\u{1F939}\u{1F3FB}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_juggling_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'juggling', + 'light skin tone', + 'man', + 'multitask', + 'uc9', + 'diversity', + 'ball', + 'circus', + 'throw', + 'activity', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon', + 'circus tent', + 'clown', + 'clowns' + ], + modifiable: true), + Emoji( + name: 'man juggling: medium-light skin tone', + char: '\u{1F939}\u{1F3FC}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_juggling_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'juggling', + 'man', + 'medium-light skin tone', + 'multitask', + 'uc9', + 'diversity', + 'ball', + 'circus', + 'throw', + 'activity', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon', + 'circus tent', + 'clown', + 'clowns' + ], + modifiable: true), + Emoji( + name: 'man juggling: medium skin tone', + char: '\u{1F939}\u{1F3FD}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_juggling_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'juggling', + 'man', + 'medium skin tone', + 'multitask', + 'uc9', + 'diversity', + 'ball', + 'circus', + 'throw', + 'activity', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon', + 'circus tent', + 'clown', + 'clowns' + ], + modifiable: true), + Emoji( + name: 'man juggling: medium-dark skin tone', + char: '\u{1F939}\u{1F3FE}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_juggling_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'juggling', + 'man', + 'medium-dark skin tone', + 'multitask', + 'uc9', + 'diversity', + 'ball', + 'circus', + 'throw', + 'activity', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon', + 'circus tent', + 'clown', + 'clowns' + ], + modifiable: true), + Emoji( + name: 'man juggling: dark skin tone', + char: '\u{1F939}\u{1F3FF}\u{200D}\u{2642}\u{FE0F}', + shortName: 'man_juggling_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personSport, + keywords: [ + 'dark skin tone', + 'juggling', + 'man', + 'multitask', + 'uc9', + 'diversity', + 'ball', + 'circus', + 'throw', + 'activity', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'balls', + 'ballon', + 'circus tent', + 'clown', + 'clowns' + ], + modifiable: true), + Emoji( + name: 'performing arts', + char: '\u{1F3AD}', + shortName: 'performing_arts', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.artsCrafts, + keywords: [ + 'art', + 'mask', + 'performing', + 'theater', + 'theatre', + 'uc6', + 'theatre', + 'halloween', + 'movie', + 'circus', + 'girls night', + 'play', + 'fame', + 'las vegas', + 'fun', + 'activity', + 'mask', + 'fantasy', + 'opera', + 'theater', + 'craft', + 'drama', + 'monet', + 'samhain', + 'movies', + 'cinema', + 'film', + 'films', + 'video', + 'videos', + 'circus tent', + 'clown', + 'clowns', + 'ladies night', + 'girls only', + 'girlfriend', + 'famous', + 'celebrity', + 'vegas' + ]), + Emoji( + name: 'ballet shoes', + char: '\u{1FA70}', + shortName: 'ballet_shoes', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.clothing, + keywords: [ + 'uc12', + 'shoe', + 'dance', + 'vintage', + 'activity', + 'opera', + 'shoes', + 'baskets', + 'loafers', + 'sandals', + 'pumps', + 'boots', + 'heels', + 'dancers', + 'dancing', + 'ballet', + 'ballerina', + 'dabbing', + 'salsa' + ]), + Emoji( + name: 'artist palette', + char: '\u{1F3A8}', + shortName: 'art', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.artsCrafts, + keywords: [ + 'art', + 'museum', + 'painting', + 'palette', + 'uc6', + 'theatre', + 'painting', + 'color', + 'instagram', + 'fun', + 'activity', + 'theater', + 'craft', + 'drama', + 'monet', + 'painter', + 'arts', + 'colour', + 'coloring', + 'colouring', + 'drawing', + 'marker', + 'sketch' + ]), + Emoji( + name: 'clapper board', + char: '\u{1F3AC}', + shortName: 'clapper', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.lightVideo, + keywords: [ + 'clapper', + 'movie', + 'uc6', + 'movie', + 'disney', + 'california', + 'fame', + 'activity', + 'movies', + 'cinema', + 'film', + 'films', + 'video', + 'videos', + 'cartoon', + 'famous', + 'celebrity' + ]), + Emoji( + name: 'microphone', + char: '\u{1F3A4}', + shortName: 'microphone', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.music, + keywords: [ + 'karaoke', + 'mic', + 'uc6', + 'instruments', + 'rock and roll', + 'disco', + 'fame', + 'activity', + 'music', + 'instrument', + 'singing', + 'concert', + 'jaz', + 'listen', + 'singer', + 'song', + 'musique', + 'famous', + 'celebrity' + ]), + Emoji( + name: 'headphone', + char: '\u{1F3A7}', + shortName: 'headphones', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.music, + keywords: [ + 'earbud', + 'uc6', + 'instruments', + 'headphones', + 'rock and roll', + 'earphone', + 'activity', + 'music', + 'instrument', + 'singing', + 'concert', + 'jaz', + 'listen', + 'singer', + 'song', + 'musique', + 'headphone', + 'head phones', + 'casque', + 'earbud' + ]), + Emoji( + name: 'musical score', + char: '\u{1F3BC}', + shortName: 'musical_score', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.music, + keywords: [ + 'music', + 'score', + 'uc6', + 'instruments', + 'piano', + 'rock and roll', + 'disco', + 'activity', + 'music', + 'instrument', + 'singing', + 'concert', + 'jaz', + 'listen', + 'singer', + 'song', + 'musique' + ]), + Emoji( + name: 'musical keyboard', + char: '\u{1F3B9}', + shortName: 'musical_keyboard', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.musicalInstrument, + keywords: [ + 'instrument', + 'keyboard', + 'music', + 'piano', + 'uc6', + 'instruments', + 'play', + 'keyboard', + 'piano', + 'rock and roll', + 'activity', + 'music', + 'instrument', + 'singing', + 'concert', + 'jaz', + 'listen', + 'singer', + 'song', + 'musique', + 'keyboards' + ]), + Emoji( + name: 'drum', + char: '\u{1F941}', + shortName: 'drum', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.musicalInstrument, + keywords: [ + 'drum', + 'drumsticks', + 'music', + 'uc9', + 'instruments', + 'play', + 'rock and roll', + 'activity', + 'toy', + 'music', + 'instrument', + 'singing', + 'concert', + 'jaz', + 'listen', + 'singer', + 'song', + 'musique' + ]), + Emoji( + name: 'long drum', + char: '\u{1FA98}', + shortName: 'long_drum', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.musicalInstrument, + keywords: [ + 'uc13', + 'instruments', + 'play', + 'activity', + 'music', + 'instrument', + 'singing', + 'concert', + 'jaz', + 'listen', + 'singer', + 'song', + 'musique' + ]), + Emoji( + name: 'saxophone', + char: '\u{1F3B7}', + shortName: 'saxophone', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.musicalInstrument, + keywords: [ + 'instrument', + 'music', + 'sax', + 'uc6', + 'instruments', + 'play', + 'activity', + 'music', + 'instrument', + 'singing', + 'concert', + 'jaz', + 'listen', + 'singer', + 'song', + 'musique' + ]), + Emoji( + name: 'trumpet', + char: '\u{1F3BA}', + shortName: 'trumpet', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.musicalInstrument, + keywords: [ + 'instrument', + 'music', + 'uc6', + 'instruments', + 'play', + 'activity', + 'independence day', + 'music', + 'instrument', + 'singing', + 'concert', + 'jaz', + 'listen', + 'singer', + 'song', + 'musique', + '4th of july' + ]), + Emoji( + name: 'guitar', + char: '\u{1F3B8}', + shortName: 'guitar', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.musicalInstrument, + keywords: [ + 'instrument', + 'music', + 'uc6', + 'instruments', + 'mexican', + 'play', + 'rock and roll', + 'activity', + 'guitarra', + 'stringed', + 'music', + 'instrument', + 'singing', + 'concert', + 'jaz', + 'listen', + 'singer', + 'song', + 'musique', + 'mexico', + 'cinco de mayo', + 'español', + 'guitare', + 'gitarre', + 'chitarra', + 'bangio' + ]), + Emoji( + name: 'banjo', + char: '\u{1FA95}', + shortName: 'banjo', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.musicalInstrument, + keywords: [ + 'uc12', + 'instruments', + 'play', + 'activity', + 'toy', + 'guitarra', + 'stringed', + 'music', + 'instrument', + 'singing', + 'concert', + 'jaz', + 'listen', + 'singer', + 'song', + 'musique', + 'guitare', + 'gitarre', + 'chitarra', + 'bangio' + ]), + Emoji( + name: 'violin', + char: '\u{1F3BB}', + shortName: 'violin', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.musicalInstrument, + keywords: [ + 'instrument', + 'music', + 'uc6', + 'instruments', + 'sarcastic', + 'play', + 'activity', + 'stringed', + 'music', + 'instrument', + 'singing', + 'concert', + 'jaz', + 'listen', + 'singer', + 'song', + 'musique', + 'sarcasm' + ]), + Emoji( + name: 'accordion', + char: '\u{1FA97}', + shortName: 'accordion', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.musicalInstrument, + keywords: [ + 'uc13', + 'instruments', + 'play', + 'keyboard', + 'activity', + 'squeezebox', + 'music', + 'instrument', + 'singing', + 'concert', + 'jaz', + 'listen', + 'singer', + 'song', + 'musique', + 'keyboards', + 'akkordeon', + 'bellows', + 'aerophone' + ]), + Emoji( + name: 'game die', + char: '\u{1F3B2}', + shortName: 'game_die', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.game, + keywords: [ + 'dice', + 'die', + 'game', + 'uc6', + 'game', + 'boys night', + 'play', + 'bingo', + 'las vegas', + 'dice', + 'fun', + 'activity', + 'toy', + 'games', + 'gaming', + 'guys night', + 'vegas' + ]), + Emoji( + name: 'chess pawn', + char: '\u{265F}\u{FE0F}', + shortName: 'chess_pawn', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.game, + keywords: ['uc1', 'game', 'play', 'fun', 'activity', 'games', 'gaming']), + Emoji( + name: 'direct hit', + char: '\u{1F3AF}', + shortName: 'dart', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.game, + keywords: [ + 'bull', + 'bullseye', + 'dart', + 'eye', + 'game', + 'hit', + 'target', + 'uc6', + 'sport', + 'game', + 'boys night', + 'play', + 'target', + 'fun', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'games', + 'gaming', + 'guys night' + ]), + Emoji( + name: 'bowling', + char: '\u{1F3B3}', + shortName: 'bowling', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.sport, + keywords: [ + 'ball', + 'game', + 'uc6', + 'sport', + 'game', + 'ball', + 'boys night', + 'play', + 'fun', + 'throw', + 'activity', + 'sports', + 'exercise', + 'athlete', + 'athletes', + 'athletic', + 'team', + 'fitness', + 'work out', + 'workout', + 'games', + 'gaming', + 'balls', + 'ballon', + 'guys night' + ]), + Emoji( + name: 'video game', + char: '\u{1F3AE}', + shortName: 'video_game', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.game, + keywords: [ + 'controller', + 'game', + 'uc6', + 'electronics', + 'game', + 'boys night', + 'play', + 'controller', + 'fun', + 'activity', + 'games', + 'gaming', + 'guys night', + 'remote' + ]), + Emoji( + name: 'slot machine', + char: '\u{1F3B0}', + shortName: 'slot_machine', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.game, + keywords: [ + 'game', + 'slot', + 'uc6', + 'game', + 'boys night', + 'play', + 'bingo', + 'las vegas', + 'fun', + 'activity', + 'games', + 'gaming', + 'guys night', + 'vegas' + ]), + Emoji( + name: 'puzzle piece', + char: '\u{1F9E9}', + shortName: 'jigsaw', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.game, + keywords: [ + 'uc11', + 'game', + 'play', + 'fun', + 'puzzle', + 'activity', + 'household', + 'toy', + 'question', + 'games', + 'gaming', + 'quiz', + 'puzzled' + ]), + Emoji( + name: 'automobile', + char: '\u{1F697}', + shortName: 'red_car', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportGround, + keywords: [ + 'car', + 'uc6', + 'transportation', + 'car', + 'travel', + 'toy', + 'cars', + 'vehicle', + 'fast car', + 'drive', + 'driving', + 'auto' + ]), + Emoji( + name: 'taxi', + char: '\u{1F695}', + shortName: 'taxi', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportGround, + keywords: [ + 'vehicle', + 'uc6', + 'transportation', + 'car', + 'travel', + 'cars', + 'vehicle', + 'fast car', + 'drive', + 'driving', + 'auto' + ]), + Emoji( + name: 'sport utility vehicle', + char: '\u{1F699}', + shortName: 'blue_car', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportGround, + keywords: [ + 'recreational', + 'sport utility', + 'uc6', + 'transportation', + 'car', + 'travel', + 'cars', + 'vehicle', + 'fast car', + 'drive', + 'driving', + 'auto' + ]), + Emoji( + name: 'pickup truck', + char: '\u{1F6FB}', + shortName: 'pickup_truck', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportGround, + keywords: [ + 'uc13', + 'transportation', + 'car', + 'truck', + 'travel', + 'cars', + 'vehicle', + 'fast car', + 'drive', + 'driving', + 'auto', + 'trucks' + ]), + Emoji( + name: 'bus', + char: '\u{1F68C}', + shortName: 'bus', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportGround, + keywords: [ + 'vehicle', + 'uc6', + 'transportation', + 'bus', + 'classroom', + 'travel', + 'vacation', + 'buses', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning' + ]), + Emoji( + name: 'trolleybus', + char: '\u{1F68E}', + shortName: 'trolleybus', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportGround, + keywords: [ + 'bus', + 'tram', + 'trolley', + 'uc6', + 'transportation', + 'bus', + 'travel', + 'buses' + ]), + Emoji( + name: 'racing car', + char: '\u{1F3CE}\u{FE0F}', + shortName: 'race_car', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportGround, + keywords: [ + 'car', + 'racing', + 'uc7', + 'transportation', + 'car', + 'disney', + 'fun', + 'rich', + 'cars', + 'vehicle', + 'fast car', + 'drive', + 'driving', + 'auto', + 'cartoon', + 'grand', + 'expensive', + 'fancy' + ]), + Emoji( + name: 'police car', + char: '\u{1F693}', + shortName: 'police_car', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportGround, + keywords: [ + 'car', + 'patrol', + 'police', + 'uc6', + 'transportation', + 'car', + 'police', + '911', + 'sirens', + 'help', + 'cars', + 'vehicle', + 'fast car', + 'drive', + 'driving', + 'auto', + 'cop', + 'policeman', + 'popo', + 'prison', + 'handcuff', + 'jail', + 'justice', + 'emergency', + 'injury', + 'switch' + ]), + Emoji( + name: 'ambulance', + char: '\u{1F691}', + shortName: 'ambulance', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportGround, + keywords: [ + 'vehicle', + 'uc6', + 'transportation', + '911', + 'sirens', + 'help', + 'poison', + 'covid', + 'emergency', + 'injury', + 'switch', + 'toxic', + 'toxins' + ]), + Emoji( + name: 'fire engine', + char: '\u{1F692}', + shortName: 'fire_engine', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportGround, + keywords: [ + 'engine', + 'fire', + 'truck', + 'uc6', + 'transportation', + 'truck', + '911', + 'sirens', + 'help', + 'fires', + 'trucks', + 'emergency', + 'injury', + 'switch' + ]), + Emoji( + name: 'minibus', + char: '\u{1F690}', + shortName: 'minibus', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportGround, + keywords: [ + 'bus', + 'uc6', + 'transportation', + 'bus', + 'travel', + 'camp', + 'vacation', + 'buses', + 'camping', + 'tent', + 'camper', + 'outdoor', + 'outside' + ]), + Emoji( + name: 'delivery truck', + char: '\u{1F69A}', + shortName: 'truck', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportGround, + keywords: [ + 'delivery', + 'truck', + 'uc6', + 'transportation', + 'truck', + 'moving', + 'trucks' + ]), + Emoji( + name: 'articulated lorry', + char: '\u{1F69B}', + shortName: 'articulated_lorry', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportGround, + keywords: [ + 'lorry', + 'semi', + 'truck', + 'uc6', + 'transportation', + 'truck', + 'moving', + 'trucks' + ]), + Emoji( + name: 'tractor', + char: '\u{1F69C}', + shortName: 'tractor', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportGround, + keywords: ['vehicle', 'uc6', 'transportation', 'farm']), + Emoji( + name: 'white cane', + char: '\u{1F9AF}', + shortName: 'probing_cane', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.tool, + keywords: [ + 'uc12', + 'transportation', + 'cane', + 'handicap', + 'navigate', + 'blind', + 'probe', + 'accessibility', + 'disabled', + 'disability', + 'white cane' + ]), + Emoji( + name: 'manual wheelchair', + char: '\u{1F9BD}', + shortName: 'manual_wheelchair', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportGround, + keywords: [ + 'uc12', + 'transportation', + 'handicap', + 'accessibility', + 'disabled', + 'disability' + ]), + Emoji( + name: 'motorized wheelchair', + char: '\u{1F9BC}', + shortName: 'motorized_wheelchair', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportGround, + keywords: [ + 'uc12', + 'transportation', + 'handicap', + 'accessibility', + 'disabled', + 'disability' + ]), + Emoji( + name: 'kick scooter', + char: '\u{1F6F4}', + shortName: 'scooter', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportGround, + keywords: ['kick', 'scooter', 'uc9', 'transportation']), + Emoji( + name: 'bicycle', + char: '\u{1F6B2}', + shortName: 'bike', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportGround, + keywords: [ + 'bike', + 'uc6', + 'transportation', + 'bike', + 'travel', + 'bikes', + 'bicycle', + 'bicycling' + ]), + Emoji( + name: 'motor scooter', + char: '\u{1F6F5}', + shortName: 'motor_scooter', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportGround, + keywords: [ + 'motor', + 'scooter', + 'uc9', + 'transportation', + 'travel', + 'thai', + 'pattaya' + ]), + Emoji( + name: 'motorcycle', + char: '\u{1F3CD}\u{FE0F}', + shortName: 'motorcycle', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportGround, + keywords: [ + 'racing', + 'uc7', + 'transportation', + 'bike', + 'travel', + 'super hero', + 'fun', + 'bikes', + 'bicycle', + 'bicycling', + 'superhero', + 'superman', + 'batman' + ]), + Emoji( + name: 'auto rickshaw', + char: '\u{1F6FA}', + shortName: 'auto_rickshaw', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportGround, + keywords: [ + 'uc12', + 'transportation', + 'car', + 'travel', + 'vacation', + 'thai', + 'chinese', + 'cart', + 'tuk tuk', + 'cars', + 'vehicle', + 'fast car', + 'drive', + 'driving', + 'auto', + 'pattaya', + 'chinois', + 'asian', + 'chine', + 'pedicab', + 'trishaw', + 'jinrikisha' + ]), + Emoji( + name: 'police car light', + char: '\u{1F6A8}', + shortName: 'rotating_light', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportGround, + keywords: [ + 'beacon', + 'car', + 'light', + 'police', + 'revolving', + 'uc6', + 'transportation', + 'police', + '911', + 'sirens', + 'help', + 'cop', + 'policeman', + 'popo', + 'prison', + 'handcuff', + 'jail', + 'justice', + 'emergency', + 'injury', + 'switch' + ]), + Emoji( + name: 'oncoming police car', + char: '\u{1F694}', + shortName: 'oncoming_police_car', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportGround, + keywords: [ + 'car', + 'oncoming', + 'police', + 'uc6', + 'transportation', + 'car', + 'police', + '911', + 'sirens', + 'help', + 'cars', + 'vehicle', + 'fast car', + 'drive', + 'driving', + 'auto', + 'cop', + 'policeman', + 'popo', + 'prison', + 'handcuff', + 'jail', + 'justice', + 'emergency', + 'injury', + 'switch' + ]), + Emoji( + name: 'oncoming bus', + char: '\u{1F68D}', + shortName: 'oncoming_bus', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportGround, + keywords: [ + 'bus', + 'oncoming', + 'uc6', + 'transportation', + 'bus', + 'travel', + 'buses' + ]), + Emoji( + name: 'oncoming automobile', + char: '\u{1F698}', + shortName: 'oncoming_automobile', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportGround, + keywords: [ + 'automobile', + 'car', + 'oncoming', + 'uc6', + 'transportation', + 'car', + 'travel', + 'cars', + 'vehicle', + 'fast car', + 'drive', + 'driving', + 'auto' + ]), + Emoji( + name: 'oncoming taxi', + char: '\u{1F696}', + shortName: 'oncoming_taxi', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportGround, + keywords: [ + 'oncoming', + 'taxi', + 'uc6', + 'transportation', + 'car', + 'travel', + 'cars', + 'vehicle', + 'fast car', + 'drive', + 'driving', + 'auto' + ]), + Emoji( + name: 'aerial tramway', + char: '\u{1F6A1}', + shortName: 'aerial_tramway', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportAir, + keywords: [ + 'aerial', + 'cable', + 'car', + 'gondola', + 'tramway', + 'uc6', + 'transportation', + 'train', + 'travel', + 'disney', + 'trains', + 'cartoon' + ]), + Emoji( + name: 'mountain cableway', + char: '\u{1F6A0}', + shortName: 'mountain_cableway', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportAir, + keywords: [ + 'cable', + 'gondola', + 'mountain', + 'uc6', + 'transportation', + 'train', + 'travel', + 'skiing', + 'snowboarding', + 'trains', + 'ski', + 'snow skiing', + 'ski boot', + 'snowboarder' + ]), + Emoji( + name: 'suspension railway', + char: '\u{1F69F}', + shortName: 'suspension_railway', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportAir, + keywords: [ + 'railway', + 'suspension', + 'uc6', + 'transportation', + 'train', + 'travel', + 'trains' + ]), + Emoji( + name: 'railway car', + char: '\u{1F683}', + shortName: 'railway_car', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportGround, + keywords: [ + 'car', + 'electric', + 'railway', + 'train', + 'tram', + 'trolleybus', + 'uc6', + 'transportation', + 'train', + 'travel', + 'trains' + ]), + Emoji( + name: 'tram car', + char: '\u{1F68B}', + shortName: 'train', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportGround, + keywords: [ + 'car', + 'tram', + 'trolleybus', + 'uc6', + 'transportation', + 'train', + 'travel', + 'trains' + ]), + Emoji( + name: 'mountain railway', + char: '\u{1F69E}', + shortName: 'mountain_railway', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportGround, + keywords: [ + 'car', + 'mountain', + 'railway', + 'uc6', + 'transportation', + 'train', + 'travel', + 'vacation', + 'mountain', + 'trains' + ]), + Emoji( + name: 'monorail', + char: '\u{1F69D}', + shortName: 'monorail', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportGround, + keywords: [ + 'vehicle', + 'uc6', + 'transportation', + 'train', + 'travel', + 'vacation', + 'disney', + 'trains', + 'cartoon' + ]), + Emoji( + name: 'high-speed train', + char: '\u{1F684}', + shortName: 'bullettrain_side', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportGround, + keywords: [ + 'railway', + 'shinkansen', + 'speed', + 'train', + 'uc6', + 'transportation', + 'train', + 'travel', + 'vacation', + 'trains' + ]), + Emoji( + name: 'bullet train', + char: '\u{1F685}', + shortName: 'bullettrain_front', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportGround, + keywords: [ + 'bullet', + 'railway', + 'shinkansen', + 'speed', + 'train', + 'uc6', + 'transportation', + 'train', + 'travel', + 'vacation', + 'trains' + ]), + Emoji( + name: 'light rail', + char: '\u{1F688}', + shortName: 'light_rail', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportGround, + keywords: [ + 'railway', + 'uc6', + 'transportation', + 'train', + 'travel', + 'trains' + ]), + Emoji( + name: 'locomotive', + char: '\u{1F682}', + shortName: 'steam_locomotive', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportGround, + keywords: [ + 'engine', + 'railway', + 'steam', + 'train', + 'uc6', + 'transportation', + 'train', + 'travel', + 'steam', + 'disney', + 'trains', + 'steaming', + 'piping', + 'cartoon' + ]), + Emoji( + name: 'train', + char: '\u{1F686}', + shortName: 'train2', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportGround, + keywords: [ + 'railway', + 'uc6', + 'transportation', + 'train', + 'travel', + 'trains' + ]), + Emoji( + name: 'metro', + char: '\u{1F687}', + shortName: 'metro', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportGround, + keywords: [ + 'subway', + 'uc6', + 'transportation', + 'train', + 'travel', + 'trains' + ]), + Emoji( + name: 'tram', + char: '\u{1F68A}', + shortName: 'tram', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportGround, + keywords: [ + 'trolleybus', + 'uc6', + 'transportation', + 'train', + 'travel', + 'trains' + ]), + Emoji( + name: 'station', + char: '\u{1F689}', + shortName: 'station', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportGround, + keywords: [ + 'railway', + 'train', + 'uc6', + 'transportation', + 'train', + 'travel', + 'vacation', + 'trains' + ]), + Emoji( + name: 'airplane', + char: '\u{2708}\u{FE0F}', + shortName: 'airplane', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportAir, + keywords: [ + 'aeroplane', + 'airplane', + 'uc1', + 'transportation', + 'plane', + 'fly', + 'travel', + 'vacation', + 'airplane', + 'planes', + 'flight', + 'flying', + 'flights', + 'avion', + 'airline', + 'aircraft', + 'airforce', + 'air force', + 'airport' + ]), + Emoji( + name: 'airplane departure', + char: '\u{1F6EB}', + shortName: 'airplane_departure', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportAir, + keywords: [ + 'aeroplane', + 'airplane', + 'check-in', + 'departure', + 'departures', + 'uc7', + 'transportation', + 'plane', + 'fly', + 'travel', + 'vacation', + 'airplane', + 'planes', + 'flight', + 'flying', + 'flights', + 'avion', + 'airline', + 'aircraft', + 'airforce', + 'air force', + 'airport' + ]), + Emoji( + name: 'airplane arrival', + char: '\u{1F6EC}', + shortName: 'airplane_arriving', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportAir, + keywords: [ + 'aeroplane', + 'airplane', + 'arrivals', + 'arriving', + 'landing', + 'uc7', + 'transportation', + 'plane', + 'fly', + 'travel', + 'vacation', + 'airplane', + 'planes', + 'flight', + 'flying', + 'flights', + 'avion', + 'airline', + 'aircraft', + 'airforce', + 'air force', + 'airport' + ]), + Emoji( + name: 'small airplane', + char: '\u{1F6E9}\u{FE0F}', + shortName: 'airplane_small', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportAir, + keywords: [ + 'aeroplane', + 'airplane', + 'uc7', + 'transportation', + 'plane', + 'fly', + 'travel', + 'vacation', + 'airplane', + 'rich', + 'planes', + 'flight', + 'flying', + 'flights', + 'avion', + 'airline', + 'aircraft', + 'airforce', + 'air force', + 'airport', + 'grand', + 'expensive', + 'fancy' + ]), + Emoji( + name: 'seat', + char: '\u{1F4BA}', + shortName: 'seat', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportAir, + keywords: [ + 'chair', + 'uc6', + 'transportation', + 'fly', + 'travel', + 'vacation', + 'seat', + 'flight', + 'flying', + 'flights', + 'avion', + 'bench', + 'sedia', + 'Stuhl', + 'chaise', + 'silla', + 'armchair' + ]), + Emoji( + name: 'satellite', + char: '\u{1F6F0}\u{FE0F}', + shortName: 'satellite_orbital', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportAir, + keywords: [ + 'space', + 'uc7', + 'space', + 'drone', + 'outer space', + 'galaxy', + 'universe', + 'nasa', + 'spaceship' + ]), + Emoji( + name: 'rocket', + char: '\u{1F680}', + shortName: 'rocket', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportAir, + keywords: [ + 'space', + 'uc6', + 'transportation', + 'fly', + 'space', + 'travel', + 'blast', + 'star wars', + 'flight', + 'flying', + 'flights', + 'avion', + 'outer space', + 'galaxy', + 'universe', + 'nasa', + 'spaceship', + 'boom' + ]), + Emoji( + name: 'flying saucer', + char: '\u{1F6F8}', + shortName: 'flying_saucer', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportAir, + keywords: [ + 'UFO', + 'uc10', + 'transportation', + 'space', + 'travel', + 'alien', + 'outer space', + 'galaxy', + 'universe', + 'nasa', + 'spaceship', + 'ufo' + ]), + Emoji( + name: 'helicopter', + char: '\u{1F681}', + shortName: 'helicopter', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportAir, + keywords: [ + 'vehicle', + 'uc6', + 'transportation', + 'plane', + 'fly', + 'travel', + 'vacation', + 'rich', + 'planes', + 'flight', + 'flying', + 'flights', + 'avion', + 'grand', + 'expensive', + 'fancy' + ]), + Emoji( + name: 'canoe', + char: '\u{1F6F6}', + shortName: 'canoe', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportWater, + keywords: [ + 'boat', + 'canoe', + 'uc9', + 'transportation', + 'travel', + 'rowing', + 'rowboat', + 'canoe' + ]), + Emoji( + name: 'sailboat', + char: '\u{26F5}', + shortName: 'sailboat', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportWater, + keywords: [ + 'boat', + 'resort', + 'sea', + 'yacht', + 'uc5', + 'transportation', + 'travel', + 'boat', + 'vacation', + 'pirate', + 'rich', + 'ocean', + 'boats', + 'boating', + 'grand', + 'expensive', + 'fancy', + 'sea' + ]), + Emoji( + name: 'speedboat', + char: '\u{1F6A4}', + shortName: 'speedboat', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportWater, + keywords: [ + 'boat', + 'uc6', + 'transportation', + 'travel', + 'boat', + 'tropical', + 'vacation', + 'florida', + 'scuba', + 'boats', + 'boating', + 'snorkel' + ]), + Emoji( + name: 'motor boat', + char: '\u{1F6E5}\u{FE0F}', + shortName: 'motorboat', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportWater, + keywords: [ + 'boat', + 'motorboat', + 'uc7', + 'transportation', + 'travel', + 'boat', + 'scuba', + 'rich', + 'boats', + 'boating', + 'snorkel', + 'grand', + 'expensive', + 'fancy' + ]), + Emoji( + name: 'passenger ship', + char: '\u{1F6F3}\u{FE0F}', + shortName: 'cruise_ship', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportWater, + keywords: [ + 'passenger', + 'ship', + 'uc7', + 'transportation', + 'travel', + 'boat', + 'vacation', + 'disney', + 'florida', + 'fun', + 'ocean', + 'boats', + 'boating', + 'cartoon', + 'sea' + ]), + Emoji( + name: 'ferry', + char: '\u{26F4}\u{FE0F}', + shortName: 'ferry', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportWater, + keywords: [ + 'boat', + 'passenger', + 'uc5', + 'transportation', + 'travel', + 'boat', + 'vacation', + 'ocean', + 'boats', + 'boating', + 'sea' + ]), + Emoji( + name: 'ship', + char: '\u{1F6A2}', + shortName: 'ship', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportWater, + keywords: [ + 'boat', + 'passenger', + 'uc6', + 'transportation', + 'travel', + 'boat', + 'smoking', + 'vacation', + 'moving', + 'ocean', + 'boats', + 'boating', + 'smoke', + 'cigarette', + 'puff', + 'sea' + ]), + Emoji( + name: 'anchor', + char: '\u{2693}', + shortName: 'anchor', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportWater, + keywords: [ + 'ship', + 'tool', + 'uc4', + 'boat', + 'vacation', + 'pirate', + 'boats', + 'boating' + ]), + Emoji( + name: 'fuel pump', + char: '\u{26FD}', + shortName: 'fuelpump', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportGround, + keywords: [ + 'fuel', + 'fuelpump', + 'gas', + 'pump', + 'station', + 'uc5', + 'travel', + 'gas pump', + 'petrol' + ]), + Emoji( + name: 'construction', + char: '\u{1F6A7}', + shortName: 'construction', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportGround, + keywords: ['barrier', 'uc6', 'construction']), + Emoji( + name: 'vertical traffic light', + char: '\u{1F6A6}', + shortName: 'vertical_traffic_light', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportGround, + keywords: ['light', 'signal', 'traffic', 'uc6', 'stop light']), + Emoji( + name: 'horizontal traffic light', + char: '\u{1F6A5}', + shortName: 'traffic_light', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportGround, + keywords: ['light', 'signal', 'traffic', 'uc6', 'stop light']), + Emoji( + name: 'bus stop', + char: '\u{1F68F}', + shortName: 'busstop', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportGround, + keywords: ['bus', 'busstop', 'stop', 'uc6']), + Emoji( + name: 'world map', + char: '\u{1F5FA}\u{FE0F}', + shortName: 'map', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeMap, + keywords: [ + 'map', + 'world', + 'uc7', + 'places', + 'travel', + 'map', + 'vacation', + 'pirate', + 'history', + 'minecraft', + 'navigate', + 'direction', + 'world', + 'maps', + 'location', + 'locate', + 'local', + 'lost', + 'ancient', + 'old' + ]), + Emoji( + name: 'moai', + char: '\u{1F5FF}', + shortName: 'moyai', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.otherObject, + keywords: [ + 'face', + 'moyai', + 'statue', + 'uc6', + 'places', + 'travel', + 'japan', + 'vacation', + 'memorial', + 'japanese', + 'ninja' + ]), + Emoji( + name: 'Statue of Liberty', + char: '\u{1F5FD}', + shortName: 'statue_of_liberty', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeBuilding, + keywords: [ + 'liberty', + 'statue', + 'uc6', + 'places', + 'america', + 'travel', + 'vacation', + 'statue of liberty', + 'free speech', + 'new york', + 'independence day', + 'memorial', + 'usa', + 'united states', + 'united states of america', + 'american', + 'statueofliberty', + 'freedom of speech', + '4th of july' + ]), + Emoji( + name: 'Tokyo tower', + char: '\u{1F5FC}', + shortName: 'tokyo_tower', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeBuilding, + keywords: [ + 'Tokyo', + 'tower', + 'uc6', + 'building', + 'places', + 'travel', + 'japan', + 'vacation', + 'memorial', + 'buildings', + 'japanese', + 'ninja' + ]), + Emoji( + name: 'castle', + char: '\u{1F3F0}', + shortName: 'european_castle', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeBuilding, + keywords: [ + 'European', + 'uc6', + 'building', + 'places', + 'travel', + 'vacation', + 'paris', + 'history', + 'irish', + 'scotland', + 'viking', + 'minecraft', + 'buildings', + 'french', + 'france', + 'ancient', + 'old', + 'saint patricks day', + 'st patricks day', + 'leprechaun', + 'scottish', + 'knight' + ]), + Emoji( + name: 'Japanese castle', + char: '\u{1F3EF}', + shortName: 'japanese_castle', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeBuilding, + keywords: [ + 'Japanese', + 'castle', + 'uc6', + 'building', + 'places', + 'travel', + 'japan', + 'vacation', + 'buildings', + 'japanese', + 'ninja' + ]), + Emoji( + name: 'stadium', + char: '\u{1F3DF}\u{FE0F}', + shortName: 'stadium', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeBuilding, + keywords: [ + 'stadium', + 'uc7', + 'building', + 'instruments', + 'places', + 'travel', + 'game', + 'vacation', + 'boys night', + 'buildings', + 'music', + 'instrument', + 'singing', + 'concert', + 'jaz', + 'listen', + 'singer', + 'song', + 'musique', + 'games', + 'gaming', + 'guys night' + ]), + Emoji( + name: 'ferris wheel', + char: '\u{1F3A1}', + shortName: 'ferris_wheel', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeOther, + keywords: [ + 'amusement park', + 'ferris', + 'wheel', + 'uc6', + 'places', + 'travel', + 'vacation', + 'amusement park', + 'circus', + 'ferris wheel', + 'england', + 'disney', + 'fun', + 'summer', + 'independence day', + 'theme park', + 'circus tent', + 'clown', + 'clowns', + 'united kingdom', + 'london', + 'uk', + 'cartoon', + 'weekend', + '4th of july' + ]), + Emoji( + name: 'roller coaster', + char: '\u{1F3A2}', + shortName: 'roller_coaster', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeOther, + keywords: [ + 'amusement park', + 'coaster', + 'roller', + 'uc6', + 'places', + 'travel', + 'vacation', + 'amusement park', + 'disney', + 'fun', + 'summer', + 'theme park', + 'cartoon', + 'weekend' + ]), + Emoji( + name: 'carousel horse', + char: '\u{1F3A0}', + shortName: 'carousel_horse', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeOther, + keywords: [ + 'carousel', + 'horse', + 'uc6', + 'places', + 'vacation', + 'amusement park', + 'carousel', + 'donkey', + 'disney', + 'fun', + 'independence day', + 'theme park', + 'carousel horse', + 'poney', + 'cartoon', + '4th of july' + ]), + Emoji( + name: 'fountain', + char: '\u{26F2}', + shortName: 'fountain', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeOther, + keywords: [ + 'fountain', + 'uc5', + 'places', + 'travel', + 'vacation', + 'rich', + 'memorial', + 'grand', + 'expensive', + 'fancy' + ]), + Emoji( + name: 'umbrella on ground', + char: '\u{26F1}\u{FE0F}', + shortName: 'beach_umbrella', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.skyWeather, + keywords: [ + 'rain', + 'sun', + 'umbrella', + 'uc5', + 'travel', + 'tropical', + 'vacation', + 'umbrella', + 'hawaii', + 'california', + 'florida', + 'summer', + 'aloha', + 'kawaii', + 'maui', + 'moana', + 'weekend' + ]), + Emoji( + name: 'beach with umbrella', + char: '\u{1F3D6}\u{FE0F}', + shortName: 'beach', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeGeographic, + keywords: [ + 'beach', + 'umbrella', + 'uc7', + 'places', + 'travel', + 'tropical', + 'vacation', + 'swim', + 'beach', + 'australia', + 'umbrella', + 'hawaii', + 'california', + 'florida', + 'fun', + 'summer', + 'swimming', + 'swimmer', + 'aloha', + 'kawaii', + 'maui', + 'moana', + 'weekend' + ]), + Emoji( + name: 'desert island', + char: '\u{1F3DD}\u{FE0F}', + shortName: 'island', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeGeographic, + keywords: [ + 'desert', + 'island', + 'uc7', + 'places', + 'travel', + 'tropical', + 'vacation', + 'swim', + 'beach', + 'hawaii', + 'florida', + 'summer', + 'swimming', + 'swimmer', + 'aloha', + 'kawaii', + 'maui', + 'moana', + 'weekend' + ]), + Emoji( + name: 'desert', + char: '\u{1F3DC}\u{FE0F}', + shortName: 'desert', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeGeographic, + keywords: [ + 'desert', + 'uc7', + 'places', + 'travel', + 'vacation', + 'hot', + 'australia', + 'california', + 'las vegas', + 'heat', + 'warm', + 'caliente', + 'chaud', + 'heiß', + 'vegas' + ]), + Emoji( + name: 'volcano', + char: '\u{1F30B}', + shortName: 'volcano', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeGeographic, + keywords: [ + 'eruption', + 'mountain', + 'uc6', + 'places', + 'travel', + 'japan', + 'smoking', + 'tropical', + 'mountain', + 'explosion', + 'hawaii', + 'minecraft', + 'japanese', + 'ninja', + 'smoke', + 'cigarette', + 'puff', + 'explode', + 'aloha', + 'kawaii', + 'maui', + 'moana' + ]), + Emoji( + name: 'mountain', + char: '\u{26F0}\u{FE0F}', + shortName: 'mountain', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeGeographic, + keywords: [ + 'mountain', + 'uc5', + 'places', + 'travel', + 'camp', + 'vacation', + 'mountain', + 'climb', + 'camping', + 'tent', + 'camper', + 'outdoor', + 'outside' + ]), + Emoji( + name: 'snow-capped mountain', + char: '\u{1F3D4}\u{FE0F}', + shortName: 'mountain_snow', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeGeographic, + keywords: [ + 'cold', + 'mountain', + 'snow', + 'uc7', + 'places', + 'winter', + 'travel', + 'snow', + 'camp', + 'vacation', + 'cold', + 'snowboarding', + 'mountain', + 'paris', + 'polar bear', + 'freeze', + 'frozen', + 'frost', + 'ice cube', + 'camping', + 'tent', + 'camper', + 'outdoor', + 'outside', + 'chilly', + 'chilled', + 'brisk', + 'freezing', + 'frostbite', + 'icicles', + 'snowboarder', + 'french', + 'france' + ]), + Emoji( + name: 'mount fuji', + char: '\u{1F5FB}', + shortName: 'mount_fuji', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeGeographic, + keywords: [ + 'fuji', + 'mountain', + 'uc6', + 'places', + 'travel', + 'japan', + 'camp', + 'vacation', + 'cold', + 'mountain', + 'japanese', + 'ninja', + 'camping', + 'tent', + 'camper', + 'outdoor', + 'outside', + 'chilly', + 'chilled', + 'brisk', + 'freezing', + 'frostbite', + 'icicles' + ]), + Emoji( + name: 'camping', + char: '\u{1F3D5}\u{FE0F}', + shortName: 'camping', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeGeographic, + keywords: [ + 'camping', + 'uc7', + 'places', + 'travel', + 'camp', + 'vacation', + 'mountain', + 'fun', + 'parks', + 'camping', + 'tent', + 'camper', + 'outdoor', + 'outside', + 'regional park', + 'nature park', + 'natural park' + ]), + Emoji( + name: 'tent', + char: '\u{26FA}', + shortName: 'tent', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeOther, + keywords: [ + 'camping', + 'uc5', + 'places', + 'travel', + 'camp', + 'vacation', + 'summer', + 'camping', + 'tent', + 'camper', + 'outdoor', + 'outside', + 'weekend' + ]), + Emoji( + name: 'house', + char: '\u{1F3E0}', + shortName: 'house', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeBuilding, + keywords: [ + 'home', + 'house', + 'uc6', + 'building', + 'places', + 'house', + 'covid', + 'buildings', + 'houses', + 'apartment', + 'apartments', + 'casa', + 'maison', + 'home' + ]), + Emoji( + name: 'house with garden', + char: '\u{1F3E1}', + shortName: 'house_with_garden', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeBuilding, + keywords: [ + 'garden', + 'home', + 'house', + 'uc6', + 'building', + 'places', + 'house', + 'buildings', + 'houses', + 'apartment', + 'apartments', + 'casa', + 'maison', + 'home' + ]), + Emoji( + name: 'houses', + char: '\u{1F3D8}\u{FE0F}', + shortName: 'homes', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeBuilding, + keywords: [ + 'houses', + 'uc7', + 'building', + 'places', + 'house', + 'buildings', + 'houses', + 'apartment', + 'apartments', + 'casa', + 'maison', + 'home' + ]), + Emoji( + name: 'derelict house', + char: '\u{1F3DA}\u{FE0F}', + shortName: 'house_abandoned', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeBuilding, + keywords: [ + 'derelict', + 'house', + 'uc7', + 'building', + 'places', + 'house', + 'halloween', + 'buildings', + 'houses', + 'apartment', + 'apartments', + 'casa', + 'maison', + 'home', + 'samhain' + ]), + Emoji( + name: 'hut', + char: '\u{1F6D6}', + shortName: 'hut', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeBuilding, + keywords: ['uc13', 'places', 'star wars']), + Emoji( + name: 'building construction', + char: '\u{1F3D7}\u{FE0F}', + shortName: 'construction_site', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeBuilding, + keywords: [ + 'construction', + 'uc7', + 'building', + 'crane', + 'build', + 'construction', + 'buildings' + ]), + Emoji( + name: 'factory', + char: '\u{1F3ED}', + shortName: 'factory', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeBuilding, + keywords: [ + 'building', + 'uc6', + 'building', + 'places', + 'travel', + 'steam', + 'power', + 'poison', + 'buildings', + 'steaming', + 'piping', + 'toxic', + 'toxins' + ]), + Emoji( + name: 'office building', + char: '\u{1F3E2}', + shortName: 'office', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeBuilding, + keywords: [ + 'building', + 'uc6', + 'building', + 'places', + 'classroom', + 'business', + 'work', + 'buildings', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'office' + ]), + Emoji( + name: 'department store', + char: '\u{1F3EC}', + shortName: 'department_store', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeBuilding, + keywords: [ + 'department', + 'store', + 'uc6', + 'building', + 'places', + 'disney', + 'buildings', + 'cartoon' + ]), + Emoji( + name: 'Japanese post office', + char: '\u{1F3E3}', + shortName: 'post_office', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeBuilding, + keywords: [ + 'Japanese', + 'post', + 'uc6', + 'building', + 'places', + 'japan', + 'mail', + 'buildings', + 'japanese', + 'ninja', + 'email', + 'post', + 'post office' + ]), + Emoji( + name: 'post office', + char: '\u{1F3E4}', + shortName: 'european_post_office', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeBuilding, + keywords: [ + 'European', + 'post', + 'uc6', + 'building', + 'places', + 'mail', + 'buildings', + 'email', + 'post', + 'post office' + ]), + Emoji( + name: 'hospital', + char: '\u{1F3E5}', + shortName: 'hospital', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeBuilding, + keywords: [ + 'doctor', + 'medicine', + 'uc6', + 'building', + 'places', + 'health', + '911', + 'nurse', + 'covid', + 'buildings', + 'medicine', + 'doctor', + 'emergency', + 'injury' + ]), + Emoji( + name: 'bank', + char: '\u{1F3E6}', + shortName: 'bank', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeBuilding, + keywords: [ + 'building', + 'uc6', + 'building', + 'places', + 'money', + 'rich', + 'buildings', + 'cash', + 'dollars', + 'dollar', + 'bucks', + 'currency', + 'funds', + 'payment', + 'money face', + 'reward', + 'thief', + 'bank', + 'benjamins', + 'argent', + 'dinero', + 'i soldi', + 'Geld', + 'grand', + 'expensive', + 'fancy' + ]), + Emoji( + name: 'hotel', + char: '\u{1F3E8}', + shortName: 'hotel', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeBuilding, + keywords: [ + 'building', + 'uc6', + 'building', + 'places', + 'vacation', + 'las vegas', + 'hotel', + 'buildings', + 'vegas', + 'vacancy', + 'no vacancy' + ]), + Emoji( + name: 'convenience store', + char: '\u{1F3EA}', + shortName: 'convenience_store', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeBuilding, + keywords: [ + 'convenience', + 'store', + 'uc6', + 'building', + 'places', + 'las vegas', + 'buildings', + 'vegas' + ]), + Emoji( + name: 'school', + char: '\u{1F3EB}', + shortName: 'school', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeBuilding, + keywords: [ + 'building', + 'uc6', + 'building', + 'places', + 'classroom', + 'buildings', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning' + ]), + Emoji( + name: 'love hotel', + char: '\u{1F3E9}', + shortName: 'love_hotel', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeBuilding, + keywords: [ + 'hotel', + 'love', + 'uc6', + 'building', + 'places', + 'love', + 'japan', + 'vacation', + 'pink', + 'porn', + 'hotel', + 'buildings', + 'i love you', + 'te amo', + "je t'aime", + 'anniversary', + 'lovin', + 'amour', + 'aimer', + 'amor', + 'valentines day', + 'enamour', + 'lovey', + 'japanese', + 'ninja', + 'rose', + 'vacancy', + 'no vacancy' + ]), + Emoji( + name: 'wedding', + char: '\u{1F492}', + shortName: 'wedding', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeBuilding, + keywords: [ + 'chapel', + 'romance', + 'uc6', + 'building', + 'places', + 'wedding', + 'love', + 'pink', + 'buildings', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'i love you', + 'te amo', + "je t'aime", + 'anniversary', + 'lovin', + 'amour', + 'aimer', + 'amor', + 'valentines day', + 'enamour', + 'lovey', + 'rose' + ]), + Emoji( + name: 'classical building', + char: '\u{1F3DB}\u{FE0F}', + shortName: 'classical_building', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeBuilding, + keywords: [ + 'classical', + 'uc7', + 'building', + 'places', + 'travel', + 'vacation', + 'police', + 'history', + 'court', + 'memorial', + 'buildings', + 'cop', + 'policeman', + 'popo', + 'prison', + 'handcuff', + 'jail', + 'justice', + 'ancient', + 'old' + ]), + Emoji( + name: 'church', + char: '\u{26EA}', + shortName: 'church', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeReligious, + keywords: [ + 'Christian', + 'cross', + 'religion', + 'uc5', + 'building', + 'places', + 'wedding', + 'religion', + 'travel', + 'christmas', + 'pray', + 'condolence', + 'jesus', + 'easter', + 'bible', + 'advent', + 'buildings', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'navidad', + 'xmas', + 'noel', + 'merry christmas', + 'prayer', + 'praying', + 'prayers', + 'grateful', + 'sorry', + 'heaven', + 'bless', + 'faith', + 'holy', + 'spirit', + 'hopeful', + 'blessed', + 'preach', + 'offering', + 'compassion' + ]), + Emoji( + name: 'mosque', + char: '\u{1F54C}', + shortName: 'mosque', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeReligious, + keywords: [ + 'Muslim', + 'islam', + 'religion', + 'uc8', + 'building', + 'places', + 'religion', + 'vacation', + 'pray', + 'condolence', + 'islam', + 'buildings', + 'prayer', + 'praying', + 'prayers', + 'grateful', + 'sorry', + 'heaven', + 'bless', + 'faith', + 'holy', + 'spirit', + 'hopeful', + 'blessed', + 'preach', + 'offering', + 'compassion', + 'muslim', + 'arab' + ]), + Emoji( + name: 'synagogue', + char: '\u{1F54D}', + shortName: 'synagogue', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeReligious, + keywords: [ + 'Jew', + 'Jewish', + 'religion', + 'temple', + 'uc8', + 'building', + 'places', + 'wedding', + 'religion', + 'vacation', + 'pray', + 'condolence', + 'jewish', + 'buildings', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'prayer', + 'praying', + 'prayers', + 'grateful', + 'sorry', + 'heaven', + 'bless', + 'faith', + 'holy', + 'spirit', + 'hopeful', + 'blessed', + 'preach', + 'offering', + 'compassion', + 'hannukah', + 'hanukkah', + 'israel' + ]), + Emoji( + name: 'hindu temple', + char: '\u{1F6D5}', + shortName: 'hindu_temple', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeReligious, + keywords: [ + 'uc12', + 'building', + 'places', + 'travel', + 'pray', + 'dharmachakra', + 'buildings', + 'prayer', + 'praying', + 'prayers', + 'grateful', + 'sorry', + 'heaven', + 'bless', + 'faith', + 'holy', + 'spirit', + 'hopeful', + 'blessed', + 'preach', + 'offering', + 'jainism', + 'buddhism', + 'hinduism', + 'nirvana', + 'maintain', + 'keep', + 'law', + 'bueno', + 'dharma', + 'kama', + 'artha', + 'moksa', + 'karma' + ]), + Emoji( + name: 'kaaba', + char: '\u{1F54B}', + shortName: 'kaaba', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeReligious, + keywords: [ + 'Muslim', + 'islam', + 'religion', + 'uc8', + 'building', + 'places', + 'religion', + 'pray', + 'condolence', + 'islam', + 'buildings', + 'prayer', + 'praying', + 'prayers', + 'grateful', + 'sorry', + 'heaven', + 'bless', + 'faith', + 'holy', + 'spirit', + 'hopeful', + 'blessed', + 'preach', + 'offering', + 'compassion', + 'muslim', + 'arab' + ]), + Emoji( + name: 'shinto shrine', + char: '\u{26E9}\u{FE0F}', + shortName: 'shinto_shrine', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeReligious, + keywords: [ + 'religion', + 'shinto', + 'shrine', + 'uc5', + 'building', + 'places', + 'travel', + 'japan', + 'vacation', + 'buildings', + 'japanese', + 'ninja' + ]), + Emoji( + name: 'railway track', + char: '\u{1F6E4}\u{FE0F}', + shortName: 'railway_track', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportGround, + keywords: [ + 'railway', + 'train', + 'uc7', + 'train', + 'travel', + 'vacation', + 'trains' + ]), + Emoji( + name: 'motorway', + char: '\u{1F6E3}\u{FE0F}', + shortName: 'motorway', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportGround, + keywords: [ + 'highway', + 'road', + 'uc7', + 'travel', + 'camp', + 'vacation', + 'road', + 'camping', + 'tent', + 'camper', + 'outdoor', + 'outside', + 'route', + 'highway', + 'street' + ]), + Emoji( + name: 'map of Japan', + char: '\u{1F5FE}', + shortName: 'japan', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeMap, + keywords: [ + 'Japan', + 'map', + 'uc6', + 'places', + 'travel', + 'japan', + 'map', + 'vacation', + 'japanese', + 'ninja', + 'maps', + 'location', + 'locate', + 'local', + 'lost' + ]), + Emoji( + name: 'moon viewing ceremony', + char: '\u{1F391}', + shortName: 'rice_scene', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.event, + keywords: [ + 'celebration', + 'ceremony', + 'moon', + 'uc6', + 'places', + 'space', + 'sky', + 'travel', + 'japan', + 'outer space', + 'galaxy', + 'universe', + 'nasa', + 'spaceship', + 'japanese', + 'ninja' + ]), + Emoji( + name: 'national park', + char: '\u{1F3DE}\u{FE0F}', + shortName: 'park', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeGeographic, + keywords: [ + 'park', + 'uc7', + 'places', + 'travel', + 'camp', + 'vacation', + 'summer', + 'river', + 'memorial', + 'parks', + 'camping', + 'tent', + 'camper', + 'outdoor', + 'outside', + 'weekend', + 'regional park', + 'nature park', + 'natural park' + ]), + Emoji( + name: 'sunrise', + char: '\u{1F305}', + shortName: 'sunrise', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeOther, + keywords: [ + 'morning', + 'sun', + 'uc6', + 'places', + 'sun', + 'sky', + 'travel', + 'tropical', + 'vacation', + 'day', + 'hump day', + 'morning', + 'hawaii', + 'california', + 'florida', + 'scuba', + 'summer', + 'sunshine', + 'sunny', + 'eclipse', + 'solar', + 'solareclipse', + 'shiny', + 'good morning', + 'aloha', + 'kawaii', + 'maui', + 'moana', + 'snorkel', + 'weekend' + ]), + Emoji( + name: 'sunrise over mountains', + char: '\u{1F304}', + shortName: 'sunrise_over_mountains', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeOther, + keywords: [ + 'morning', + 'mountain', + 'sun', + 'sunrise', + 'uc6', + 'places', + 'sun', + 'sky', + 'travel', + 'camp', + 'vacation', + 'day', + 'morning', + 'mountain', + 'california', + 'sunshine', + 'sunny', + 'eclipse', + 'solar', + 'solareclipse', + 'shiny', + 'camping', + 'tent', + 'camper', + 'outdoor', + 'outside', + 'good morning' + ]), + Emoji( + name: 'shooting star', + char: '\u{1F320}', + shortName: 'stars', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.skyWeather, + keywords: [ + 'falling', + 'shooting', + 'star', + 'uc6', + 'space', + 'star wars', + 'fame', + 'sparkle', + 'outer space', + 'galaxy', + 'universe', + 'nasa', + 'spaceship', + 'famous', + 'celebrity', + 'bright', + 'shine', + 'twinkle' + ]), + Emoji( + name: 'sparkler', + char: '\u{1F387}', + shortName: 'sparkler', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.event, + keywords: [ + 'celebration', + 'fireworks', + 'sparkle', + 'uc6', + 'holidays', + 'happy birthday', + 'firework', + 'explosion', + 'celebrate', + 'glitter', + 'disney', + 'sparkle', + 'holiday', + 'Bon anniversaire', + 'joyeux anniversaire', + 'buon compleanno', + 'feliz cumpleaños', + 'alles Gute zum Geburtstag', + 'feliz Aniversário', + 'Gratulerer med dagen', + 'fireworks', + 'firecracker', + 'explode', + 'celebration', + 'event', + 'celebrating', + 'festa', + 'parties', + 'events', + 'new years', + 'new year', + 'fiesta', + 'fete', + 'newyear', + 'party', + 'festive', + 'festival', + 'yolo', + 'festejar', + 'cartoon', + 'bright', + 'shine', + 'twinkle' + ]), + Emoji( + name: 'fireworks', + char: '\u{1F386}', + shortName: 'fireworks', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.event, + keywords: [ + 'celebration', + 'uc6', + 'holidays', + 'firework', + 'explosion', + 'celebrate', + 'glitter', + 'disney', + 'sparkle', + 'independence day', + 'holiday', + 'fireworks', + 'firecracker', + 'explode', + 'celebration', + 'event', + 'celebrating', + 'festa', + 'parties', + 'events', + 'new years', + 'new year', + 'fiesta', + 'fete', + 'newyear', + 'party', + 'festive', + 'festival', + 'yolo', + 'festejar', + 'cartoon', + 'bright', + 'shine', + 'twinkle', + '4th of july' + ]), + Emoji( + name: 'sunset', + char: '\u{1F307}', + shortName: 'city_sunset', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeOther, + keywords: [ + 'dusk', + 'sun', + 'uc6', + 'building', + 'places', + 'sky', + 'vacation', + 'buildings' + ]), + Emoji( + name: 'cityscape at dusk', + char: '\u{1F306}', + shortName: 'city_dusk', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeOther, + keywords: [ + 'city', + 'dusk', + 'evening', + 'landscape', + 'sun', + 'sunset', + 'uc6', + 'building', + 'places', + 'buildings' + ]), + Emoji( + name: 'cityscape', + char: '\u{1F3D9}\u{FE0F}', + shortName: 'cityscape', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeOther, + keywords: [ + 'city', + 'uc7', + 'building', + 'places', + 'vacation', + 'new york', + 'england', + 'donald trump', + 'rich', + 'buildings', + 'united kingdom', + 'london', + 'uk', + 'trump', + 'grand', + 'expensive', + 'fancy' + ]), + Emoji( + name: 'night with stars', + char: '\u{1F303}', + shortName: 'night_with_stars', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeOther, + keywords: [ + 'night', + 'star', + 'uc6', + 'building', + 'places', + 'halloween', + 'sky', + 'vacation', + 'goodnight', + 'buildings', + 'samhain' + ]), + Emoji( + name: 'milky way', + char: '\u{1F30C}', + shortName: 'milky_way', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.skyWeather, + keywords: [ + 'space', + 'uc6', + 'places', + 'space', + 'sky', + 'travel', + 'star', + 'vacation', + 'goodnight', + 'star wars', + 'dream', + 'fantasy', + 'outer space', + 'galaxy', + 'universe', + 'nasa', + 'spaceship', + 'stars', + 'dreams' + ]), + Emoji( + name: 'bridge at night', + char: '\u{1F309}', + shortName: 'bridge_at_night', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeOther, + keywords: [ + 'bridge', + 'night', + 'uc6', + 'places', + 'travel', + 'vacation', + 'goodnight', + 'england', + 'california', + 'united kingdom', + 'london', + 'uk' + ]), + Emoji( + name: 'foggy', + char: '\u{1F301}', + shortName: 'foggy', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeOther, + keywords: [ + 'fog', + 'uc6', + 'building', + 'places', + 'sky', + 'travel', + 'vacation', + 'england', + 'buildings', + 'united kingdom', + 'london', + 'uk' + ]), + Emoji( + name: 'watch', + char: '\u{231A}', + shortName: 'watch', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.time, + keywords: [ + 'clock', + 'uc1', + 'electronics', + 'time', + 'accessories', + 'bling', + 'wait', + 'clocks', + 'clock', + 'jewels', + 'gems', + 'jewel', + 'jewelry', + 'treasure', + 'hours' + ]), + Emoji( + name: 'mobile phone', + char: '\u{1F4F1}', + shortName: 'mobile_phone', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.phone, + keywords: [ + 'cell', + 'mobile', + 'phone', + 'telephone', + 'uc6', + 'electronics', + 'phone', + 'talk', + 'selfie', + 'technology', + 'laptop', + 'instagram', + 'telephone', + 'iphone', + 'smartphone', + 'text', + 'talking', + 'speech', + 'social', + 'chat', + 'voice', + 'speechless', + 'speak', + 'computer', + 'online', + 'wifi', + 'website', + 'zoom', + 'ipad', + 'tablet' + ]), + Emoji( + name: 'mobile phone with arrow', + char: '\u{1F4F2}', + shortName: 'calling', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.phone, + keywords: [ + 'arrow', + 'call', + 'cell', + 'mobile', + 'phone', + 'receive', + 'telephone', + 'uc6', + 'electronics', + 'phone', + 'selfie', + 'technology', + 'download', + 'telephone', + 'iphone', + 'smartphone', + 'text' + ]), + Emoji( + name: 'laptop', + char: '\u{1F4BB}', + shortName: 'computer', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.computer, + keywords: [ + 'computer', + 'pc', + 'personal', + 'uc6', + 'electronics', + 'classroom', + 'internet', + 'technology', + 'laptop', + 'download', + 'instagram', + 'youtube', + 'work', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'computer', + 'online', + 'wifi', + 'website', + 'zoom', + 'ipad', + 'tablet', + 'vlog', + 'office' + ]), + Emoji( + name: 'keyboard', + char: '\u{2328}\u{FE0F}', + shortName: 'keyboard', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.computer, + keywords: [ + 'computer', + 'uc1', + 'electronics', + 'classroom', + 'technology', + 'laptop', + 'work', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'computer', + 'online', + 'wifi', + 'website', + 'zoom', + 'ipad', + 'tablet', + 'office' + ]), + Emoji( + name: 'desktop computer', + char: '\u{1F5A5}\u{FE0F}', + shortName: 'desktop', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.computer, + keywords: [ + 'computer', + 'desktop', + 'uc7', + 'electronics', + 'classroom', + 'internet', + 'technology', + 'laptop', + 'download', + 'instagram', + 'youtube', + 'work', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'computer', + 'online', + 'wifi', + 'website', + 'zoom', + 'ipad', + 'tablet', + 'vlog', + 'office' + ]), + Emoji( + name: 'printer', + char: '\u{1F5A8}\u{FE0F}', + shortName: 'printer', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.computer, + keywords: [ + 'computer', + 'uc7', + 'electronics', + 'classroom', + 'technology', + 'work', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'office' + ]), + Emoji( + name: 'computer mouse', + char: '\u{1F5B1}\u{FE0F}', + shortName: 'mouse_three_button', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.computer, + keywords: [ + 'computer', + 'uc7', + 'electronics', + 'classroom', + 'game', + 'technology', + 'laptop', + 'click', + 'work', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'games', + 'gaming', + 'computer', + 'online', + 'wifi', + 'website', + 'zoom', + 'ipad', + 'tablet', + 'office' + ]), + Emoji( + name: 'trackball', + char: '\u{1F5B2}\u{FE0F}', + shortName: 'trackball', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.computer, + keywords: [ + 'computer', + 'uc7', + 'electronics', + 'classroom', + 'game', + 'technology', + 'laptop', + 'click', + 'work', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'games', + 'gaming', + 'computer', + 'online', + 'wifi', + 'website', + 'zoom', + 'ipad', + 'tablet', + 'office' + ]), + Emoji( + name: 'joystick', + char: '\u{1F579}\u{FE0F}', + shortName: 'joystick', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.game, + keywords: [ + 'game', + 'video game', + 'uc7', + 'electronics', + 'game', + 'boys night', + 'technology', + 'controller', + 'pacman', + 'games', + 'gaming', + 'guys night', + 'remote', + 'pac man' + ]), + Emoji( + name: 'clamp', + char: '\u{1F5DC}\u{FE0F}', + shortName: 'compression', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.tool, + keywords: [ + 'compress', + 'tool', + 'vice', + 'uc7', + 'tool', + 'download', + 'steel', + 'tools', + 'metal' + ]), + Emoji( + name: 'computer disk', + char: '\u{1F4BD}', + shortName: 'minidisc', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.computer, + keywords: [ + 'computer', + 'disk', + 'minidisk', + 'optical', + 'uc6', + 'instruments', + 'electronics', + 'classroom', + 'laptop', + 'download', + 'work', + 'music', + 'instrument', + 'singing', + 'concert', + 'jaz', + 'listen', + 'singer', + 'song', + 'musique', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'computer', + 'online', + 'wifi', + 'website', + 'zoom', + 'ipad', + 'tablet', + 'office' + ]), + Emoji( + name: 'floppy disk', + char: '\u{1F4BE}', + shortName: 'floppy_disk', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.computer, + keywords: [ + 'computer', + 'disk', + 'floppy', + 'uc6', + 'electronics', + 'classroom', + 'laptop', + 'download', + 'work', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'computer', + 'online', + 'wifi', + 'website', + 'zoom', + 'ipad', + 'tablet', + 'office' + ]), + Emoji( + name: 'optical disk', + char: '\u{1F4BF}', + shortName: 'cd', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.computer, + keywords: [ + 'cd', + 'computer', + 'disk', + 'optical', + 'uc6', + 'instruments', + 'electronics', + 'download', + 'music', + 'instrument', + 'singing', + 'concert', + 'jaz', + 'listen', + 'singer', + 'song', + 'musique' + ]), + Emoji( + name: 'dvd', + char: '\u{1F4C0}', + shortName: 'dvd', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.computer, + keywords: [ + 'blu-ray', + 'computer', + 'disk', + 'dvd', + 'optical', + 'uc6', + 'electronics' + ]), + Emoji( + name: 'videocassette', + char: '\u{1F4FC}', + shortName: 'vhs', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.lightVideo, + keywords: [ + 'tape', + 'vhs', + 'video', + 'uc6', + 'electronics', + 'history', + 'ancient', + 'old' + ]), + Emoji( + name: 'camera', + char: '\u{1F4F7}', + shortName: 'camera', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.lightVideo, + keywords: [ + 'video', + 'uc6', + 'electronics', + 'selfie', + 'technology', + 'detective', + 'instagram', + 'youtube', + 'vlog' + ]), + Emoji( + name: 'camera with flash', + char: '\u{1F4F8}', + shortName: 'camera_with_flash', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.lightVideo, + keywords: [ + 'camera', + 'flash', + 'video', + 'uc7', + 'electronics', + 'technology', + 'instagram' + ]), + Emoji( + name: 'video camera', + char: '\u{1F4F9}', + shortName: 'video_camera', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.lightVideo, + keywords: [ + 'camera', + 'video', + 'uc6', + 'electronics', + 'movie', + 'technology', + 'porn', + 'youtube', + 'movies', + 'cinema', + 'film', + 'films', + 'video', + 'videos', + 'vlog' + ]), + Emoji( + name: 'movie camera', + char: '\u{1F3A5}', + shortName: 'movie_camera', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.lightVideo, + keywords: [ + 'camera', + 'cinema', + 'movie', + 'uc6', + 'movie', + 'movies', + 'cinema', + 'film', + 'films', + 'video', + 'videos' + ]), + Emoji( + name: 'film projector', + char: '\u{1F4FD}\u{FE0F}', + shortName: 'projector', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.lightVideo, + keywords: [ + 'cinema', + 'film', + 'movie', + 'projector', + 'video', + 'uc7', + 'movie', + 'disney', + 'california', + 'fame', + 'movies', + 'cinema', + 'film', + 'films', + 'video', + 'videos', + 'cartoon', + 'famous', + 'celebrity' + ]), + Emoji( + name: 'film frames', + char: '\u{1F39E}\u{FE0F}', + shortName: 'film_frames', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.lightVideo, + keywords: [ + 'cinema', + 'film', + 'frames', + 'movie', + 'uc7', + 'movie', + 'disney', + 'california', + 'movies', + 'cinema', + 'film', + 'films', + 'video', + 'videos', + 'cartoon' + ]), + Emoji( + name: 'telephone receiver', + char: '\u{1F4DE}', + shortName: 'telephone_receiver', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.phone, + keywords: [ + 'phone', + 'receiver', + 'telephone', + 'uc6', + 'electronics', + 'phone', + 'talk', + 'telephone', + 'iphone', + 'smartphone', + 'text', + 'talking', + 'speech', + 'social', + 'chat', + 'voice', + 'speechless', + 'speak' + ]), + Emoji( + name: 'telephone', + char: '\u{260E}\u{FE0F}', + shortName: 'telephone', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.phone, + keywords: [ + 'phone', + 'uc1', + 'electronics', + 'phone', + 'talk', + 'history', + 'hotel', + 'telephone', + 'iphone', + 'smartphone', + 'text', + 'talking', + 'speech', + 'social', + 'chat', + 'voice', + 'speechless', + 'speak', + 'ancient', + 'old', + 'vacancy', + 'no vacancy' + ]), + Emoji( + name: 'pager', + char: '\u{1F4DF}', + shortName: 'pager', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.phone, + keywords: [ + 'pager', + 'uc6', + 'electronics', + 'technology', + 'history', + 'work', + 'ancient', + 'old', + 'office' + ]), + Emoji( + name: 'fax machine', + char: '\u{1F4E0}', + shortName: 'fax', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.phone, + keywords: [ + 'fax', + 'uc6', + 'electronics', + 'classroom', + 'technology', + 'work', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'office' + ]), + Emoji( + name: 'television', + char: '\u{1F4FA}', + shortName: 'tv', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.lightVideo, + keywords: [ + 'tv', + 'video', + 'uc6', + 'electronics', + 'classroom', + 'technology', + 'news', + 'fame', + 'history', + 'youtube', + 'household', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'article', + 'famous', + 'celebrity', + 'ancient', + 'old', + 'vlog' + ]), + Emoji( + name: 'radio', + char: '\u{1F4FB}', + shortName: 'radio', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.music, + keywords: [ + 'video', + 'uc6', + 'instruments', + 'electronics', + 'news', + 'history', + 'sound', + 'household', + 'music', + 'instrument', + 'singing', + 'concert', + 'jaz', + 'listen', + 'singer', + 'song', + 'musique', + 'article', + 'ancient', + 'old', + 'volume', + 'speaker', + 'loud', + 'mic', + 'audio', + 'hear' + ]), + Emoji( + name: 'studio microphone', + char: '\u{1F399}\u{FE0F}', + shortName: 'microphone2', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.music, + keywords: [ + 'mic', + 'microphone', + 'music', + 'studio', + 'uc7', + 'instruments', + 'electronics', + 'news', + 'sound', + 'music', + 'instrument', + 'singing', + 'concert', + 'jaz', + 'listen', + 'singer', + 'song', + 'musique', + 'article', + 'volume', + 'speaker', + 'loud', + 'mic', + 'audio', + 'hear' + ]), + Emoji( + name: 'level slider', + char: '\u{1F39A}\u{FE0F}', + shortName: 'level_slider', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.music, + keywords: [ + 'level', + 'music', + 'slider', + 'uc7', + 'instruments', + 'electronics', + 'sound', + 'music', + 'instrument', + 'singing', + 'concert', + 'jaz', + 'listen', + 'singer', + 'song', + 'musique', + 'volume', + 'speaker', + 'loud', + 'mic', + 'audio', + 'hear' + ]), + Emoji( + name: 'control knobs', + char: '\u{1F39B}\u{FE0F}', + shortName: 'control_knobs', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.music, + keywords: [ + 'control', + 'knobs', + 'music', + 'uc7', + 'instruments', + 'electronics', + 'power', + 'bake', + 'sound', + 'music', + 'instrument', + 'singing', + 'concert', + 'jaz', + 'listen', + 'singer', + 'song', + 'musique', + 'baking', + 'volume', + 'speaker', + 'loud', + 'mic', + 'audio', + 'hear' + ]), + Emoji( + name: 'compass', + char: '\u{1F9ED}', + shortName: 'compass', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeMap, + keywords: [ + 'uc11', + 'travel', + 'camp', + 'science', + 'map', + 'globe', + 'navigate', + 'direction', + 'camping', + 'tent', + 'camper', + 'outdoor', + 'outside', + 'lab', + 'maps', + 'location', + 'locate', + 'local', + 'lost', + 'globes', + 'planet', + 'earth', + 'earthquake' + ]), + Emoji( + name: 'stopwatch', + char: '\u{23F1}\u{FE0F}', + shortName: 'stopwatch', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.time, + keywords: [ + 'clock', + 'uc6', + 'electronics', + 'time', + 'wait', + 'clocks', + 'clock', + 'hours' + ]), + Emoji( + name: 'timer clock', + char: '\u{23F2}\u{FE0F}', + shortName: 'timer', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.time, + keywords: [ + 'clock', + 'timer', + 'uc6', + 'time', + 'bake', + 'wait', + 'measure', + 'clocks', + 'clock', + 'baking', + 'hours' + ]), + Emoji( + name: 'alarm clock', + char: '\u{23F0}', + shortName: 'alarm_clock', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.time, + keywords: [ + 'alarm', + 'clock', + 'uc6', + 'time', + 'alarm', + 'wait', + 'clocks', + 'clock', + 'alarms', + 'announce', + 'hours' + ]), + Emoji( + name: 'mantelpiece clock', + char: '\u{1F570}\u{FE0F}', + shortName: 'clock', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.time, + keywords: [ + 'clock', + 'uc7', + 'time', + 'vintage', + 'wait', + 'household', + 'clocks', + 'clock', + 'hours' + ]), + Emoji( + name: 'hourglass done', + char: '\u{231B}', + shortName: 'hourglass', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.time, + keywords: [ + 'sand', + 'timer', + 'uc1', + 'time', + 'empty', + 'percent', + 'wait', + 'measure', + 'clocks', + 'clock', + 'hours' + ]), + Emoji( + name: 'hourglass not done', + char: '\u{23F3}', + shortName: 'hourglass_flowing_sand', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.time, + keywords: [ + 'hourglass', + 'sand', + 'timer', + 'uc6', + 'time', + 'infinity', + 'history', + 'percent', + 'wait', + 'measure', + 'clocks', + 'clock', + 'infini', + 'forever', + 'ancient', + 'old', + 'hours' + ]), + Emoji( + name: 'satellite antenna', + char: '\u{1F4E1}', + shortName: 'satellite', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.science, + keywords: ['antenna', 'dish', 'satellite', 'uc6', 'technology', 'power']), + Emoji( + name: 'battery', + char: '\u{1F50B}', + shortName: 'battery', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.computer, + keywords: ['battery', 'uc6', 'science', 'power', 'energy', 'lab']), + Emoji( + name: 'electric plug', + char: '\u{1F50C}', + shortName: 'electric_plug', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.computer, + keywords: [ + 'electric', + 'electricity', + 'plug', + 'uc6', + 'electronics', + 'electric', + 'power', + 'household', + 'energy' + ]), + Emoji( + name: 'light bulb', + char: '\u{1F4A1}', + shortName: 'bulb', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.lightVideo, + keywords: [ + 'bulb', + 'comic', + 'electric', + 'idea', + 'light', + 'uc6', + 'science', + 'electric', + 'light', + 'power', + 'idea', + 'sparkle', + 'innovate', + 'household', + 'energy', + 'lab', + 'lamp', + 'light bulb', + 'flashlight', + 'spotlight', + 'illuminate', + 'lightbulb', + 'lighting', + 'luce', + 'licht', + 'lumière', + 'luz', + 'bright', + 'shine', + 'twinkle', + 'innovation', + 'inquire' + ]), + Emoji( + name: 'flashlight', + char: '\u{1F526}', + shortName: 'flashlight', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.lightVideo, + keywords: [ + 'electric', + 'light', + 'tool', + 'torch', + 'uc6', + 'electronics', + 'tool', + 'light', + 'star wars', + 'search', + 'detective', + 'sparkle', + 'household', + 'energy', + 'tools', + 'lamp', + 'light bulb', + 'flashlight', + 'spotlight', + 'illuminate', + 'lightbulb', + 'lighting', + 'luce', + 'licht', + 'lumière', + 'luz', + 'look', + 'find', + 'looking', + 'see', + 'bright', + 'shine', + 'twinkle' + ]), + Emoji( + name: 'candle', + char: '\u{1F56F}\u{FE0F}', + shortName: 'candle', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.lightVideo, + keywords: [ + 'light', + 'uc7', + 'religion', + 'halloween', + 'birthday', + 'christmas', + 'light', + 'jewish', + 'samhain', + 'birth', + 'cumpleaños', + 'anniversaire', + 'bday', + 'navidad', + 'xmas', + 'noel', + 'merry christmas', + 'lamp', + 'light bulb', + 'flashlight', + 'spotlight', + 'illuminate', + 'lightbulb', + 'lighting', + 'luce', + 'licht', + 'lumière', + 'luz', + 'hannukah', + 'hanukkah', + 'israel' + ]), + Emoji( + name: 'diya lamp', + char: '\u{1FA94}', + shortName: 'diya_lamp', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.lightVideo, + keywords: [ + 'uc12', + 'light', + 'jealous', + 'dharmachakra', + 'diya', + 'greed', + 'soul', + 'lamp', + 'light bulb', + 'flashlight', + 'spotlight', + 'illuminate', + 'lightbulb', + 'lighting', + 'luce', + 'licht', + 'lumière', + 'luz', + 'jainism', + 'buddhism', + 'hinduism', + 'nirvana', + 'maintain', + 'keep', + 'law', + 'bueno', + 'dharma', + 'kama', + 'artha', + 'moksa', + 'karma', + 'diyo', + 'deya', + 'divaa', + 'deepa', + 'deepam', + 'deepak', + 'diwali', + 'oil lamp', + 'selfish' + ]), + Emoji( + name: 'fire extinguisher', + char: '\u{1F9EF}', + shortName: 'fire_extinguisher', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.household, + keywords: [ + 'uc11', + 'alarm', + 'science', + 'danger', + 'household', + 'fires', + 'alarms', + 'announce', + 'lab', + 'warn', + 'attention', + 'caution', + 'alert', + 'error', + 'panic', + 'restricted', + "don't", + 'dont', + 'dangerous' + ]), + Emoji( + name: 'oil drum', + char: '\u{1F6E2}\u{FE0F}', + shortName: 'oil', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.transportGround, + keywords: [ + 'drum', + 'oil', + 'uc7', + 'jewish', + 'hannukah', + 'hanukkah', + 'israel' + ]), + Emoji( + name: 'money with wings', + char: '\u{1F4B8}', + shortName: 'money_with_wings', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.money, + keywords: [ + 'bank', + 'banknote', + 'bill', + 'dollar', + 'fly', + 'money', + 'note', + 'wings', + 'uc6', + 'money', + 'vacation', + 'boys night', + 'coins', + 'las vegas', + 'rich', + 'purchase', + 'cash', + 'dollars', + 'dollar', + 'bucks', + 'currency', + 'funds', + 'payment', + 'money face', + 'reward', + 'thief', + 'bank', + 'benjamins', + 'argent', + 'dinero', + 'i soldi', + 'Geld', + 'guys night', + 'vegas', + 'grand', + 'expensive', + 'fancy', + 'buy', + 'shop', + 'spend' + ]), + Emoji( + name: 'dollar banknote', + char: '\u{1F4B5}', + shortName: 'dollar', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.money, + keywords: [ + 'bank', + 'banknote', + 'bill', + 'currency', + 'dollar', + 'money', + 'note', + 'uc6', + 'money', + 'coins', + 'rich', + 'purchase', + 'cash', + 'dollars', + 'dollar', + 'bucks', + 'currency', + 'funds', + 'payment', + 'money face', + 'reward', + 'thief', + 'bank', + 'benjamins', + 'argent', + 'dinero', + 'i soldi', + 'Geld', + 'grand', + 'expensive', + 'fancy', + 'buy', + 'shop', + 'spend' + ]), + Emoji( + name: 'yen banknote', + char: '\u{1F4B4}', + shortName: 'yen', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.money, + keywords: [ + 'bank', + 'banknote', + 'bill', + 'currency', + 'money', + 'note', + 'yen', + 'uc6', + 'money', + 'coins', + 'rich', + 'purchase', + 'cash', + 'dollars', + 'dollar', + 'bucks', + 'currency', + 'funds', + 'payment', + 'money face', + 'reward', + 'thief', + 'bank', + 'benjamins', + 'argent', + 'dinero', + 'i soldi', + 'Geld', + 'grand', + 'expensive', + 'fancy', + 'buy', + 'shop', + 'spend' + ]), + Emoji( + name: 'euro banknote', + char: '\u{1F4B6}', + shortName: 'euro', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.money, + keywords: [ + 'bank', + 'banknote', + 'bill', + 'currency', + 'euro', + 'money', + 'note', + 'uc6', + 'money', + 'coins', + 'rich', + 'purchase', + 'cash', + 'dollars', + 'dollar', + 'bucks', + 'currency', + 'funds', + 'payment', + 'money face', + 'reward', + 'thief', + 'bank', + 'benjamins', + 'argent', + 'dinero', + 'i soldi', + 'Geld', + 'grand', + 'expensive', + 'fancy', + 'buy', + 'shop', + 'spend' + ]), + Emoji( + name: 'pound banknote', + char: '\u{1F4B7}', + shortName: 'pound', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.money, + keywords: [ + 'bank', + 'banknote', + 'bill', + 'currency', + 'money', + 'note', + 'pound', + 'uc6', + 'money', + 'coins', + 'rich', + 'purchase', + 'cash', + 'dollars', + 'dollar', + 'bucks', + 'currency', + 'funds', + 'payment', + 'money face', + 'reward', + 'thief', + 'bank', + 'benjamins', + 'argent', + 'dinero', + 'i soldi', + 'Geld', + 'grand', + 'expensive', + 'fancy', + 'buy', + 'shop', + 'spend' + ]), + Emoji( + name: 'coin', + char: '\u{1FA99}', + shortName: 'coin', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.money, + keywords: [ + 'uc13', + 'money', + 'coins', + 'purchase', + 'cash', + 'dollars', + 'dollar', + 'bucks', + 'currency', + 'funds', + 'payment', + 'money face', + 'reward', + 'thief', + 'bank', + 'benjamins', + 'argent', + 'dinero', + 'i soldi', + 'Geld', + 'buy', + 'shop', + 'spend' + ]), + Emoji( + name: 'money bag', + char: '\u{1F4B0}', + shortName: 'moneybag', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.money, + keywords: [ + 'bag', + 'dollar', + 'money', + 'moneybag', + 'uc6', + 'wedding', + 'bag', + 'money', + 'award', + 'pirate', + 'bling', + 'coins', + 'donald trump', + 'rich', + 'weddings', + 'marriage', + 'newlywed', + 'bride', + 'groome', + 'groom', + 'married', + 'marry', + 'swag', + 'cash', + 'dollars', + 'dollar', + 'bucks', + 'currency', + 'funds', + 'payment', + 'money face', + 'reward', + 'thief', + 'bank', + 'benjamins', + 'argent', + 'dinero', + 'i soldi', + 'Geld', + 'awards', + 'prize', + 'prizes', + 'trophy', + 'trophies', + 'spot', + 'best', + 'champion', + 'hero', + 'jewels', + 'gems', + 'jewel', + 'jewelry', + 'treasure', + 'trump', + 'grand', + 'expensive', + 'fancy' + ]), + Emoji( + name: 'credit card', + char: '\u{1F4B3}', + shortName: 'credit_card', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.money, + keywords: [ + 'bank', + 'card', + 'credit', + 'money', + 'uc6', + 'money', + 'vacation', + 'boys night', + 'rich', + 'purchase', + 'cash', + 'dollars', + 'dollar', + 'bucks', + 'currency', + 'funds', + 'payment', + 'money face', + 'reward', + 'thief', + 'bank', + 'benjamins', + 'argent', + 'dinero', + 'i soldi', + 'Geld', + 'guys night', + 'grand', + 'expensive', + 'fancy', + 'buy', + 'shop', + 'spend' + ]), + Emoji( + name: 'gem stone', + char: '\u{1F48E}', + shortName: 'gem', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.clothing, + keywords: [ + 'diamond', + 'gem', + 'jewel', + 'uc6', + 'bling', + 'minecraft', + 'diamond', + 'rich', + 'sparkle', + 'jewels', + 'gems', + 'jewel', + 'jewelry', + 'treasure', + 'grand', + 'expensive', + 'fancy', + 'bright', + 'shine', + 'twinkle' + ]), + Emoji( + name: 'balance scale', + char: '\u{2696}\u{FE0F}', + shortName: 'scales', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.tool, + keywords: [ + 'Libra', + 'balance', + 'justice', + 'scales', + 'tool', + 'weight', + 'zodiac', + 'uc4', + 'tool', + 'science', + 'poison', + 'measure', + 'tools', + 'lab', + 'toxic', + 'toxins' + ]), + Emoji( + name: 'ladder', + char: '\u{1FA9C}', + shortName: 'ladder', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.tool, + keywords: ['uc13', 'tool', 'household', 'climb', 'tools']), + Emoji( + name: 'toolbox', + char: '\u{1F9F0}', + shortName: 'toolbox', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.tool, + keywords: ['uc11', 'tool', 'household', 'build', 'tools']), + Emoji( + name: 'screwdriver', + char: '\u{1FA9B}', + shortName: 'screwdriver', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.tool, + keywords: [ + 'uc13', + 'tool', + 'household', + 'build', + 'phillips', + 'tools', + 'flat tip', + 'flat head', + 'spiral ratchet', + 'ratchet', + 'slot head', + 'torx', + 'star head', + 'hex key' + ]), + Emoji( + name: 'wrench', + char: '\u{1F527}', + shortName: 'wrench', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.tool, + keywords: [ + 'spanner', + 'tool', + 'wrench', + 'uc6', + 'tool', + 'steel', + 'build', + 'tools', + 'metal' + ]), + Emoji( + name: 'hammer', + char: '\u{1F528}', + shortName: 'hammer', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.tool, + keywords: [ + 'tool', + 'uc6', + 'tool', + 'weapon', + 'steel', + 'household', + 'build', + 'tools', + 'weapons', + 'metal' + ]), + Emoji( + name: 'hammer and pick', + char: '\u{2692}\u{FE0F}', + shortName: 'hammer_pick', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.tool, + keywords: [ + 'hammer', + 'pick', + 'tool', + 'uc4', + 'tool', + 'weapon', + 'minecraft', + 'steel', + 'build', + 'chop', + 'tools', + 'weapons', + 'metal' + ]), + Emoji( + name: 'hammer and wrench', + char: '\u{1F6E0}\u{FE0F}', + shortName: 'tools', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.tool, + keywords: [ + 'hammer', + 'spanner', + 'tool', + 'wrench', + 'uc7', + 'tool', + 'steel', + 'build', + 'tools', + 'metal' + ]), + Emoji( + name: 'pick', + char: '\u{26CF}\u{FE0F}', + shortName: 'pick', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.tool, + keywords: [ + 'mining', + 'tool', + 'uc5', + 'tool', + 'weapon', + 'farm', + 'viking', + 'minecraft', + 'killer', + 'steel', + 'build', + 'chop', + 'shinobi', + 'tools', + 'weapons', + 'knight', + 'savage', + 'scary clown', + 'metal', + 'samurai' + ]), + Emoji( + name: 'nut and bolt', + char: '\u{1F529}', + shortName: 'nut_and_bolt', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.tool, + keywords: [ + 'bolt', + 'nut', + 'tool', + 'uc6', + 'tool', + 'nutcase', + 'steel', + 'build', + 'tools', + 'metal' + ]), + Emoji( + name: 'gear', + char: '\u{2699}\u{FE0F}', + shortName: 'gear', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.tool, + keywords: ['tool', 'uc4', 'tool', 'steel', 'tools', 'metal']), + Emoji( + name: 'brick', + char: '\u{1F9F1}', + shortName: 'bricks', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeBuilding, + keywords: [ + 'uc11', + 'house', + 'trap', + 'donald trump', + 'private', + 'household', + 'build', + 'block', + 'houses', + 'apartment', + 'apartments', + 'casa', + 'maison', + 'home', + 'trump', + 'прив', + 'privé', + 'privado', + 'reserved' + ]), + Emoji( + name: 'chains', + char: '\u{26D3}\u{FE0F}', + shortName: 'chains', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.tool, + keywords: [ + 'chain', + 'uc5', + 'tool', + 'halloween', + 'steel', + 'shinobi', + 'tools', + 'samhain', + 'metal', + 'samurai' + ]), + Emoji( + name: 'hook', + char: '\u{1FA9D}', + shortName: 'hook', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.tool, + keywords: ['uc13', 'tool', 'steel', 'tools', 'metal']), + Emoji( + name: 'knot', + char: '\u{1FAA2}', + shortName: 'knot', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.artsCrafts, + keywords: [ + 'uc13', + 'boat', + 'rock climbing', + 'build', + 'rope', + 'tie', + 'boats', + 'boating', + 'climber', + 'cordage', + 'hitches', + 'bends', + 'splices', + 'loop' + ]), + Emoji( + name: 'magnet', + char: '\u{1F9F2}', + shortName: 'magnet', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.tool, + keywords: ['uc11', 'science', 'magnet', 'household', 'lab']), + Emoji( + name: 'pistol', + char: '\u{1F52B}', + shortName: 'gun', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.tool, + keywords: [ + 'gun', + 'handgun', + 'revolver', + 'tool', + 'weapon', + 'uc6', + 'weapon', + 'angry', + 'dead', + 'gun', + 'sarcastic', + 'deadpool', + 'danger', + 'soldier', + 'texas', + 'summer', + 'killer', + 'hunt', + 'shot', + 'war', + 'independence day', + 'weapons', + 'upset', + 'pissed', + 'pissed off', + 'unhappy', + 'frustrated', + 'anger', + 'rage', + 'frustration', + 'furious', + 'mad', + 'death', + 'die', + 'dying', + 'fart', + 'goth', + 'grave', + 'headstone', + 'horror', + 'hurt', + 'kill', + 'murder', + 'tomb', + 'toot', + 'died', + 'guns', + 'trigger', + 'sarcasm', + 'warn', + 'attention', + 'caution', + 'alert', + 'error', + 'panic', + 'restricted', + "don't", + 'dont', + 'dangerous', + 'weekend', + 'savage', + 'scary clown', + '4th of july' + ]), + Emoji( + name: 'bomb', + char: '\u{1F4A3}', + shortName: 'bomb', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.emotion, + keywords: [ + 'comic', + 'uc6', + 'weapon', + 'dead', + 'blast', + 'explosion', + 'deadpool', + 'power', + 'danger', + 'minecraft', + 'throw', + 'killer', + 'war', + 'weapons', + 'death', + 'die', + 'dying', + 'fart', + 'goth', + 'grave', + 'headstone', + 'horror', + 'hurt', + 'kill', + 'murder', + 'tomb', + 'toot', + 'died', + 'boom', + 'explode', + 'warn', + 'attention', + 'caution', + 'alert', + 'error', + 'panic', + 'restricted', + "don't", + 'dont', + 'dangerous', + 'savage', + 'scary clown' + ]), + Emoji( + name: 'firecracker', + char: '\u{1F9E8}', + shortName: 'firecracker', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.event, + keywords: [ + 'uc11', + 'weapon', + 'blast', + 'danger', + 'chinese', + 'throw', + 'war', + 'independence day', + 'shinobi', + 'weapons', + 'boom', + 'warn', + 'attention', + 'caution', + 'alert', + 'error', + 'panic', + 'restricted', + "don't", + 'dont', + 'dangerous', + 'chinois', + 'asian', + 'chine', + '4th of july', + 'samurai' + ]), + Emoji( + name: 'axe', + char: '\u{1FA93}', + shortName: 'axe', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.tool, + keywords: [ + 'uc12', + 'tool', + 'weapon', + 'halloween', + 'danger', + 'killer', + 'steel', + 'chopper', + 'knives', + 'chop', + 'tools', + 'weapons', + 'samhain', + 'warn', + 'attention', + 'caution', + 'alert', + 'error', + 'panic', + 'restricted', + "don't", + 'dont', + 'dangerous', + 'savage', + 'scary clown', + 'metal', + 'hatchet', + 'adz', + 'tomahawk' + ]), + Emoji( + name: 'carpentry saw', + char: '\u{1FA9A}', + shortName: 'carpentry_saw', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.tool, + keywords: [ + 'uc13', + 'tool', + 'weapon', + 'steel', + 'chop', + 'tools', + 'weapons', + 'metal' + ]), + Emoji( + name: 'kitchen knife', + char: '\u{1F52A}', + shortName: 'knife', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.dishware, + keywords: [ + 'cooking', + 'hocho', + 'knife', + 'tool', + 'weapon', + 'uc6', + 'tool', + 'weapon', + 'blood', + 'danger', + 'cutlery', + 'minecraft', + 'crazy', + 'killer', + 'hunt', + 'steel', + 'utensils', + 'knives', + 'chop', + 'tools', + 'weapons', + 'sangre', + 'sang', + 'blut', + 'sangue', + 'warn', + 'attention', + 'caution', + 'alert', + 'error', + 'panic', + 'restricted', + "don't", + 'dont', + 'dangerous', + 'dish', + 'weird', + 'awkward', + 'insane', + 'wild', + 'savage', + 'scary clown', + 'metal' + ]), + Emoji( + name: 'dagger', + char: '\u{1F5E1}\u{FE0F}', + shortName: 'dagger', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.tool, + keywords: [ + 'knife', + 'weapon', + 'uc7', + 'weapon', + 'halloween', + 'blood', + 'viking', + 'killer', + 'hunt', + 'steel', + 'knives', + 'weapons', + 'samhain', + 'sangre', + 'sang', + 'blut', + 'sangue', + 'knight', + 'savage', + 'scary clown', + 'metal' + ]), + Emoji( + name: 'crossed swords', + char: '\u{2694}\u{FE0F}', + shortName: 'crossed_swords', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.tool, + keywords: [ + 'crossed', + 'swords', + 'weapon', + 'uc4', + 'weapon', + 'japan', + 'dead', + 'deadpool', + 'danger', + 'viking', + 'minecraft', + 'killer', + 'steel', + 'war', + 'knives', + 'shinobi', + 'weapons', + 'japanese', + 'ninja', + 'death', + 'die', + 'dying', + 'fart', + 'goth', + 'grave', + 'headstone', + 'horror', + 'hurt', + 'kill', + 'murder', + 'tomb', + 'toot', + 'died', + 'warn', + 'attention', + 'caution', + 'alert', + 'error', + 'panic', + 'restricted', + "don't", + 'dont', + 'dangerous', + 'knight', + 'savage', + 'scary clown', + 'metal', + 'samurai' + ]), + Emoji( + name: 'shield', + char: '\u{1F6E1}\u{FE0F}', + shortName: 'shield', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.tool, + keywords: [ + 'weapon', + 'uc7', + 'harry potter', + 'viking', + 'minecraft', + 'knight' + ]), + Emoji( + name: 'cigarette', + char: '\u{1F6AC}', + shortName: 'smoking', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.otherObject, + keywords: [ + 'smoking', + 'uc6', + 'drugs', + 'smoking', + 'danger', + 'poison', + 'killer', + 'drug', + 'narcotics', + 'smoke', + 'cigarette', + 'puff', + 'warn', + 'attention', + 'caution', + 'alert', + 'error', + 'panic', + 'restricted', + "don't", + 'dont', + 'dangerous', + 'toxic', + 'toxins', + 'savage', + 'scary clown' + ]), + Emoji( + name: 'coffin', + char: '\u{26B0}\u{FE0F}', + shortName: 'coffin', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.otherObject, + keywords: [ + 'death', + 'uc4', + 'halloween', + 'dead', + 'rip', + 'condolence', + 'killer', + 'war', + 'covid', + 'samhain', + 'death', + 'die', + 'dying', + 'fart', + 'goth', + 'grave', + 'headstone', + 'horror', + 'hurt', + 'kill', + 'murder', + 'tomb', + 'toot', + 'died', + 'rest in peace', + 'compassion', + 'savage', + 'scary clown' + ]), + Emoji( + name: 'headstone', + char: '\u{1FAA6}', + shortName: 'headstone', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.otherObject, + keywords: [ + 'uc13', + 'halloween', + 'dead', + 'killer', + 'war', + 'memorial', + 'covid', + 'tombstone', + 'samhain', + 'death', + 'die', + 'dying', + 'fart', + 'goth', + 'grave', + 'headstone', + 'horror', + 'hurt', + 'kill', + 'murder', + 'tomb', + 'toot', + 'died', + 'savage', + 'scary clown' + ]), + Emoji( + name: 'funeral urn', + char: '\u{26B1}\u{FE0F}', + shortName: 'urn', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.otherObject, + keywords: [ + 'ashes', + 'death', + 'funeral', + 'urn', + 'uc4', + 'halloween', + 'dead', + 'rip', + 'condolence', + 'covid', + 'samhain', + 'death', + 'die', + 'dying', + 'fart', + 'goth', + 'grave', + 'headstone', + 'horror', + 'hurt', + 'kill', + 'murder', + 'tomb', + 'toot', + 'died', + 'rest in peace', + 'compassion' + ]), + Emoji( + name: 'amphora', + char: '\u{1F3FA}', + shortName: 'amphora', + emojiGroup: EmojiGroup.foodDrink, + emojiSubgroup: EmojiSubgroup.dishware, + keywords: [ + 'Aquarius', + 'cooking', + 'drink', + 'jug', + 'tool', + 'weapon', + 'zodiac', + 'uc8', + 'bling', + 'jewels', + 'gems', + 'jewel', + 'jewelry', + 'treasure' + ]), + Emoji( + name: 'magic wand', + char: '\u{1FA84}', + shortName: 'magic_wand', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.game, + keywords: [ + 'uc13', + 'harry potter', + 'disney', + 'wizard', + 'cartoon', + 'Sorcerer', + 'Sorceress', + 'witch' + ]), + Emoji( + name: 'crystal ball', + char: '\u{1F52E}', + shortName: 'crystal_ball', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.game, + keywords: [ + 'ball', + 'crystal', + 'fairy tale', + 'fantasy', + 'fortune', + 'tool', + 'uc6', + 'halloween', + 'ball', + 'harry potter', + 'magic', + 'disney', + 'bling', + 'mirror', + 'future', + 'mystery', + 'wizard', + 'fantasy', + 'energy', + 'snow white', + 'samhain', + 'balls', + 'ballon', + 'spell', + 'genie', + 'magical', + 'cartoon', + 'jewels', + 'gems', + 'jewel', + 'jewelry', + 'treasure', + 'Sorcerer', + 'Sorceress', + 'witch' + ]), + Emoji( + name: 'prayer beads', + char: '\u{1F4FF}', + shortName: 'prayer_beads', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.clothing, + keywords: [ + 'beads', + 'clothing', + 'necklace', + 'prayer', + 'religion', + 'uc8', + 'religion', + 'rosary', + 'pray', + 'jesus', + 'bible', + 'prayer', + 'praying', + 'prayers', + 'grateful', + 'sorry', + 'heaven', + 'bless', + 'faith', + 'holy', + 'spirit', + 'hopeful', + 'blessed', + 'preach', + 'offering' + ]), + Emoji( + name: 'nazar amulet', + char: '\u{1F9FF}', + shortName: 'nazar_amulet', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.game, + keywords: [ + 'uc11', + 'game', + 'eyes', + 'luck', + 'magic', + 'evil', + 'fantasy', + 'eye bead', + 'games', + 'gaming', + 'eye', + 'eyebrow', + 'good luck', + 'lucky', + 'spell', + 'genie', + 'magical', + 'imp', + 'demon', + 'devil', + 'naughty', + 'devilish', + 'diablo', + 'diable', + 'satan', + 'Nazar Boncuğu', + 'Munçuk', + 'turkish' + ]), + Emoji( + name: 'barber pole', + char: '\u{1F488}', + shortName: 'barber', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.placeOther, + keywords: ['barber', 'haircut', 'pole', 'uc6']), + Emoji( + name: 'alembic', + char: '\u{2697}\u{FE0F}', + shortName: 'alembic', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.science, + keywords: [ + 'chemistry', + 'tool', + 'uc4', + 'classroom', + 'science', + 'poison', + 'minecraft', + 'measure', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'lab', + 'toxic', + 'toxins' + ]), + Emoji( + name: 'telescope', + char: '\u{1F52D}', + shortName: 'telescope', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.science, + keywords: [ + 'science', + 'tool', + 'uc6', + 'space', + 'star', + 'science', + 'search', + 'outer space', + 'galaxy', + 'universe', + 'nasa', + 'spaceship', + 'stars', + 'lab', + 'look', + 'find', + 'looking', + 'see' + ]), + Emoji( + name: 'microscope', + char: '\u{1F52C}', + shortName: 'microscope', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.science, + keywords: [ + 'science', + 'tool', + 'uc6', + 'classroom', + 'science', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'lab' + ]), + Emoji( + name: 'hole', + char: '\u{1F573}\u{FE0F}', + shortName: 'hole', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.emotion, + keywords: ['hole', 'uc7', 'trap']), + Emoji( + name: 'window', + char: '\u{1FA9F}', + shortName: 'window', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.household, + keywords: [ + 'uc13', + 'house', + 'sky', + 'day', + 'household', + 'daydream', + 'houses', + 'apartment', + 'apartments', + 'casa', + 'maison', + 'home' + ]), + Emoji( + name: 'adhesive bandage', + char: '\u{1FA79}', + shortName: 'adhesive_bandage', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.medical, + keywords: [ + 'uc12', + 'health', + '911', + 'nurse', + 'bandaid', + 'medical', + 'medicine', + 'doctor', + 'emergency', + 'injury' + ]), + Emoji( + name: 'stethoscope', + char: '\u{1FA7A}', + shortName: 'stethoscope', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.medical, + keywords: [ + 'uc12', + 'health', + '911', + 'nurse', + 'heart', + 'auscultation', + 'covid', + 'medical', + 'medicine', + 'doctor', + 'emergency', + 'injury', + 'hearts', + 'serce', + 'corazón', + 'coração', + 'coeur' + ]), + Emoji( + name: 'pill', + char: '\u{1F48A}', + shortName: 'pill', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.medical, + keywords: [ + 'doctor', + 'medicine', + 'sick', + 'uc6', + 'drugs', + 'health', + 'nurse', + 'poison', + 'killer', + 'medical', + 'drug', + 'narcotics', + 'medicine', + 'doctor', + 'toxic', + 'toxins', + 'savage', + 'scary clown' + ]), + Emoji( + name: 'syringe', + char: '\u{1F489}', + shortName: 'syringe', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.medical, + keywords: [ + 'doctor', + 'medicine', + 'needle', + 'shot', + 'sick', + 'tool', + 'uc6', + 'weapon', + 'drugs', + 'dead', + 'health', + '911', + 'blood', + 'nurse', + 'poison', + 'killer', + 'shot', + 'needle', + 'bleed', + 'covid', + 'medical', + 'weapons', + 'drug', + 'narcotics', + 'death', + 'die', + 'dying', + 'fart', + 'goth', + 'grave', + 'headstone', + 'horror', + 'hurt', + 'kill', + 'murder', + 'tomb', + 'toot', + 'died', + 'medicine', + 'doctor', + 'emergency', + 'injury', + 'sangre', + 'sang', + 'blut', + 'sangue', + 'toxic', + 'toxins', + 'savage', + 'scary clown', + 'donation', + 'menstruation' + ]), + Emoji( + name: 'drop of blood', + char: '\u{1FA78}', + shortName: 'drop_of_blood', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.medical, + keywords: [ + 'uc12', + 'body', + 'science', + 'health', + '911', + 'blood', + 'vampire', + 'shot', + 'bleed', + 'medical', + 'body part', + 'anatomy', + 'lab', + 'medicine', + 'doctor', + 'emergency', + 'injury', + 'sangre', + 'sang', + 'blut', + 'sangue', + 'dracula', + 'donation', + 'menstruation' + ]), + Emoji( + name: 'dna', + char: '\u{1F9EC}', + shortName: 'dna', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.science, + keywords: [ + 'uc11', + 'family', + 'body', + 'science', + 'blood', + 'history', + 'future', + 'deoxyribonucleic acid', + 'medical', + 'families', + 'group', + 'brother', + 'sister', + 'daughter', + 'son', + 'together', + 'sibling', + 'twins', + 'brothers', + 'sisters', + 'body part', + 'anatomy', + 'lab', + 'sangre', + 'sang', + 'blut', + 'sangue', + 'ancient', + 'old', + 'gene', + 'genetic code', + 'RNA', + 'chromosome', + 'heredity', + 'nucleic acid' + ]), + Emoji( + name: 'microbe', + char: '\u{1F9A0}', + shortName: 'microbe', + emojiGroup: EmojiGroup.animalsNature, + emojiSubgroup: EmojiSubgroup.animalBug, + keywords: [ + 'uc11', + 'body', + 'science', + 'stinky', + 'bacteria', + 'booger', + 'virus', + 'covid', + 'medical', + 'body part', + 'anatomy', + 'lab', + 'smell', + 'stink', + 'odor', + 'microorganism', + 'bacterium', + 'corona' + ]), + Emoji( + name: 'petri dish', + char: '\u{1F9EB}', + shortName: 'petri_dish', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.science, + keywords: [ + 'uc11', + 'classroom', + 'science', + 'bacteria', + 'petrie dish', + 'mushroom', + 'virus', + 'covid', + 'medical', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'lab', + 'microorganism', + 'bacterium', + 'petri plate', + 'cell culture dish', + 'moss', + 'corona' + ]), + Emoji( + name: 'test tube', + char: '\u{1F9EA}', + shortName: 'test_tube', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.science, + keywords: [ + 'uc11', + 'science', + 'poison', + 'test-tube', + 'measure', + 'medical', + 'lab', + 'toxic', + 'toxins', + 'culture tube', + 'sample tube' + ]), + Emoji( + name: 'thermometer', + char: '\u{1F321}\u{FE0F}', + shortName: 'thermometer', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.skyWeather, + keywords: [ + 'weather', + 'uc7', + 'science', + 'health', + 'hot', + 'virus', + 'measure', + 'medical', + 'lab', + 'medicine', + 'doctor', + 'heat', + 'warm', + 'caliente', + 'chaud', + 'heiß', + 'corona' + ]), + Emoji( + name: 'mouse trap', + char: '\u{1FAA4}', + shortName: 'mouse_trap', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.household, + keywords: ['uc13', 'trap', 'household', 'rodent']), + Emoji( + name: 'broom', + char: '\u{1F9F9}', + shortName: 'broom', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.household, + keywords: ['uc11', 'clean', 'sweep', 'household', 'dust', 'mop']), + Emoji( + name: 'basket', + char: '\u{1F9FA}', + shortName: 'basket', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.household, + keywords: [ + 'uc11', + 'household', + 'sew', + 'knit', + 'embroider', + 'stitch', + 'repair', + 'crochet', + 'alter', + 'seamstress', + 'fix' + ]), + Emoji( + name: 'sewing needle', + char: '\u{1FAA1}', + shortName: 'sewing_needle', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.artsCrafts, + keywords: [ + 'uc13', + 'bathroom', + 'needle', + 'household', + 'sew', + 'knit', + 'embroider', + 'stitch', + 'repair', + 'crochet', + 'alter', + 'seamstress', + 'fix' + ]), + Emoji( + name: 'roll of paper', + char: '\u{1F9FB}', + shortName: 'roll_of_paper', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.household, + keywords: [ + 'uc11', + 'bathroom', + 'diarrhea', + 'shit', + 'clean', + 'household', + 'shits', + 'the shits', + 'poop', + 'turd', + 'feces', + 'pile', + 'merde', + 'butthole', + 'caca', + 'crap', + 'dirty', + 'pooo', + 'mess', + 'brown', + 'poopoo' + ]), + Emoji( + name: 'toilet', + char: '\u{1F6BD}', + shortName: 'toilet', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.household, + keywords: [ + 'toilet', + 'uc6', + 'bathroom', + 'sick', + 'diarrhea', + 'shit', + 'private', + 'household', + 'throne', + 'barf', + 'vomit', + 'throw up', + 'puke', + 'get well', + 'cough', + 'puking', + 'barfing', + 'malade', + 'spew', + 'shits', + 'the shits', + 'poop', + 'turd', + 'feces', + 'pile', + 'merde', + 'butthole', + 'caca', + 'crap', + 'dirty', + 'pooo', + 'mess', + 'brown', + 'poopoo', + 'прив', + 'privé', + 'privado', + 'reserved' + ]), + Emoji( + name: 'plunger', + char: '\u{1FAA0}', + shortName: 'plunger', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.household, + keywords: [ + 'uc13', + 'bathroom', + 'shit', + 'household', + 'poop', + 'turd', + 'feces', + 'pile', + 'merde', + 'butthole', + 'caca', + 'crap', + 'dirty', + 'pooo', + 'mess', + 'brown', + 'poopoo' + ]), + Emoji( + name: 'bucket', + char: '\u{1FAA3}', + shortName: 'bucket', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.household, + keywords: ['uc13', 'household', 'pail', 'vessel']), + Emoji( + name: 'potable water', + char: '\u{1F6B0}', + shortName: 'potable_water', + emojiGroup: EmojiGroup.symbols, + emojiSubgroup: EmojiSubgroup.transportSign, + keywords: [ + 'drinking', + 'potable', + 'water', + 'uc6', + 'drip', + 'water', + 'household', + 'water drop' + ]), + Emoji( + name: 'shower', + char: '\u{1F6BF}', + shortName: 'shower', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.household, + keywords: [ + 'water', + 'uc6', + 'bathroom', + 'clean', + 'wash', + 'shower', + 'bathe', + 'bathing', + 'washing' + ]), + Emoji( + name: 'bathtub', + char: '\u{1F6C1}', + shortName: 'bathtub', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.household, + keywords: [ + 'bath', + 'uc6', + 'bathroom', + 'steam', + 'clean', + 'wash', + 'steaming', + 'piping', + 'shower', + 'bathe', + 'bathing', + 'washing' + ]), + Emoji( + name: 'person taking bath', + char: '\u{1F6C0}', + shortName: 'bath', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personResting, + keywords: [ + 'bath', + 'bathtub', + 'uc6', + 'diversity', + 'bathroom', + 'steam', + 'clean', + 'wash', + 'spa', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'steaming', + 'piping', + 'shower', + 'bathe', + 'bathing', + 'washing', + 'relax', + 'sauna' + ]), + Emoji( + name: 'person taking bath: light skin tone', + char: '\u{1F6C0}\u{1F3FB}', + shortName: 'bath_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personResting, + keywords: [ + 'bath', + 'bathtub', + 'light skin tone', + 'uc8', + 'diversity', + 'bathroom', + 'steam', + 'clean', + 'wash', + 'spa', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'steaming', + 'piping', + 'shower', + 'bathe', + 'bathing', + 'washing', + 'relax', + 'sauna' + ], + modifiable: true), + Emoji( + name: 'person taking bath: medium-light skin tone', + char: '\u{1F6C0}\u{1F3FC}', + shortName: 'bath_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personResting, + keywords: [ + 'bath', + 'bathtub', + 'medium-light skin tone', + 'uc8', + 'diversity', + 'bathroom', + 'steam', + 'clean', + 'wash', + 'spa', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'steaming', + 'piping', + 'shower', + 'bathe', + 'bathing', + 'washing', + 'relax', + 'sauna' + ], + modifiable: true), + Emoji( + name: 'person taking bath: medium skin tone', + char: '\u{1F6C0}\u{1F3FD}', + shortName: 'bath_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personResting, + keywords: [ + 'bath', + 'bathtub', + 'medium skin tone', + 'uc8', + 'diversity', + 'bathroom', + 'steam', + 'clean', + 'wash', + 'spa', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'steaming', + 'piping', + 'shower', + 'bathe', + 'bathing', + 'washing', + 'relax', + 'sauna' + ], + modifiable: true), + Emoji( + name: 'person taking bath: medium-dark skin tone', + char: '\u{1F6C0}\u{1F3FE}', + shortName: 'bath_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personResting, + keywords: [ + 'bath', + 'bathtub', + 'medium-dark skin tone', + 'uc8', + 'diversity', + 'bathroom', + 'steam', + 'clean', + 'wash', + 'spa', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'steaming', + 'piping', + 'shower', + 'bathe', + 'bathing', + 'washing', + 'relax', + 'sauna' + ], + modifiable: true), + Emoji( + name: 'person taking bath: dark skin tone', + char: '\u{1F6C0}\u{1F3FF}', + shortName: 'bath_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personResting, + keywords: [ + 'bath', + 'bathtub', + 'dark skin tone', + 'uc8', + 'diversity', + 'bathroom', + 'steam', + 'clean', + 'wash', + 'spa', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'steaming', + 'piping', + 'shower', + 'bathe', + 'bathing', + 'washing', + 'relax', + 'sauna' + ], + modifiable: true), + Emoji( + name: 'toothbrush', + char: '\u{1FAA5}', + shortName: 'toothbrush', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.household, + keywords: ['uc13', 'bathroom']), + Emoji( + name: 'soap', + char: '\u{1F9FC}', + shortName: 'soap', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.household, + keywords: [ + 'uc11', + 'bathroom', + 'health', + 'pink', + 'clean', + 'wash', + 'household', + 'dishes', + 'savon', + 'covid', + 'medicine', + 'doctor', + 'rose', + 'shower', + 'bathe', + 'bathing', + 'washing' + ]), + Emoji( + name: 'razor', + char: '\u{1FA92}', + shortName: 'razor', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.household, + keywords: [ + 'uc12', + 'weapon', + 'mustache', + 'beard', + 'blade', + 'weapons', + 'shave', + 'trim' + ]), + Emoji( + name: 'sponge', + char: '\u{1F9FD}', + shortName: 'sponge', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.household, + keywords: [ + 'uc11', + 'bathroom', + 'clean', + 'wash', + 'household', + 'dishes', + 'shower', + 'bathe', + 'bathing', + 'washing' + ]), + Emoji( + name: 'lotion bottle', + char: '\u{1F9F4}', + shortName: 'squeeze_bottle', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.household, + keywords: [ + 'uc11', + 'bathroom', + 'beach', + 'clean', + 'wash', + 'picnic', + 'household', + 'dishes', + 'savon', + 'covid', + 'shower', + 'bathe', + 'bathing', + 'washing' + ]), + Emoji( + name: 'bellhop bell', + char: '\u{1F6CE}\u{FE0F}', + shortName: 'bellhop', + emojiGroup: EmojiGroup.travelPlaces, + emojiSubgroup: EmojiSubgroup.hotel, + keywords: [ + 'bell', + 'bellhop', + 'hotel', + 'uc7', + 'vacation', + 'help', + 'suitcase', + 'hotel', + 'carry-on', + 'vacancy', + 'no vacancy' + ]), + Emoji( + name: 'key', + char: '\u{1F511}', + shortName: 'key', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.lock, + keywords: [ + 'lock', + 'password', + 'uc6', + 'lock', + 'household', + 'locks', + 'key', + 'keys' + ]), + Emoji( + name: 'old key', + char: '\u{1F5DD}\u{FE0F}', + shortName: 'key2', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.lock, + keywords: [ + 'clue', + 'key', + 'lock', + 'old', + 'uc7', + 'lock', + 'harry potter', + 'household', + 'locks', + 'key', + 'keys' + ]), + Emoji( + name: 'door', + char: '\u{1F6AA}', + shortName: 'door', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.household, + keywords: [ + 'door', + 'uc6', + 'minecraft', + 'hotel', + 'household', + 'vacancy', + 'no vacancy' + ]), + Emoji( + name: 'chair', + char: '\u{1FA91}', + shortName: 'chair', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.household, + keywords: [ + 'uc12', + 'household', + 'sit', + 'seat', + 'throne', + 'sitting', + 'kneel', + 'kneeling', + 'bench', + 'sedia', + 'Stuhl', + 'chaise', + 'silla', + 'armchair' + ]), + Emoji( + name: 'mirror', + char: '\u{1FA9E}', + shortName: 'mirror', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.household, + keywords: [ + 'uc13', + 'bathroom', + 'beautiful', + 'disney', + 'mirror', + 'household', + 'snow white', + 'cute', + 'pretty', + 'adorable', + 'adore', + 'beauty', + 'cutie', + 'babe', + 'lovely', + 'cartoon' + ]), + Emoji( + name: 'couch and lamp', + char: '\u{1F6CB}\u{FE0F}', + shortName: 'couch', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.household, + keywords: [ + 'couch', + 'hotel', + 'lamp', + 'uc7', + 'tired', + 'light', + 'sofa', + 'hotel', + 'household', + 'seat', + 'sleepy', + 'sleep', + 'dormi', + 'pillow', + 'blanket', + 'exhausted', + 'lamp', + 'light bulb', + 'flashlight', + 'spotlight', + 'illuminate', + 'lightbulb', + 'lighting', + 'luce', + 'licht', + 'lumière', + 'luz', + 'vacancy', + 'no vacancy', + 'bench', + 'sedia', + 'Stuhl', + 'chaise', + 'silla', + 'armchair' + ]), + Emoji( + name: 'bed', + char: '\u{1F6CF}\u{FE0F}', + shortName: 'bed', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.household, + keywords: [ + 'hotel', + 'sleep', + 'uc7', + 'tired', + 'hotel', + 'household', + 'sleepy', + 'sleep', + 'dormi', + 'pillow', + 'blanket', + 'exhausted', + 'vacancy', + 'no vacancy' + ]), + Emoji( + name: 'person in bed', + char: '\u{1F6CC}', + shortName: 'sleeping_accommodation', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personResting, + keywords: [ + 'hotel', + 'sleep', + 'uc7', + 'diversity', + 'tired', + 'lazy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'sleepy', + 'sleep', + 'dormi', + 'pillow', + 'blanket', + 'exhausted' + ]), + Emoji( + name: 'person in bed: light skin tone', + char: '\u{1F6CC}\u{1F3FB}', + shortName: 'person_in_bed_tone1', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personResting, + keywords: [ + 'hotel', + 'light skin tone', + 'sleep', + 'uc8', + 'diversity', + 'tired', + 'lazy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'sleepy', + 'sleep', + 'dormi', + 'pillow', + 'blanket', + 'exhausted' + ], + modifiable: true), + Emoji( + name: 'person in bed: medium-light skin tone', + char: '\u{1F6CC}\u{1F3FC}', + shortName: 'person_in_bed_tone2', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personResting, + keywords: [ + 'hotel', + 'medium-light skin tone', + 'sleep', + 'uc8', + 'diversity', + 'tired', + 'lazy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'sleepy', + 'sleep', + 'dormi', + 'pillow', + 'blanket', + 'exhausted' + ], + modifiable: true), + Emoji( + name: 'person in bed: medium skin tone', + char: '\u{1F6CC}\u{1F3FD}', + shortName: 'person_in_bed_tone3', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personResting, + keywords: [ + 'hotel', + 'medium skin tone', + 'sleep', + 'uc8', + 'diversity', + 'tired', + 'lazy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'sleepy', + 'sleep', + 'dormi', + 'pillow', + 'blanket', + 'exhausted' + ], + modifiable: true), + Emoji( + name: 'person in bed: medium-dark skin tone', + char: '\u{1F6CC}\u{1F3FE}', + shortName: 'person_in_bed_tone4', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personResting, + keywords: [ + 'hotel', + 'medium-dark skin tone', + 'sleep', + 'uc8', + 'diversity', + 'tired', + 'lazy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'sleepy', + 'sleep', + 'dormi', + 'pillow', + 'blanket', + 'exhausted' + ], + modifiable: true), + Emoji( + name: 'person in bed: dark skin tone', + char: '\u{1F6CC}\u{1F3FF}', + shortName: 'person_in_bed_tone5', + emojiGroup: EmojiGroup.peopleBody, + emojiSubgroup: EmojiSubgroup.personResting, + keywords: [ + 'dark skin tone', + 'hotel', + 'sleep', + 'uc8', + 'diversity', + 'tired', + 'lazy', + 'diverse', + 'modifier', + 'modifiers', + 'equality', + 'sleepy', + 'sleep', + 'dormi', + 'pillow', + 'blanket', + 'exhausted' + ], + modifiable: true), + Emoji( + name: 'teddy bear', + char: '\u{1F9F8}', + shortName: 'teddy_bear', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.game, + keywords: [ + 'uc11', + 'animal', + 'baby', + 'play', + 'gummy', + 'household', + 'stuffed animal', + 'toy', + 'animals', + 'animal kingdom', + 'kid', + 'babies', + 'infant', + 'infants', + 'crying kid', + 'bebe', + 'little', + 'petite', + 'bambino', + 'doudou' + ]), + Emoji( + name: 'framed picture', + char: '\u{1F5BC}\u{FE0F}', + shortName: 'frame_photo', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.artsCrafts, + keywords: [ + 'art', + 'frame', + 'museum', + 'painting', + 'picture', + 'uc7', + 'theatre', + 'travel', + 'vacation', + 'painting', + 'image', + 'instagram', + 'household', + 'theater', + 'craft', + 'drama', + 'monet', + 'painter', + 'arts' + ]), + Emoji( + name: 'shopping bags', + char: '\u{1F6CD}\u{FE0F}', + shortName: 'shopping_bags', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.clothing, + keywords: [ + 'bag', + 'hotel', + 'shopping', + 'uc7', + 'bag', + 'gift', + 'birthday', + 'happy birthday', + 'celebrate', + 'rich', + 'purchase', + 'swag', + 'present', + 'cadeau', + 'bows', + 'presents', + 'birth', + 'cumpleaños', + 'anniversaire', + 'bday', + 'Bon anniversaire', + 'joyeux anniversaire', + 'buon compleanno', + 'feliz cumpleaños', + 'alles Gute zum Geburtstag', + 'feliz Aniversário', + 'Gratulerer med dagen', + 'celebration', + 'event', + 'celebrating', + 'festa', + 'parties', + 'events', + 'new years', + 'new year', + 'fiesta', + 'fete', + 'newyear', + 'party', + 'festive', + 'festival', + 'yolo', + 'festejar', + 'grand', + 'expensive', + 'fancy', + 'buy', + 'shop', + 'spend' + ]), + Emoji( + name: 'shopping cart', + char: '\u{1F6D2}', + shortName: 'shopping_cart', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.household, + keywords: [ + 'cart', + 'shopping', + 'trolley', + 'uc9', + 'food', + 'purchase', + 'foods', + 'eat', + 'meal', + 'comida', + 'nourriture', + 'eats', + 'groceries', + 'grocery', + 'hungry', + 'tasty', + 'mmm', + 'yummy', + 'feed', + 'hunger', + 'buy', + 'shop', + 'spend' + ]), + Emoji( + name: 'wrapped gift', + char: '\u{1F381}', + shortName: 'gift', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.event, + keywords: [ + 'box', + 'celebration', + 'gift', + 'present', + 'wrapped', + 'uc6', + 'holidays', + 'love', + 'gift', + 'birthday', + 'christmas', + 'happy birthday', + 'celebrate', + 'holiday', + 'i love you', + 'te amo', + "je t'aime", + 'anniversary', + 'lovin', + 'amour', + 'aimer', + 'amor', + 'valentines day', + 'enamour', + 'lovey', + 'present', + 'cadeau', + 'bows', + 'presents', + 'birth', + 'cumpleaños', + 'anniversaire', + 'bday', + 'navidad', + 'xmas', + 'noel', + 'merry christmas', + 'Bon anniversaire', + 'joyeux anniversaire', + 'buon compleanno', + 'feliz cumpleaños', + 'alles Gute zum Geburtstag', + 'feliz Aniversário', + 'Gratulerer med dagen', + 'celebration', + 'event', + 'celebrating', + 'festa', + 'parties', + 'events', + 'new years', + 'new year', + 'fiesta', + 'fete', + 'newyear', + 'party', + 'festive', + 'festival', + 'yolo', + 'festejar' + ]), + Emoji( + name: 'balloon', + char: '\u{1F388}', + shortName: 'balloon', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.event, + keywords: [ + 'celebration', + 'uc6', + 'holidays', + 'baby', + 'birthday', + 'good', + 'balloons', + 'happy birthday', + 'celebrate', + 'independence day', + 'sperm', + 'toy', + 'holiday', + 'kid', + 'babies', + 'infant', + 'infants', + 'crying kid', + 'bebe', + 'little', + 'petite', + 'bambino', + 'birth', + 'cumpleaños', + 'anniversaire', + 'bday', + 'good job', + 'nice', + 'well done', + 'bravo', + 'congratulations', + 'congrats', + 'Bon anniversaire', + 'joyeux anniversaire', + 'buon compleanno', + 'feliz cumpleaños', + 'alles Gute zum Geburtstag', + 'feliz Aniversário', + 'Gratulerer med dagen', + 'celebration', + 'event', + 'celebrating', + 'festa', + 'parties', + 'events', + 'new years', + 'new year', + 'fiesta', + 'fete', + 'newyear', + 'party', + 'festive', + 'festival', + 'yolo', + 'festejar', + '4th of july' + ]), + Emoji( + name: 'carp streamer', + char: '\u{1F38F}', + shortName: 'flags', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.event, + keywords: [ + 'carp', + 'celebration', + 'streamer', + 'uc6', + 'japan', + 'japanese', + 'ninja' + ]), + Emoji( + name: 'ribbon', + char: '\u{1F380}', + shortName: 'ribbon', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.event, + keywords: [ + 'celebration', + 'uc6', + 'holidays', + 'love', + 'gift', + 'birthday', + 'accessories', + 'happy birthday', + 'celebrate', + 'rich', + 'holiday', + 'i love you', + 'te amo', + "je t'aime", + 'anniversary', + 'lovin', + 'amour', + 'aimer', + 'amor', + 'valentines day', + 'enamour', + 'lovey', + 'present', + 'cadeau', + 'bows', + 'presents', + 'birth', + 'cumpleaños', + 'anniversaire', + 'bday', + 'Bon anniversaire', + 'joyeux anniversaire', + 'buon compleanno', + 'feliz cumpleaños', + 'alles Gute zum Geburtstag', + 'feliz Aniversário', + 'Gratulerer med dagen', + 'celebration', + 'event', + 'celebrating', + 'festa', + 'parties', + 'events', + 'new years', + 'new year', + 'fiesta', + 'fete', + 'newyear', + 'party', + 'festive', + 'festival', + 'yolo', + 'festejar', + 'grand', + 'expensive', + 'fancy' + ]), + Emoji( + name: 'confetti ball', + char: '\u{1F38A}', + shortName: 'confetti_ball', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.event, + keywords: [ + 'ball', + 'celebration', + 'confetti', + 'uc6', + 'happy', + 'birthday', + 'cheers', + 'girls night', + 'boys night', + 'happy birthday', + 'confetti', + 'celebrate', + 'glitter', + 'hooray', + 'cheek', + 'cheeky', + 'excited', + 'feliz', + 'heureux', + 'cheerful', + 'delighted', + 'ecstatic', + 'elated', + 'glad', + 'joy', + 'merry', + 'birth', + 'cumpleaños', + 'anniversaire', + 'bday', + 'gān bēi', + 'Na zdravi', + 'Proost', + 'Prost', + 'Sláinte', + 'Cin cin', + 'Kanpai', + 'Na zdrowie', + 'Saúde', + 'На здоровье', + 'Salud', + 'Skål', + 'Sei gesund', + 'santé', + 'ladies night', + 'girls only', + 'girlfriend', + 'guys night', + 'Bon anniversaire', + 'joyeux anniversaire', + 'buon compleanno', + 'feliz cumpleaños', + 'alles Gute zum Geburtstag', + 'feliz Aniversário', + 'Gratulerer med dagen', + 'celebration', + 'event', + 'celebrating', + 'festa', + 'parties', + 'events', + 'new years', + 'new year', + 'fiesta', + 'fete', + 'newyear', + 'party', + 'festive', + 'festival', + 'yolo', + 'festejar' + ]), + Emoji( + name: 'party popper', + char: '\u{1F389}', + shortName: 'tada', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.event, + keywords: [ + 'celebration', + 'party', + 'popper', + 'tada', + 'uc6', + 'holidays', + 'happy', + 'birthday', + 'cheers', + 'good', + 'girls night', + 'boys night', + 'happy birthday', + 'confetti', + 'celebrate', + 'glitter', + 'bingo', + 'fame', + 'fun', + 'independence day', + 'holiday', + 'hooray', + 'cheek', + 'cheeky', + 'excited', + 'feliz', + 'heureux', + 'cheerful', + 'delighted', + 'ecstatic', + 'elated', + 'glad', + 'joy', + 'merry', + 'birth', + 'cumpleaños', + 'anniversaire', + 'bday', + 'gān bēi', + 'Na zdravi', + 'Proost', + 'Prost', + 'Sláinte', + 'Cin cin', + 'Kanpai', + 'Na zdrowie', + 'Saúde', + 'На здоровье', + 'Salud', + 'Skål', + 'Sei gesund', + 'santé', + 'good job', + 'nice', + 'well done', + 'bravo', + 'congratulations', + 'congrats', + 'ladies night', + 'girls only', + 'girlfriend', + 'guys night', + 'Bon anniversaire', + 'joyeux anniversaire', + 'buon compleanno', + 'feliz cumpleaños', + 'alles Gute zum Geburtstag', + 'feliz Aniversário', + 'Gratulerer med dagen', + 'celebration', + 'event', + 'celebrating', + 'festa', + 'parties', + 'events', + 'new years', + 'new year', + 'fiesta', + 'fete', + 'newyear', + 'party', + 'festive', + 'festival', + 'yolo', + 'festejar', + 'famous', + 'celebrity', + '4th of july' + ]), + Emoji( + name: 'piñata', + char: '\u{1FA85}', + shortName: 'piñata', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.game, + keywords: [ + 'uc13', + 'mexican', + 'birthday', + 'happy birthday', + 'celebrate', + 'pinata', + 'mexico', + 'cinco de mayo', + 'español', + 'birth', + 'cumpleaños', + 'anniversaire', + 'bday', + 'Bon anniversaire', + 'joyeux anniversaire', + 'buon compleanno', + 'feliz cumpleaños', + 'alles Gute zum Geburtstag', + 'feliz Aniversário', + 'Gratulerer med dagen', + 'celebration', + 'event', + 'celebrating', + 'festa', + 'parties', + 'events', + 'new years', + 'new year', + 'fiesta', + 'fete', + 'newyear', + 'party', + 'festive', + 'festival', + 'yolo', + 'festejar', + 'papier-mâché', + 'paper mache', + 'pignatta', + 'dahi handi', + 'fer cagar el tió', + 'suikawari', + 'pukpok-palayok', + 'cartonería' + ]), + Emoji( + name: 'nesting dolls', + char: '\u{1FA86}', + shortName: 'nesting_dolls', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.game, + keywords: [ + 'uc13', + 'russian', + 'toy', + 'matryoshka dolls', + 'babushka dolls', + 'stacking dolls', + 'russian dolls' + ]), + Emoji( + name: 'Japanese dolls', + char: '\u{1F38E}', + shortName: 'dolls', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.event, + keywords: [ + 'Japanese', + 'celebration', + 'doll', + 'festival', + 'uc6', + 'japan', + 'japanese', + 'ninja' + ]), + Emoji( + name: 'red paper lantern', + char: '\u{1F3EE}', + shortName: 'izakaya_lantern', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.lightVideo, + keywords: [ + 'bar', + 'lantern', + 'light', + 'red', + 'uc6', + 'japan', + 'japanese', + 'ninja' + ]), + Emoji( + name: 'wind chime', + char: '\u{1F390}', + shortName: 'wind_chime', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.event, + keywords: [ + 'bell', + 'celebration', + 'chime', + 'wind', + 'uc6', + 'japan', + 'japanese', + 'ninja' + ]), + Emoji( + name: 'red envelope', + char: '\u{1F9E7}', + shortName: 'red_envelope', + emojiGroup: EmojiGroup.activities, + emojiSubgroup: EmojiSubgroup.event, + keywords: [ + 'uc11', + 'gift', + 'chinese', + 'present', + 'cadeau', + 'bows', + 'presents', + 'chinois', + 'asian', + 'chine' + ]), + Emoji( + name: 'envelope', + char: '\u{2709}\u{FE0F}', + shortName: 'envelope', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.mail, + keywords: [ + 'email', + 'letter', + 'uc1', + 'write', + 'mail', + 'envelope', + 'work', + 'writing', + 'email', + 'post', + 'post office', + 'letter', + 'message', + 'offer', + 'office' + ]), + Emoji( + name: 'envelope with arrow', + char: '\u{1F4E9}', + shortName: 'envelope_with_arrow', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.mail, + keywords: [ + 'arrow', + 'down', + 'e-mail', + 'email', + 'envelope', + 'letter', + 'mail', + 'outgoing', + 'sent', + 'uc6', + 'mail', + 'envelope', + 'download', + 'work', + 'email', + 'post', + 'post office', + 'letter', + 'message', + 'offer', + 'office' + ]), + Emoji( + name: 'incoming envelope', + char: '\u{1F4E8}', + shortName: 'incoming_envelope', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.mail, + keywords: [ + 'e-mail', + 'email', + 'envelope', + 'incoming', + 'letter', + 'mail', + 'receive', + 'uc6', + 'mail', + 'envelope', + 'work', + 'email', + 'post', + 'post office', + 'letter', + 'message', + 'offer', + 'office' + ]), + Emoji( + name: 'e-mail', + char: '\u{1F4E7}', + shortName: 'e-mail', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.mail, + keywords: [ + 'email', + 'letter', + 'mail', + 'uc6', + 'classroom', + 'mail', + 'business', + 'envelope', + 'work', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'email', + 'post', + 'post office', + 'letter', + 'message', + 'offer', + 'office' + ]), + Emoji( + name: 'love letter', + char: '\u{1F48C}', + shortName: 'love_letter', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.emotion, + keywords: [ + 'heart', + 'letter', + 'love', + 'mail', + 'uc6', + 'love', + 'mail', + 'envelope', + 'pink', + 'i love you', + 'te amo', + "je t'aime", + 'anniversary', + 'lovin', + 'amour', + 'aimer', + 'amor', + 'valentines day', + 'enamour', + 'lovey', + 'email', + 'post', + 'post office', + 'letter', + 'message', + 'offer', + 'rose' + ]), + Emoji( + name: 'inbox tray', + char: '\u{1F4E5}', + shortName: 'inbox_tray', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.mail, + keywords: [ + 'box', + 'inbox', + 'letter', + 'mail', + 'receive', + 'tray', + 'uc6', + 'business', + 'envelope', + 'work', + 'letter', + 'message', + 'offer', + 'office' + ]), + Emoji( + name: 'outbox tray', + char: '\u{1F4E4}', + shortName: 'outbox_tray', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.mail, + keywords: [ + 'box', + 'letter', + 'mail', + 'outbox', + 'sent', + 'tray', + 'uc6', + 'business', + 'work', + 'office' + ]), + Emoji( + name: 'package', + char: '\u{1F4E6}', + shortName: 'package', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.mail, + keywords: [ + 'box', + 'parcel', + 'uc6', + 'classroom', + 'gift', + 'mail', + 'moving', + 'work', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'present', + 'cadeau', + 'bows', + 'presents', + 'email', + 'post', + 'post office', + 'office' + ]), + Emoji( + name: 'label', + char: '\u{1F3F7}\u{FE0F}', + shortName: 'label', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.bookPaper, + keywords: ['label', 'uc7', 'discount', 'price', 'sale', 'bargain']), + Emoji( + name: 'closed mailbox with lowered flag', + char: '\u{1F4EA}', + shortName: 'mailbox_closed', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.mail, + keywords: [ + 'closed', + 'lowered', + 'mail', + 'mailbox', + 'postbox', + 'uc6', + 'mail', + 'envelope', + 'household', + 'email', + 'post', + 'post office', + 'letter', + 'message', + 'offer' + ]), + Emoji( + name: 'closed mailbox with raised flag', + char: '\u{1F4EB}', + shortName: 'mailbox', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.mail, + keywords: [ + 'closed', + 'mail', + 'mailbox', + 'postbox', + 'uc6', + 'mail', + 'envelope', + 'email', + 'post', + 'post office', + 'letter', + 'message', + 'offer' + ]), + Emoji( + name: 'open mailbox with raised flag', + char: '\u{1F4EC}', + shortName: 'mailbox_with_mail', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.mail, + keywords: [ + 'mail', + 'mailbox', + 'open', + 'postbox', + 'uc6', + 'mail', + 'envelope', + 'household', + 'email', + 'post', + 'post office', + 'letter', + 'message', + 'offer' + ]), + Emoji( + name: 'open mailbox with lowered flag', + char: '\u{1F4ED}', + shortName: 'mailbox_with_no_mail', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.mail, + keywords: [ + 'lowered', + 'mail', + 'mailbox', + 'open', + 'postbox', + 'uc6', + 'mail', + 'envelope', + 'empty', + 'email', + 'post', + 'post office', + 'letter', + 'message', + 'offer' + ]), + Emoji( + name: 'postbox', + char: '\u{1F4EE}', + shortName: 'postbox', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.mail, + keywords: [ + 'mail', + 'mailbox', + 'uc6', + 'mail', + 'envelope', + 'email', + 'post', + 'post office', + 'letter', + 'message', + 'offer' + ]), + Emoji( + name: 'postal horn', + char: '\u{1F4EF}', + shortName: 'postal_horn', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.sound, + keywords: [ + 'horn', + 'post', + 'postal', + 'uc6', + 'instruments', + 'music', + 'instrument', + 'singing', + 'concert', + 'jaz', + 'listen', + 'singer', + 'song', + 'musique' + ]), + Emoji( + name: 'placard', + char: '\u{1FAA7}', + shortName: 'placard', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.otherObject, + keywords: [ + 'uc13', + 'peace', + 'protest', + 'peace out', + 'peace sign', + 'blm', + 'demonstration' + ]), + Emoji( + name: 'scroll', + char: '\u{1F4DC}', + shortName: 'scroll', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.bookPaper, + keywords: [ + 'paper', + 'uc6', + 'classroom', + 'harry potter', + 'document', + 'scroll', + 'history', + 'work', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'documents', + 'ancient', + 'old', + 'office' + ]), + Emoji( + name: 'page with curl', + char: '\u{1F4C3}', + shortName: 'page_with_curl', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.bookPaper, + keywords: [ + 'curl', + 'document', + 'page', + 'uc6', + 'classroom', + 'write', + 'document', + 'envelope', + 'work', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'writing', + 'documents', + 'letter', + 'message', + 'offer', + 'office' + ]), + Emoji( + name: 'page facing up', + char: '\u{1F4C4}', + shortName: 'page_facing_up', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.bookPaper, + keywords: [ + 'document', + 'page', + 'uc6', + 'classroom', + 'write', + 'document', + 'work', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'writing', + 'documents', + 'office' + ]), + Emoji( + name: 'bookmark tabs', + char: '\u{1F4D1}', + shortName: 'bookmark_tabs', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.bookPaper, + keywords: [ + 'bookmark', + 'mark', + 'marker', + 'tabs', + 'uc6', + 'classroom', + 'write', + 'document', + 'work', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'writing', + 'documents', + 'office' + ]), + Emoji( + name: 'receipt', + char: '\u{1F9FE}', + shortName: 'receipt', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.money, + keywords: [ + 'uc11', + 'document', + 'business', + 'discount', + 'history', + 'price', + 'rich', + 'purchase', + 'household', + 'restaurant', + 'invoice', + 'documents', + 'sale', + 'bargain', + 'ancient', + 'old', + 'grand', + 'expensive', + 'fancy', + 'buy', + 'shop', + 'spend' + ]), + Emoji( + name: 'bar chart', + char: '\u{1F4CA}', + shortName: 'bar_chart', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.office, + keywords: [ + 'bar', + 'chart', + 'graph', + 'uc6', + 'classroom', + 'business', + 'data', + 'measure', + 'work', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'graph', + 'office' + ]), + Emoji( + name: 'chart increasing', + char: '\u{1F4C8}', + shortName: 'chart_with_upwards_trend', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.office, + keywords: [ + 'chart', + 'graph', + 'growth', + 'trend', + 'upward', + 'uc6', + 'classroom', + 'business', + 'data', + 'measure', + 'work', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'graph', + 'office' + ]), + Emoji( + name: 'chart decreasing', + char: '\u{1F4C9}', + shortName: 'chart_with_downwards_trend', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.office, + keywords: [ + 'chart', + 'down', + 'graph', + 'trend', + 'uc6', + 'classroom', + 'business', + 'data', + 'measure', + 'work', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'graph', + 'office' + ]), + Emoji( + name: 'spiral notepad', + char: '\u{1F5D2}\u{FE0F}', + shortName: 'notepad_spiral', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.office, + keywords: [ + 'note', + 'pad', + 'spiral', + 'uc7', + 'classroom', + 'write', + 'business', + 'envelope', + 'work', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'writing', + 'letter', + 'message', + 'offer', + 'office' + ]), + Emoji( + name: 'spiral calendar', + char: '\u{1F5D3}\u{FE0F}', + shortName: 'calendar_spiral', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.office, + keywords: [ + 'calendar', + 'pad', + 'spiral', + 'uc7', + 'classroom', + 'calendar', + 'advent', + 'schedule', + 'work', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'date', + 'sunday', + 'monday', + 'tuesday', + 'wednesday', + 'thursday', + 'friday', + 'saturday', + 'month', + 'agenda', + 'mois', + 'year', + 'today', + 'jour', + 'week', + 'when', + 'office' + ]), + Emoji( + name: 'tear-off calendar', + char: '\u{1F4C6}', + shortName: 'calendar', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.office, + keywords: [ + 'calendar', + 'uc6', + 'classroom', + 'day', + 'calendar', + 'business', + 'schedule', + 'work', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'date', + 'sunday', + 'monday', + 'tuesday', + 'wednesday', + 'thursday', + 'friday', + 'saturday', + 'month', + 'agenda', + 'mois', + 'year', + 'today', + 'jour', + 'week', + 'when', + 'office' + ]), + Emoji( + name: 'calendar', + char: '\u{1F4C5}', + shortName: 'date', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.office, + keywords: [ + 'date', + 'uc6', + 'classroom', + 'calendar', + 'advent', + 'schedule', + 'work', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'date', + 'sunday', + 'monday', + 'tuesday', + 'wednesday', + 'thursday', + 'friday', + 'saturday', + 'month', + 'agenda', + 'mois', + 'year', + 'today', + 'jour', + 'week', + 'when', + 'office' + ]), + Emoji( + name: 'wastebasket', + char: '\u{1F5D1}\u{FE0F}', + shortName: 'wastebasket', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.office, + keywords: [ + 'wastebasket', + 'uc7', + 'classroom', + 'business', + 'trash', + 'clean', + 'empty', + 'delete', + 'household', + 'work', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'litter', + 'trash can', + 'garbage', + 'rubbish', + 'poubelle', + 'basura', + 'office' + ]), + Emoji( + name: 'card index', + char: '\u{1F4C7}', + shortName: 'card_index', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.office, + keywords: [ + 'card', + 'index', + 'rolodex', + 'uc6', + 'classroom', + 'business', + 'work', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'office' + ]), + Emoji( + name: 'card file box', + char: '\u{1F5C3}\u{FE0F}', + shortName: 'card_box', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.office, + keywords: [ + 'box', + 'card', + 'file', + 'uc7', + 'classroom', + 'business', + 'work', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'office' + ]), + Emoji( + name: 'ballot box with ballot', + char: '\u{1F5F3}\u{FE0F}', + shortName: 'ballot_box', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.mail, + keywords: [ + 'ballot', + 'box', + 'uc7', + 'classroom', + 'vote', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning' + ]), + Emoji( + name: 'file cabinet', + char: '\u{1F5C4}\u{FE0F}', + shortName: 'file_cabinet', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.office, + keywords: [ + 'cabinet', + 'file', + 'filing', + 'uc7', + 'classroom', + 'business', + 'work', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'office' + ]), + Emoji( + name: 'clipboard', + char: '\u{1F4CB}', + shortName: 'clipboard', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.office, + keywords: [ + 'clipboard', + 'uc6', + 'classroom', + 'write', + 'business', + 'data', + 'work', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'writing', + 'graph', + 'office' + ]), + Emoji( + name: 'file folder', + char: '\u{1F4C1}', + shortName: 'file_folder', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.office, + keywords: [ + 'file', + 'folder', + 'uc6', + 'classroom', + 'work', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'office' + ]), + Emoji( + name: 'open file folder', + char: '\u{1F4C2}', + shortName: 'open_file_folder', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.office, + keywords: [ + 'file', + 'folder', + 'open', + 'uc6', + 'classroom', + 'work', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'office' + ]), + Emoji( + name: 'card index dividers', + char: '\u{1F5C2}\u{FE0F}', + shortName: 'dividers', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.office, + keywords: [ + 'card', + 'dividers', + 'index', + 'uc7', + 'classroom', + 'work', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'office' + ]), + Emoji( + name: 'rolled-up newspaper', + char: '\u{1F5DE}\u{FE0F}', + shortName: 'newspaper2', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.bookPaper, + keywords: [ + 'news', + 'newspaper', + 'paper', + 'rolled', + 'uc7', + 'classroom', + 'write', + 'news', + 'history', + 'household', + 'work', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'writing', + 'article', + 'ancient', + 'old', + 'office' + ]), + Emoji( + name: 'newspaper', + char: '\u{1F4F0}', + shortName: 'newspaper', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.bookPaper, + keywords: [ + 'news', + 'paper', + 'uc6', + 'classroom', + 'write', + 'news', + 'history', + 'household', + 'work', + 'covid', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'writing', + 'article', + 'ancient', + 'old', + 'office' + ]), + Emoji( + name: 'notebook', + char: '\u{1F4D3}', + shortName: 'notebook', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.bookPaper, + keywords: [ + 'notebook', + 'uc6', + 'book', + 'classroom', + 'write', + 'work', + 'journal', + 'books', + 'read', + 'reading', + 'cahier', + 'livre', + 'cuaderno', + 'diary', + 'dictionary', + 'encyclopedia', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'writing', + 'office' + ]), + Emoji( + name: 'notebook with decorative cover', + char: '\u{1F4D4}', + shortName: 'notebook_with_decorative_cover', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.bookPaper, + keywords: [ + 'book', + 'cover', + 'decorated', + 'notebook', + 'uc6', + 'book', + 'classroom', + 'write', + 'work', + 'journal', + 'books', + 'read', + 'reading', + 'cahier', + 'livre', + 'cuaderno', + 'diary', + 'dictionary', + 'encyclopedia', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'writing', + 'office' + ]), + Emoji( + name: 'ledger', + char: '\u{1F4D2}', + shortName: 'ledger', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.bookPaper, + keywords: [ + 'notebook', + 'uc6', + 'classroom', + 'write', + 'work', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'writing', + 'office' + ]), + Emoji( + name: 'closed book', + char: '\u{1F4D5}', + shortName: 'closed_book', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.bookPaper, + keywords: [ + 'book', + 'closed', + 'uc6', + 'book', + 'classroom', + 'write', + 'bible', + 'history', + 'work', + 'books', + 'read', + 'reading', + 'cahier', + 'livre', + 'cuaderno', + 'diary', + 'dictionary', + 'encyclopedia', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'writing', + 'ancient', + 'old', + 'office' + ]), + Emoji( + name: 'green book', + char: '\u{1F4D7}', + shortName: 'green_book', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.bookPaper, + keywords: [ + 'book', + 'green', + 'uc6', + 'book', + 'classroom', + 'bible', + 'history', + 'work', + 'books', + 'read', + 'reading', + 'cahier', + 'livre', + 'cuaderno', + 'diary', + 'dictionary', + 'encyclopedia', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'ancient', + 'old', + 'office' + ]), + Emoji( + name: 'blue book', + char: '\u{1F4D8}', + shortName: 'blue_book', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.bookPaper, + keywords: [ + 'blue', + 'book', + 'uc6', + 'book', + 'classroom', + 'write', + 'bible', + 'history', + 'work', + 'books', + 'read', + 'reading', + 'cahier', + 'livre', + 'cuaderno', + 'diary', + 'dictionary', + 'encyclopedia', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'writing', + 'ancient', + 'old', + 'office' + ]), + Emoji( + name: 'orange book', + char: '\u{1F4D9}', + shortName: 'orange_book', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.bookPaper, + keywords: [ + 'book', + 'orange', + 'uc6', + 'book', + 'classroom', + 'write', + 'bible', + 'history', + 'work', + 'books', + 'read', + 'reading', + 'cahier', + 'livre', + 'cuaderno', + 'diary', + 'dictionary', + 'encyclopedia', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'writing', + 'ancient', + 'old', + 'office' + ]), + Emoji( + name: 'books', + char: '\u{1F4DA}', + shortName: 'books', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.bookPaper, + keywords: [ + 'book', + 'uc6', + 'book', + 'classroom', + 'write', + 'harry potter', + 'nerd', + 'history', + 'work', + 'books', + 'read', + 'reading', + 'cahier', + 'livre', + 'cuaderno', + 'diary', + 'dictionary', + 'encyclopedia', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'writing', + 'smart', + 'geek', + 'serious', + 'ancient', + 'old', + 'office' + ]), + Emoji( + name: 'open book', + char: '\u{1F4D6}', + shortName: 'book', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.bookPaper, + keywords: [ + 'book', + 'open', + 'uc6', + 'book', + 'classroom', + 'write', + 'harry potter', + 'history', + 'schedule', + 'work', + 'journal', + 'books', + 'read', + 'reading', + 'cahier', + 'livre', + 'cuaderno', + 'diary', + 'dictionary', + 'encyclopedia', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'writing', + 'ancient', + 'old', + 'office' + ]), + Emoji( + name: 'bookmark', + char: '\u{1F516}', + shortName: 'bookmark', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.bookPaper, + keywords: [ + 'mark', + 'uc6', + 'book', + 'discount', + 'price', + 'household', + 'books', + 'read', + 'reading', + 'cahier', + 'livre', + 'cuaderno', + 'diary', + 'dictionary', + 'encyclopedia', + 'sale', + 'bargain' + ]), + Emoji( + name: 'safety pin', + char: '\u{1F9F7}', + shortName: 'safety_pin', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.household, + keywords: [ + 'uc11', + 'household', + 'sew', + 'work', + 'knit', + 'embroider', + 'stitch', + 'repair', + 'crochet', + 'alter', + 'seamstress', + 'fix', + 'office' + ]), + Emoji( + name: 'link', + char: '\u{1F517}', + shortName: 'link', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.tool, + keywords: [ + 'link', + 'uc6', + 'classroom', + 'steel', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'metal' + ]), + Emoji( + name: 'paperclip', + char: '\u{1F4CE}', + shortName: 'paperclip', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.office, + keywords: [ + 'paperclip', + 'uc6', + 'classroom', + 'business', + 'household', + 'work', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'office' + ]), + Emoji( + name: 'linked paperclips', + char: '\u{1F587}\u{FE0F}', + shortName: 'paperclips', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.office, + keywords: [ + 'link', + 'paperclip', + 'uc7', + 'classroom', + 'business', + 'work', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'office' + ]), + Emoji( + name: 'triangular ruler', + char: '\u{1F4D0}', + shortName: 'triangular_ruler', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.office, + keywords: [ + 'ruler', + 'set', + 'triangle', + 'uc6', + 'tool', + 'classroom', + 'triangle', + 'measure', + 'work', + 'tools', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'triangles', + 'office' + ]), + Emoji( + name: 'straight ruler', + char: '\u{1F4CF}', + shortName: 'straight_ruler', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.office, + keywords: [ + 'ruler', + 'straight edge', + 'uc6', + 'theatre', + 'tool', + 'classroom', + 'household', + 'measure', + 'work', + 'theater', + 'craft', + 'drama', + 'monet', + 'tools', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'office' + ]), + Emoji( + name: 'abacus', + char: '\u{1F9EE}', + shortName: 'abacus', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.computer, + keywords: [ + 'uc11', + 'math', + 'science', + 'vintage', + 'history', + 'calculator', + 'toy', + 'work', + 'decimal', + 'percentage', + 'fraction', + 'lab', + 'ancient', + 'old', + 'count', + 'add', + 'office' + ]), + Emoji( + name: 'pushpin', + char: '\u{1F4CC}', + shortName: 'pushpin', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.office, + keywords: [ + 'pin', + 'uc6', + 'classroom', + 'map', + 'business', + 'household', + 'work', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'maps', + 'location', + 'locate', + 'local', + 'lost', + 'office' + ]), + Emoji( + name: 'round pushpin', + char: '\u{1F4CD}', + shortName: 'round_pushpin', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.office, + keywords: [ + 'pin', + 'pushpin', + 'uc6', + 'classroom', + 'map', + 'business', + 'household', + 'work', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'maps', + 'location', + 'locate', + 'local', + 'lost', + 'office' + ]), + Emoji( + name: 'scissors', + char: '\u{2702}\u{FE0F}', + shortName: 'scissors', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.office, + keywords: [ + 'cutting', + 'tool', + 'uc1', + 'theatre', + 'tool', + 'weapon', + 'classroom', + 'steel', + 'household', + 'sew', + 'work', + 'theater', + 'craft', + 'drama', + 'monet', + 'tools', + 'weapons', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'metal', + 'knit', + 'embroider', + 'stitch', + 'repair', + 'crochet', + 'alter', + 'seamstress', + 'fix', + 'office' + ]), + Emoji( + name: 'pen', + char: '\u{1F58A}\u{FE0F}', + shortName: 'pen_ballpoint', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.writing, + keywords: [ + 'ballpoint', + 'uc7', + 'tool', + 'classroom', + 'write', + 'business', + 'color', + 'correct', + 'detective', + 'household', + 'work', + 'journal', + 'tools', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'writing', + 'colour', + 'coloring', + 'colouring', + 'drawing', + 'marker', + 'sketch', + 'passing grade', + 'office' + ]), + Emoji( + name: 'fountain pen', + char: '\u{1F58B}\u{FE0F}', + shortName: 'pen_fountain', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.writing, + keywords: [ + 'fountain', + 'pen', + 'uc7', + 'tool', + 'classroom', + 'write', + 'color', + 'correct', + 'work', + 'tools', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'writing', + 'colour', + 'coloring', + 'colouring', + 'drawing', + 'marker', + 'sketch', + 'passing grade', + 'office' + ]), + Emoji( + name: 'black nib', + char: '\u{2712}\u{FE0F}', + shortName: 'black_nib', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.writing, + keywords: [ + 'nib', + 'pen', + 'uc1', + 'classroom', + 'write', + 'correct', + 'work', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'writing', + 'passing grade', + 'office' + ]), + Emoji( + name: 'paintbrush', + char: '\u{1F58C}\u{FE0F}', + shortName: 'paintbrush', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.writing, + keywords: [ + 'painting', + 'uc7', + 'theatre', + 'classroom', + 'write', + 'painting', + 'color', + 'theater', + 'craft', + 'drama', + 'monet', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'writing', + 'painter', + 'arts', + 'colour', + 'coloring', + 'colouring', + 'drawing', + 'marker', + 'sketch' + ]), + Emoji( + name: 'crayon', + char: '\u{1F58D}\u{FE0F}', + shortName: 'crayon', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.writing, + keywords: [ + 'crayon', + 'uc7', + 'theatre', + 'classroom', + 'write', + 'color', + 'household', + 'theater', + 'craft', + 'drama', + 'monet', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'writing', + 'colour', + 'coloring', + 'colouring', + 'drawing', + 'marker', + 'sketch' + ]), + Emoji( + name: 'memo', + char: '\u{1F4DD}', + shortName: 'pencil', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.writing, + keywords: [ + 'pencil', + 'uc6', + 'classroom', + 'write', + 'document', + 'envelope', + 'color', + 'correct', + 'work', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'writing', + 'documents', + 'letter', + 'message', + 'offer', + 'colour', + 'coloring', + 'colouring', + 'drawing', + 'marker', + 'sketch', + 'passing grade', + 'office' + ]), + Emoji( + name: 'pencil', + char: '\u{270F}\u{FE0F}', + shortName: 'pencil2', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.writing, + keywords: [ + 'pencil', + 'uc1', + 'theatre', + 'tool', + 'classroom', + 'write', + 'color', + 'correct', + 'household', + 'work', + 'theater', + 'craft', + 'drama', + 'monet', + 'tools', + 'school', + 'teach', + 'learn', + 'study', + 'college', + 'degree', + 'education', + 'homework', + 'student', + 'teacher', + 'university', + 'test', + 'learning', + 'writing', + 'colour', + 'coloring', + 'colouring', + 'drawing', + 'marker', + 'sketch', + 'passing grade', + 'office' + ]), + Emoji( + name: 'magnifying glass tilted left', + char: '\u{1F50D}', + shortName: 'mag', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.lightVideo, + keywords: [ + 'glass', + 'magnifying', + 'search', + 'tool', + 'uc6', + 'google', + 'search', + 'detective', + 'household', + 'look', + 'find', + 'looking', + 'see' + ]), + Emoji( + name: 'magnifying glass tilted right', + char: '\u{1F50E}', + shortName: 'mag_right', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.lightVideo, + keywords: [ + 'glass', + 'magnifying', + 'search', + 'tool', + 'uc6', + 'google', + 'search', + 'detective', + 'look', + 'find', + 'looking', + 'see' + ]), + Emoji( + name: 'locked with pen', + char: '\u{1F50F}', + shortName: 'lock_with_ink_pen', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.lock, + keywords: [ + 'ink', + 'lock', + 'nib', + 'pen', + 'privacy', + 'uc6', + 'lock', + 'locks', + 'key', + 'keys' + ]), + Emoji( + name: 'locked with key', + char: '\u{1F510}', + shortName: 'closed_lock_with_key', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.lock, + keywords: [ + 'closed', + 'key', + 'lock', + 'secure', + 'uc6', + 'lock', + 'household', + 'locks', + 'key', + 'keys' + ]), + Emoji( + name: 'locked', + char: '\u{1F512}', + shortName: 'lock', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.lock, + keywords: ['closed', 'uc6', 'lock', 'locks', 'key', 'keys']), + Emoji( + name: 'unlocked', + char: '\u{1F513}', + shortName: 'unlock', + emojiGroup: EmojiGroup.objects, + emojiSubgroup: EmojiSubgroup.lock, + keywords: [ + 'lock', + 'open', + 'unlock', + 'uc6', + 'lock', + 'locks', + 'key', + 'keys' + ]), + Emoji( + name: 'red heart', + char: '\u{2764}\u{FE0F}', + shortName: 'heart', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.emotion, + keywords: [ + 'heart', + 'uc1', + 'shapes', + 'love', + 'rainbow', + 'red heart', + 'heart', + 'i love you', + 'te amo', + "je t'aime", + 'anniversary', + 'lovin', + 'amour', + 'aimer', + 'amor', + 'valentines day', + 'enamour', + 'lovey', + 'hearts', + 'serce', + 'corazón', + 'coração', + 'coeur', + '<3' + ]), + Emoji( + name: 'orange heart', + char: '\u{1F9E1}', + shortName: 'orange_heart', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.emotion, + keywords: [ + 'orange', + 'uc10', + 'shapes', + 'love', + 'rainbow', + 'orange', + 'heart', + 'i love you', + 'te amo', + "je t'aime", + 'anniversary', + 'lovin', + 'amour', + 'aimer', + 'amor', + 'valentines day', + 'enamour', + 'lovey', + 'hearts', + 'serce', + 'corazón', + 'coração', + 'coeur' + ]), + Emoji( + name: 'yellow heart', + char: '\u{1F49B}', + shortName: 'yellow_heart', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.emotion, + keywords: [ + 'yellow', + 'uc6', + 'shapes', + 'love', + 'rainbow', + 'friend', + 'heart', + 'i love you', + 'te amo', + "je t'aime", + 'anniversary', + 'lovin', + 'amour', + 'aimer', + 'amor', + 'valentines day', + 'enamour', + 'lovey', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'hearts', + 'serce', + 'corazón', + 'coração', + 'coeur' + ]), + Emoji( + name: 'green heart', + char: '\u{1F49A}', + shortName: 'green_heart', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.emotion, + keywords: [ + 'green', + 'uc6', + 'shapes', + 'halloween', + 'love', + 'rainbow', + 'irish', + 'jealous', + 'heart', + 'samhain', + 'i love you', + 'te amo', + "je t'aime", + 'anniversary', + 'lovin', + 'amour', + 'aimer', + 'amor', + 'valentines day', + 'enamour', + 'lovey', + 'saint patricks day', + 'st patricks day', + 'leprechaun', + 'hearts', + 'serce', + 'corazón', + 'coração', + 'coeur' + ]), + Emoji( + name: 'blue heart', + char: '\u{1F499}', + shortName: 'blue_heart', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.emotion, + keywords: [ + 'blue', + 'uc6', + 'shapes', + 'love', + 'rainbow', + 'friend', + 'heart', + 'i love you', + 'te amo', + "je t'aime", + 'anniversary', + 'lovin', + 'amour', + 'aimer', + 'amor', + 'valentines day', + 'enamour', + 'lovey', + 'friends', + 'friendship', + 'best friends', + 'bestfriends', + 'ami', + 'amiga', + 'amigo', + 'hearts', + 'serce', + 'corazón', + 'coração', + 'coeur' + ]), + Emoji( + name: 'purple heart', + char: '\u{1F49C}', + shortName: 'purple_heart', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.emotion, + keywords: [ + 'purple', + 'uc6', + 'shapes', + 'love', + 'rainbow', + 'pink', + 'heart', + 'i love you', + 'te amo', + "je t'aime", + 'anniversary', + 'lovin', + 'amour', + 'aimer', + 'amor', + 'valentines day', + 'enamour', + 'lovey', + 'rose', + 'hearts', + 'serce', + 'corazón', + 'coração', + 'coeur' + ]), + Emoji( + name: 'black heart', + char: '\u{1F5A4}', + shortName: 'black_heart', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.emotion, + keywords: [ + 'black', + 'evil', + 'wicked', + 'uc9', + 'shapes', + 'halloween', + 'love', + 'heartbreak', + 'rainbow', + 'hate', + 'killer', + 'heart', + 'samhain', + 'i love you', + 'te amo', + "je t'aime", + 'anniversary', + 'lovin', + 'amour', + 'aimer', + 'amor', + 'valentines day', + 'enamour', + 'lovey', + 'broken heart', + 'heartbroken', + 'i hate', + 'disgust', + 'stump', + 'shout', + 'dislike', + 'rude', + 'annoy', + 'grinch', + 'gross', + 'grumpy', + 'mean', + 'problem', + 'suck', + 'jerk', + 'asshole', + 'no', + 'savage', + 'scary clown', + 'hearts', + 'serce', + 'corazón', + 'coração', + 'coeur' + ]), + Emoji( + name: 'brown heart', + char: '\u{1F90E}', + shortName: 'brown_heart', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.emotion, + keywords: [ + 'uc12', + 'shapes', + 'heart', + 'hearts', + 'serce', + 'corazón', + 'coração', + 'coeur' + ]), + Emoji( + name: 'white heart', + char: '\u{1F90D}', + shortName: 'white_heart', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.emotion, + keywords: [ + 'uc12', + 'shapes', + 'heart', + 'hearts', + 'serce', + 'corazón', + 'coração', + 'coeur' + ]), + Emoji( + name: 'broken heart', + char: '\u{1F494}', + shortName: 'broken_heart', + emojiGroup: EmojiGroup.smileysEmotion, + emojiSubgroup: EmojiSubgroup.emotion, + keywords: [ + 'break', + 'broken', + 'uc6', + 'love', + 'heartbreak', + 'red heart', + 'heart', + 'i love you', + 'te amo', + "je t'aime", + 'anniversary', + 'lovin', + 'amour', + 'aimer', + 'amor', + 'valentines day', + 'enamour', + 'lovey', + 'broken heart', + 'heartbroken', + 'hearts', + 'serce', + 'corazón', + 'coração', + 'coeur', + ' keywords; + List? _runes; + + /// Emoji class. + /// [name] of emoji. [char] and character of emoji. [shortName] and a digest name of emoji, [emojiGroup] is emoji's group and [emojiSubgroup] is emoji's subgroup. [keywords] list of keywords for emoji. [modifiable] `true` if emoji has skin. + Emoji( + {this.name, + this.char, + this.shortName, + this.emojiGroup, + this.emojiSubgroup, + this.keywords = const [], + this.modifiable = false}); + + /// Runes of Emoji Character + List get charRunes { + return _runes ??= char!.runes.toList(); + } + + /// Returns current Emoji with New requested [skinTone] if modifiable, else Returns current Emoji + Emoji? newSkin(fitzpatrick skinTone) { + if (modifiable) { + switch (skinTone) { + case fitzpatrick.light: + return Emoji( + name: this.name! + ', tone1', + char: modify(this.char, skinTone), + shortName: this.shortName! + '_tone1', + emojiGroup: this.emojiGroup, + emojiSubgroup: this.emojiSubgroup, + keywords: this.keywords, + modifiable: true); + case fitzpatrick.mediumLight: + return Emoji( + name: this.name! + ', tone2', + char: modify(this.char, skinTone), + shortName: this.shortName! + '_tone2', + emojiGroup: this.emojiGroup, + emojiSubgroup: this.emojiSubgroup, + keywords: this.keywords, + modifiable: true); + case fitzpatrick.medium: + return Emoji( + name: this.name! + ', tone3', + char: modify(this.char, skinTone), + shortName: this.shortName! + '_tone3', + emojiGroup: this.emojiGroup, + emojiSubgroup: this.emojiSubgroup, + keywords: this.keywords, + modifiable: true); + case fitzpatrick.mediumDark: + return Emoji( + name: this.name! + ', tone4', + char: modify(this.char, skinTone), + shortName: this.shortName! + '_tone4', + emojiGroup: this.emojiGroup, + emojiSubgroup: this.emojiSubgroup, + keywords: this.keywords, + modifiable: true); + case fitzpatrick.dark: + return Emoji( + name: this.name! + ', tone5', + char: modify(this.char, skinTone), + shortName: this.shortName! + '_tone5', + emojiGroup: this.emojiGroup, + emojiSubgroup: this.emojiSubgroup, + keywords: this.keywords, + modifiable: true); + case fitzpatrick.None: + return Emoji.byChar(stabilize(this.char)); + } + } + return this; + } + + /// Get all Emojis + static List all() => List.unmodifiable(_emojis); + + /// Returns Emoji by [char] and character + static Emoji? byChar(String char) { + return _emojis.firstWhereOrNull((Emoji emoji) => emoji.char == char); + } + + /// Returns Emoji by [name] + static Emoji? byName(String name) { + name = name.toLowerCase(); // todo: searchable name + return _emojis.firstWhereOrNull((Emoji emoji) => emoji.name == name); + } + + /// Returns Emoji by [name] as short name. + static Emoji? byShortName(String name) { + return _emojis.firstWhereOrNull((Emoji emoji) => emoji.char == name); + } + + /// Returns list of Emojis in a same [group] + static Iterable byGroup(EmojiGroup group) { + return _emojis.where((Emoji emoji) => emoji.emojiGroup == group); + } + + /// Returns list of Emojis in a same [subgroup] + static Iterable bySubgroup(EmojiSubgroup subgroup) { + return _emojis.where((Emoji emoji) => emoji.emojiSubgroup == subgroup); + } + + /// Returns List of Emojis with Specific [keyword] + static Iterable byKeyword(String keyword) { + keyword = keyword.toLowerCase(); + return _emojis.where((Emoji emoji) => emoji.keywords.contains(keyword)); + } + + /// disassemble [emoji] to list of emojis, without skin tones if [noSkin] be `true`. + static List disassemble(String emoji, {bool noSkin = false}) { + List emojiRunes = emoji.runes.toList(); + emojiRunes.removeWhere((codeChar) => + ZeroWidthCharCodes.contains(codeChar) || + (noSkin && _isFitzpatrickCode(codeChar))); + return emojiRunes.map((char) => String.fromCharCode(char)).toList(); + // return emoji.runes.toList()..removeWhere((codeChar) => ZeroWidthCharCodes.contains(codeChar) || (noSkin && _isFitzpatrickCode(codeChar))).map((char) => String.fromCharCode(char)).toList() + } + + /// assemble emojis with [emojiChars] codes. + static String assemble(List emojiChars) { + List codeCharPoints = []; + + for (var i = 0; i < emojiChars.length; i++) { + if (i != 0 && !isFitzpatrick(emojiChars[i - 1])) { + codeCharPoints.add(ZWJ); + } + final emojiRunes = emojiChars[i].runes.toList(); + codeCharPoints.addAll(emojiRunes); + } + codeCharPoints.add(variationSelector16); + return String.fromCharCodes(codeCharPoints); + } + + /// Modify skin tone of [emoji] by requested [skinTone] + static String modify(String? emoji, fitzpatrick skinTone) { + int? skinToneCharCode; + switch (skinTone) { + case fitzpatrick.light: + skinToneCharCode = 127995; + break; + case fitzpatrick.mediumLight: + skinToneCharCode = 127996; + break; + case fitzpatrick.medium: + skinToneCharCode = 127997; + break; + case fitzpatrick.mediumDark: + skinToneCharCode = 127998; + break; + case fitzpatrick.dark: + skinToneCharCode = 127999; + break; + case fitzpatrick.None: + return stabilize(emoji); + } + + final emojiRunes = emoji!.runes.toList(); + List finalCharCodes = []; + for (final charCode in emojiRunes) { + if (!_isFitzpatrickCode(charCode)) { + finalCharCodes.add(charCode); + if (_isModifiable(charCode)) { + finalCharCodes.add(skinToneCharCode); + } + } + } + return String.fromCharCodes(finalCharCodes as Iterable); + } + + // todo: support unspecified gender for "... holding hands", "kiss", "couple with heart" and "family". + /// stabilize [skin] and [gender] of [emoji], if `true`. + static String stabilize(String? emoji, + {bool skin = true, bool gender = false}) { + if (gender) { + emoji = emoji! + .replaceAll( + '\u{200D}\u{2642}\u{FE0F}', '') // remove ZWJ man from emoji + .replaceAll( + '\u{200D}\u{2640}\u{FE0F}', '') // remove ZWJ woman from emoji + .replaceAll('\u{1F468}', '\u{1F9D1}') // replace man with person + .replaceAll('\u{1F469}', '\u{1F9D1}') // replace woman with person + .replaceAll( + '\u{1F474}', '\u{1F9D3}') // replace old man with old person + .replaceAll( + '\u{1F475}', '\u{1F9D3}'); // replace old woman with old person + } + + final List emojiRunes = emoji!.runes.toList(); + + if (skin) { + emojiRunes.removeWhere((codeChar) => _isFitzpatrickCode(codeChar)); + } + return String.fromCharCodes(emojiRunes); + } + + /// returns `true` if [emojiCode] is code of Emoji with skin!. + static _isModifiable(int emojiCode) { + return _modifiableCharCodes.contains(emojiCode); + } + + /// returns `true` if [emoji] is a Fitzpatrick Emoji. + static bool isFitzpatrick(String emoji) { + return skinToneEmojiChars.contains(emoji); + } + + /// returns `true` if [emojiCode] is code of Fitzpatrick Emoji. + static bool _isFitzpatrickCode(int emojiCode) { + return _skinToneCharCodes.contains(emojiCode); + } + + @override + toString() => char!; +} diff --git a/packages/stream_chat_flutter/lib/src/extension.dart b/packages/stream_chat_flutter/lib/src/extension.dart index daa8a69c..11ff3253 100644 --- a/packages/stream_chat_flutter/lib/src/extension.dart +++ b/packages/stream_chat_flutter/lib/src/extension.dart @@ -1,7 +1,7 @@ import 'package:characters/characters.dart'; -import 'package:emojis/emoji.dart'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; +import 'package:stream_chat_flutter/src/emoji/emoji.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; final _emojis = Emoji.all(); @@ -47,53 +47,53 @@ extension PlatformFileX on PlatformFile { } extension InputDecorationX on InputDecoration { - InputDecoration merge(InputDecoration other) { + InputDecoration merge(InputDecoration? other) { if (other == null) return this; return copyWith( - icon: other?.icon, - labelText: other?.labelText, + icon: other.icon, + labelText: other.labelText, labelStyle: labelStyle?.merge(other.labelStyle) ?? other.labelStyle, - helperText: other?.helperText, + helperText: other.helperText, helperStyle: helperStyle?.merge(other.helperStyle) ?? other.helperStyle, - helperMaxLines: other?.helperMaxLines, - hintText: other?.hintText, + helperMaxLines: other.helperMaxLines, + hintText: other.hintText, hintStyle: hintStyle?.merge(other.hintStyle) ?? other.hintStyle, - hintTextDirection: other?.hintTextDirection, - hintMaxLines: other?.hintMaxLines, - errorText: other?.errorText, + hintTextDirection: other.hintTextDirection, + hintMaxLines: other.hintMaxLines, + errorText: other.errorText, errorStyle: errorStyle?.merge(other.errorStyle) ?? other.errorStyle, - errorMaxLines: other?.errorMaxLines, - floatingLabelBehavior: other?.floatingLabelBehavior, - isCollapsed: other?.isCollapsed, - isDense: other?.isDense, - contentPadding: other?.contentPadding, - prefixIcon: other?.prefixIcon, - prefix: other?.prefix, - prefixText: other?.prefixText, - prefixIconConstraints: other?.prefixIconConstraints, + errorMaxLines: other.errorMaxLines, + floatingLabelBehavior: other.floatingLabelBehavior, + isCollapsed: other.isCollapsed, + isDense: other.isDense, + contentPadding: other.contentPadding, + prefixIcon: other.prefixIcon, + prefix: other.prefix, + prefixText: other.prefixText, + prefixIconConstraints: other.prefixIconConstraints, prefixStyle: prefixStyle?.merge(other.prefixStyle) ?? other.prefixStyle, - suffixIcon: other?.suffixIcon, - suffix: other?.suffix, - suffixText: other?.suffixText, + suffixIcon: other.suffixIcon, + suffix: other.suffix, + suffixText: other.suffixText, suffixStyle: suffixStyle?.merge(other.suffixStyle) ?? other.suffixStyle, - suffixIconConstraints: other?.suffixIconConstraints, - counter: other?.counter, - counterText: other?.counterText, + suffixIconConstraints: other.suffixIconConstraints, + counter: other.counter, + counterText: other.counterText, counterStyle: counterStyle?.merge(other.counterStyle) ?? other.counterStyle, - filled: other?.filled, - fillColor: other?.fillColor, - focusColor: other?.focusColor, - hoverColor: other?.hoverColor, - errorBorder: other?.errorBorder, - focusedBorder: other?.focusedBorder, - focusedErrorBorder: other?.focusedErrorBorder, - disabledBorder: other?.disabledBorder, - enabledBorder: other?.enabledBorder, - border: other?.border, - enabled: other?.enabled, - semanticCounterText: other?.semanticCounterText, - alignLabelWithHint: other?.alignLabelWithHint, + filled: other.filled, + fillColor: other.fillColor, + focusColor: other.focusColor, + hoverColor: other.hoverColor, + errorBorder: other.errorBorder, + focusedBorder: other.focusedBorder, + focusedErrorBorder: other.focusedErrorBorder, + disabledBorder: other.disabledBorder, + enabledBorder: other.enabledBorder, + border: other.border, + enabled: other.enabled, + semanticCounterText: other.semanticCounterText, + alignLabelWithHint: other.alignLabelWithHint, ); } } diff --git a/packages/stream_chat_flutter/lib/src/full_screen_media.dart b/packages/stream_chat_flutter/lib/src/full_screen_media.dart index aa35ed15..f960b5ee 100644 --- a/packages/stream_chat_flutter/lib/src/full_screen_media.dart +++ b/packages/stream_chat_flutter/lib/src/full_screen_media.dart @@ -24,19 +24,18 @@ class FullScreenMedia extends StatefulWidget { final int startIndex; final String userName; - final DateTime sentAt; - final ShowMessageCallback onShowMessage; + final ShowMessageCallback? onShowMessage; /// Instantiate a new FullScreenImage const FullScreenMedia({ - Key key, - @required this.mediaAttachments, - this.message, + Key? key, + required this.mediaAttachments, + required this.message, this.startIndex = 0, - this.userName = '', - this.sentAt, + String? userName, this.onShowMessage, - }) : super(key: key); + }) : userName = userName ?? '', + super(key: key); @override _FullScreenMediaState createState() => _FullScreenMediaState(); @@ -46,10 +45,10 @@ class _FullScreenMediaState extends State with SingleTickerProviderStateMixin { bool _optionsShown = true; - AnimationController _controller; - PageController _pageController; + late final AnimationController _controller; + late final PageController _pageController; - int _currentPage; + late int _currentPage; final videoPackages = {}; @@ -101,10 +100,11 @@ class _FullScreenMediaState extends State attachment.assetUrl ?? attachment.thumbUrl; return PhotoView( - imageProvider: - imageUrl == null && attachment.localUri != null - ? Image.memory(attachment.file.bytes).image - : CachedNetworkImageProvider(imageUrl), + imageProvider: (imageUrl == null && + attachment.localUri != null && + attachment.file?.bytes != null) + ? Image.memory(attachment.file!.bytes!).image + : CachedNetworkImageProvider(imageUrl!), maxScale: PhotoViewComputedScale.covered, minScale: PhotoViewComputedScale.contained, heroAttributes: PhotoViewHeroAttributes( @@ -112,12 +112,12 @@ class _FullScreenMediaState extends State ), backgroundDecoration: BoxDecoration( color: ColorTween( - begin: StreamChatTheme.of(context) - .channelTheme - .channelHeaderTheme - .color, - end: Colors.black) - .lerp(_controller.value), + begin: StreamChatTheme.of(context) + .channelTheme + .channelHeaderTheme + .color, + end: Colors.black, + ).lerp(_controller.value), ), onTapUp: (a, b, c) { setState(() { @@ -131,7 +131,7 @@ class _FullScreenMediaState extends State }, ); } else if (attachment.type == 'video') { - final controller = videoPackages[attachment.id]; + final controller = videoPackages[attachment.id]!; if (!controller.initialized) { return Center( child: CircularProgressIndicator(), @@ -153,7 +153,7 @@ class _FullScreenMediaState extends State vertical: 50.0, ), child: Chewie( - controller: controller.chewieController, + controller: controller.chewieController!, ), ), ); @@ -171,18 +171,18 @@ class _FullScreenMediaState extends State children: [ ImageHeader( userName: widget.userName, - sentAt: widget.message.createdAt == null - ? '' - : 'Sent ${getDay(widget.message.createdAt)} at ${Jiffy(widget.sentAt.toLocal()).format('HH:mm')}', + sentAt: + 'Sent ${getDay(widget.message.createdAt.toLocal())} at ${Jiffy(widget.message.createdAt.toLocal()).format('HH:mm')}', onBackPressed: () { Navigator.of(context).pop(); }, message: widget.message, - urls: widget.mediaAttachments, currentIndex: _currentPage, onShowMessage: () { - widget.onShowMessage( - widget.message, StreamChannel.of(context).channel); + widget.onShowMessage?.call( + widget.message, + StreamChannel.of(context).channel, + ); }, ), if (widget.message.type != 'ephemeral') @@ -194,9 +194,11 @@ class _FullScreenMediaState extends State mediaSelectedCallBack: (val) { setState(() { _currentPage = val; - _pageController.animateToPage(val, - duration: Duration(milliseconds: 300), - curve: Curves.easeInOut); + _pageController.animateToPage( + val, + duration: Duration(milliseconds: 300), + curve: Curves.easeInOut, + ); Navigator.pop(context); }); }, @@ -210,7 +212,7 @@ class _FullScreenMediaState extends State } String getDay(DateTime dateTime) { - var now = DateTime.now(); + final now = DateTime.now(); if (DateTime(dateTime.year, dateTime.month, dateTime.day) == DateTime(now.year, now.month, now.day)) { @@ -238,11 +240,11 @@ class VideoPackage { final bool _showControls; final bool _autoInitialize; final VideoPlayerController _videoPlayerController; - ChewieController _chewieController; + ChewieController? _chewieController; VideoPlayerController get videoPlayer => _videoPlayerController; - ChewieController get chewieController => _chewieController; + ChewieController? get chewieController => _chewieController; bool get initialized => _videoPlayerController.value.isInitialized; @@ -250,12 +252,11 @@ class VideoPackage { Attachment attachment, { bool showControls = false, bool autoInitialize = true, - }) : assert(attachment != null), - _showControls = showControls, + }) : _showControls = showControls, _autoInitialize = autoInitialize, _videoPlayerController = attachment.localUri != null - ? VideoPlayerController.file(File.fromUri(attachment.localUri)) - : VideoPlayerController.network(attachment.assetUrl); + ? VideoPlayerController.file(File.fromUri(attachment.localUri!)) + : VideoPlayerController.network(attachment.assetUrl!); Future initialize() { return _videoPlayerController.initialize().then((_) { @@ -278,6 +279,6 @@ class VideoPackage { Future dispose() { _chewieController?.dispose(); - return _videoPlayerController?.dispose(); + return _videoPlayerController.dispose(); } } diff --git a/packages/stream_chat_flutter/lib/src/group_image.dart b/packages/stream_chat_flutter/lib/src/group_image.dart index 4cfd655a..f39abf59 100644 --- a/packages/stream_chat_flutter/lib/src/group_image.dart +++ b/packages/stream_chat_flutter/lib/src/group_image.dart @@ -5,8 +5,8 @@ import '../stream_chat_flutter.dart'; class GroupImage extends StatelessWidget { const GroupImage({ - Key key, - @required this.images, + Key? key, + required this.images, this.constraints, this.onTap, this.borderRadius, @@ -16,11 +16,11 @@ class GroupImage extends StatelessWidget { }) : super(key: key); final List images; - final BoxConstraints constraints; - final VoidCallback onTap; + final BoxConstraints? constraints; + final VoidCallback? onTap; final bool selected; - final BorderRadius borderRadius; - final Color selectionColor; + final BorderRadius? borderRadius; + final Color? selectionColor; final double selectionThickness; @override @@ -35,13 +35,13 @@ class GroupImage extends StatelessWidget { StreamChatTheme.of(context) .ownMessageTheme .avatarTheme - .borderRadius, + ?.borderRadius, child: Container( constraints: constraints ?? StreamChatTheme.of(context) .ownMessageTheme .avatarTheme - .constraints, + ?.constraints, decoration: BoxDecoration( color: StreamChatTheme.of(context).colorTheme.accentBlue, ), @@ -107,7 +107,8 @@ class GroupImage extends StatelessWidget { if (selected) { avatar = ClipRRect( borderRadius: (borderRadius ?? - streamChatTheme.ownMessageTheme.avatarTheme.borderRadius) + + streamChatTheme.ownMessageTheme.avatarTheme?.borderRadius ?? + BorderRadius.zero) + BorderRadius.circular(selectionThickness), child: Container( color: selectionColor ?? diff --git a/packages/stream_chat_flutter/lib/src/image_footer.dart b/packages/stream_chat_flutter/lib/src/image_footer.dart index 6833a52f..27c92674 100644 --- a/packages/stream_chat_flutter/lib/src/image_footer.dart +++ b/packages/stream_chat_flutter/lib/src/image_footer.dart @@ -5,8 +5,8 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:dio/dio.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:share_plus/share_plus.dart'; import 'package:path_provider/path_provider.dart'; +import 'package:share_plus/share_plus.dart'; import 'package:stream_chat_flutter/src/stream_chat_theme.dart'; import 'package:stream_chat_flutter/src/video_thumbnail_image.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; @@ -15,13 +15,13 @@ import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; class ImageFooter extends StatefulWidget implements PreferredSizeWidget { /// Callback to call when pressing the back button. /// By default it calls [Navigator.pop] - final VoidCallback onBackPressed; + final VoidCallback? onBackPressed; /// Callback to call when the header is tapped. - final VoidCallback onTitleTap; + final VoidCallback? onTitleTap; /// Callback to call when the image is tapped. - final VoidCallback onImageTap; + final VoidCallback? onImageTap; final int currentPage; final int totalPages; @@ -29,18 +29,18 @@ class ImageFooter extends StatefulWidget implements PreferredSizeWidget { final List mediaAttachments; final Message message; - final ValueChanged mediaSelectedCallBack; + final ValueChanged? mediaSelectedCallBack; /// Creates a channel header ImageFooter({ - Key key, + Key? key, + required this.message, this.onBackPressed, this.onTitleTap, this.onImageTap, this.currentPage = 0, this.totalPages = 0, - this.mediaAttachments, - this.message, + this.mediaAttachments = const [], this.mediaSelectedCallBack, }) : preferredSize = Size.fromHeight(kToolbarHeight), super(key: key); @@ -53,14 +53,11 @@ class ImageFooter extends StatefulWidget implements PreferredSizeWidget { } class _ImageFooterState extends State { - TextEditingController _searchController; final TextEditingController _messageController = TextEditingController(); final FocusNode _messageFocusNode = FocusNode(); final List _selectedChannels = []; - Function modalSetStateCallback; - @override void initState() { super.initState(); @@ -69,13 +66,6 @@ class _ImageFooterState extends State { }); } - @override - void dispose() { - _searchController?.clear(); - _searchController?.dispose(); - super.dispose(); - } - @override Widget build(BuildContext context) { final showShareButton = !kIsWeb; @@ -106,10 +96,10 @@ class _ImageFooterState extends State { widget.mediaAttachments[widget.currentPage]; final url = attachment.imageUrl ?? attachment.assetUrl ?? - attachment.thumbUrl; + attachment.thumbUrl!; final type = attachment.type == 'image' ? 'jpg' - : url?.split('?')?.first?.split('.')?.last ?? 'jpg'; + : url.split('?').first.split('.').last; final request = await HttpClient().getUrl(Uri.parse(url)); final response = await request.close(); @@ -227,24 +217,24 @@ class _ImageFooterState extends State { final attachment = widget.mediaAttachments[index]; if (attachment.type == 'video') { media = InkWell( - onTap: () => widget.mediaSelectedCallBack(index), + onTap: () => widget.mediaSelectedCallBack!(index), child: FittedBox( fit: BoxFit.cover, child: VideoThumbnailImage( - video: attachment.file?.path ?? - attachment.assetUrl, + video: (attachment.file?.path ?? + attachment.assetUrl)!, ), ), ); } else { media = InkWell( - onTap: () => widget.mediaSelectedCallBack(index), + onTap: () => widget.mediaSelectedCallBack!(index), child: AspectRatio( aspectRatio: 1.0, child: CachedNetworkImage( imageUrl: attachment.imageUrl ?? attachment.assetUrl ?? - attachment.thumbUrl, + attachment.thumbUrl!, fit: BoxFit.cover, ), ), @@ -254,32 +244,33 @@ class _ImageFooterState extends State { return Stack( children: [ media, - Padding( - padding: EdgeInsets.all(8.0), - child: Container( - clipBehavior: Clip.antiAlias, - decoration: BoxDecoration( - shape: BoxShape.circle, - color: Colors.white.withOpacity(0.6), - boxShadow: [ - BoxShadow( - blurRadius: 8.0, - color: StreamChatTheme.of(context) - .colorTheme - .black - .withOpacity(0.3), - ), - ], - ), - padding: const EdgeInsets.all(2), - child: UserAvatar( - user: widget.message.user, - constraints: - BoxConstraints.tight(Size(24, 24)), - showOnlineStatus: false, + if (widget.message.user != null) + Padding( + padding: EdgeInsets.all(8.0), + child: Container( + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.white.withOpacity(0.6), + boxShadow: [ + BoxShadow( + blurRadius: 8.0, + color: StreamChatTheme.of(context) + .colorTheme + .black + .withOpacity(0.3), + ), + ], + ), + padding: const EdgeInsets.all(2), + child: UserAvatar( + user: widget.message.user!, + constraints: + BoxConstraints.tight(Size(24, 24)), + showOnlineStatus: false, + ), ), ), - ), ], ); }, @@ -302,7 +293,7 @@ class _ImageFooterState extends State { _messageController.clear(); - for (var channel in _selectedChannels) { + for (final channel in _selectedChannels) { final message = Message( text: text, attachments: [attachments[widget.currentPage]], diff --git a/packages/stream_chat_flutter/lib/src/image_group.dart b/packages/stream_chat_flutter/lib/src/image_group.dart index 39c2883a..a08d13de 100644 --- a/packages/stream_chat_flutter/lib/src/image_group.dart +++ b/packages/stream_chat_flutter/lib/src/image_group.dart @@ -1,15 +1,15 @@ import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; import 'package:stream_chat_flutter/src/full_screen_media.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; +import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; class ImageGroup extends StatelessWidget { const ImageGroup({ - Key key, - @required this.images, - @required this.message, - @required this.messageTheme, - @required this.size, + Key? key, + required this.images, + required this.message, + required this.messageTheme, + required this.size, this.onShowMessage, }) : super(key: key); @@ -17,7 +17,7 @@ class ImageGroup extends StatelessWidget { final Message message; final MessageTheme messageTheme; final Size size; - final ShowMessageCallback onShowMessage; + final ShowMessageCallback? onShowMessage; @override Widget build(BuildContext context) { @@ -106,9 +106,9 @@ class ImageGroup extends StatelessWidget { } void _onTap( - BuildContext context, [ + BuildContext context, int index, - ]) { + ) { final channel = StreamChannel.of(context).channel; Navigator.push( @@ -119,8 +119,7 @@ class ImageGroup extends StatelessWidget { child: FullScreenMedia( mediaAttachments: images, startIndex: index, - userName: message.user.name, - sentAt: message.createdAt, + userName: message.user?.name, message: message, onShowMessage: onShowMessage, ), diff --git a/packages/stream_chat_flutter/lib/src/image_header.dart b/packages/stream_chat_flutter/lib/src/image_header.dart index 023c8eaa..6837722c 100644 --- a/packages/stream_chat_flutter/lib/src/image_header.dart +++ b/packages/stream_chat_flutter/lib/src/image_header.dart @@ -11,31 +11,29 @@ class ImageHeader extends StatelessWidget implements PreferredSizeWidget { /// Callback to call when pressing the back button. /// By default it calls [Navigator.pop] - final VoidCallback onBackPressed; + final VoidCallback? onBackPressed; /// Callback to call when pressing the show message button. - final VoidCallback onShowMessage; + final VoidCallback? onShowMessage; /// Callback to call when the header is tapped. - final VoidCallback onTitleTap; + final VoidCallback? onTitleTap; /// Callback to call when the image is tapped. - final VoidCallback onImageTap; + final VoidCallback? onImageTap; final Message message; final String userName; final String sentAt; - final List urls; - final currentIndex; + final int currentIndex; /// Creates a channel header ImageHeader({ - Key key, - this.message, - this.urls, - this.currentIndex, + Key? key, + required this.message, + this.currentIndex = 0, this.showBackButton = true, this.onBackPressed, this.onShowMessage, @@ -109,7 +107,7 @@ class ImageHeader extends StatelessWidget implements PreferredSizeWidget { void _showMessageActionModalBottomSheet(BuildContext context) async { final channel = StreamChannel.of(context).channel; - var result = await showDialog( + final result = await showDialog( context: context, barrierColor: StreamChatTheme.of(context).colorTheme.overlay, builder: (context) { diff --git a/packages/stream_chat_flutter/lib/src/info_tile.dart b/packages/stream_chat_flutter/lib/src/info_tile.dart index 72a077cc..deb30877 100644 --- a/packages/stream_chat_flutter/lib/src/info_tile.dart +++ b/packages/stream_chat_flutter/lib/src/info_tile.dart @@ -6,15 +6,15 @@ class InfoTile extends StatelessWidget { final String message; final Widget child; final bool showMessage; - final Alignment tileAnchor; - final Alignment childAnchor; - final TextStyle textStyle; - final Color backgroundColor; + final Alignment? tileAnchor; + final Alignment? childAnchor; + final TextStyle? textStyle; + final Color? backgroundColor; InfoTile({ - this.message, - this.child, - this.showMessage, + required this.message, + required this.child, + required this.showMessage, this.tileAnchor, this.childAnchor, this.textStyle, diff --git a/packages/stream_chat_flutter/lib/src/media_list_view.dart b/packages/stream_chat_flutter/lib/src/media_list_view.dart index ddaf2c24..703fb092 100644 --- a/packages/stream_chat_flutter/lib/src/media_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/media_list_view.dart @@ -21,10 +21,10 @@ extension on Duration { class MediaListView extends StatefulWidget { final List selectedIds; - final void Function(AssetEntity media) onSelect; + final void Function(AssetEntity media)? onSelect; const MediaListView({ - Key key, + Key? key, this.selectedIds = const [], this.onSelect, }) : super(key: key); @@ -58,7 +58,7 @@ class _MediaListViewState extends State { child: InkWell( onTap: () { if (widget.onSelect != null) { - widget.onSelect(media); + widget.onSelect!(media); } }, child: Stack( @@ -147,7 +147,7 @@ class _MediaListViewState extends State { final assetList = await PhotoManager.getAssetPathList( hasAll: true, ).then((value) { - if (value?.isNotEmpty == true) { + if (value.isNotEmpty == true) { return value.singleWhere((element) => element.isAll); } }); @@ -169,8 +169,8 @@ class _MediaListViewState extends State { class MediaThumbnailProvider extends ImageProvider { const MediaThumbnailProvider({ - @required this.media, - }) : assert(media != null); + required this.media, + }); final AssetEntity media; @@ -180,7 +180,7 @@ class MediaThumbnailProvider extends ImageProvider { codec: _loadAsync(key, decode), scale: 1.0, informationCollector: () sync* { - yield ErrorDescription('Id: ${media?.id}'); + yield ErrorDescription('Id: ${media.id}'); }, ); } @@ -189,9 +189,8 @@ class MediaThumbnailProvider extends ImageProvider { MediaThumbnailProvider key, DecoderCallback decode) async { assert(key == this); final bytes = await media.thumbData; - if (bytes?.isNotEmpty != true) return null; - return await decode(bytes); + return await decode(bytes!); } @override @@ -203,12 +202,12 @@ class MediaThumbnailProvider extends ImageProvider { bool operator ==(dynamic other) { if (other.runtimeType != runtimeType) return false; final MediaThumbnailProvider typedOther = other; - return media?.id == typedOther.media?.id; + return media.id == typedOther.media.id; } @override - int get hashCode => media?.id?.hashCode ?? 0; + int get hashCode => media.id.hashCode; @override - String toString() => '$runtimeType("${media?.id}")'; + String toString() => '$runtimeType("${media.id}")'; } diff --git a/packages/stream_chat_flutter/lib/src/mention_tile.dart b/packages/stream_chat_flutter/lib/src/mention_tile.dart index 7d8b0ada..37e4e9ad 100644 --- a/packages/stream_chat_flutter/lib/src/mention_tile.dart +++ b/packages/stream_chat_flutter/lib/src/mention_tile.dart @@ -9,16 +9,16 @@ class MentionTile extends StatelessWidget { final Member member; /// Widget to display as title - final Widget title; + final Widget? title; /// Widget to display below [title] - final Widget subtitle; + final Widget? subtitle; /// Widget at the start of the tile - final Widget leading; + final Widget? leading; /// Widget at the end of tile - final Widget trailing; + final Widget? trailing; MentionTile( this.member, { @@ -46,7 +46,7 @@ class MentionTile extends StatelessWidget { 40, ), ), - user: member.user, + user: member.user!, ), SizedBox( width: 8.0, @@ -60,7 +60,7 @@ class MentionTile extends StatelessWidget { children: [ title ?? Text( - '${member.user.name}', + '${member.user!.name}', maxLines: 1, overflow: TextOverflow.ellipsis, style: StreamChatTheme.of(context).textTheme.bodyBold, @@ -87,7 +87,10 @@ class MentionTile extends StatelessWidget { ), trailing ?? Padding( - padding: const EdgeInsets.only(right: 18.0, left: 8.0), + padding: const EdgeInsets.only( + right: 18.0, + left: 8.0, + ), child: StreamSvgIcon.mentions( color: StreamChatTheme.of(context).colorTheme.accentBlue, ), diff --git a/packages/stream_chat_flutter/lib/src/message_action.dart b/packages/stream_chat_flutter/lib/src/message_action.dart index 5cedc962..6d4bd93c 100644 --- a/packages/stream_chat_flutter/lib/src/message_action.dart +++ b/packages/stream_chat_flutter/lib/src/message_action.dart @@ -4,13 +4,13 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart'; /// Class describing a message action class MessageAction { /// leading widget - final Widget leading; + final Widget? leading; /// title widget - final Widget title; + final Widget? title; /// callback called on tap - final OnMessageTap onTap; + final OnMessageTap? onTap; /// returns a new instance of a [MessageAction] MessageAction({ diff --git a/packages/stream_chat_flutter/lib/src/message_actions_modal.dart b/packages/stream_chat_flutter/lib/src/message_actions_modal.dart index 0fdabf7d..b737bd09 100644 --- a/packages/stream_chat_flutter/lib/src/message_actions_modal.dart +++ b/packages/stream_chat_flutter/lib/src/message_actions_modal.dart @@ -16,13 +16,13 @@ import 'stream_chat.dart'; import 'stream_chat_theme.dart'; class MessageActionsModal extends StatefulWidget { - final Widget Function(BuildContext, Message) editMessageInputBuilder; - final OnMessageTap onThreadReplyTap; - final OnMessageTap onReplyTap; + final Widget Function(BuildContext, Message)? editMessageInputBuilder; + final OnMessageTap? onThreadReplyTap; + final OnMessageTap? onReplyTap; final Message message; final MessageTheme messageTheme; final bool showReactions; - final OnMessageTap onCopyTap; + final OnMessageTap? onCopyTap; final bool showDeleteMessage; final bool showCopyMessage; final bool showEditMessage; @@ -31,18 +31,18 @@ class MessageActionsModal extends StatefulWidget { final bool showThreadReplyMessage; final bool showFlagButton; final bool reverse; - final ShapeBorder messageShape; - final ShapeBorder attachmentShape; + final ShapeBorder? messageShape; + final ShapeBorder? attachmentShape; final DisplayWidget showUserAvatar; - final BorderRadius attachmentBorderRadiusGeometry; + final BorderRadius? attachmentBorderRadiusGeometry; /// List of custom actions final List customActions; const MessageActionsModal({ - Key key, - @required this.message, - @required this.messageTheme, + Key? key, + required this.message, + required this.messageTheme, this.showReactions = true, this.showDeleteMessage = true, this.showEditMessage = true, @@ -80,24 +80,26 @@ class _MessageActionsModalState extends State { final user = StreamChat.of(context).user; final roughMaxSize = 2 * size.width / 3; - var messageTextLength = widget.message.text.length; + var messageTextLength = widget.message.text!.length; if (widget.message.quotedMessage != null) { - var quotedMessageLength = widget.message.quotedMessage.text.length + 40; - if (widget.message.quotedMessage.attachments?.isNotEmpty == true) { + var quotedMessageLength = + (widget.message.quotedMessage!.text?.length ?? 0) + 40; + if (widget.message.quotedMessage!.attachments.isNotEmpty) { quotedMessageLength += 40; } if (quotedMessageLength > messageTextLength) { messageTextLength = quotedMessageLength; } } - final roughSentenceSize = - messageTextLength * widget.messageTheme.messageText.fontSize * 1.2; - final divFactor = widget.message.attachments?.isNotEmpty == true + final roughSentenceSize = messageTextLength * + (widget.messageTheme.messageText?.fontSize ?? 1) * + 1.2; + final divFactor = widget.message.attachments.isNotEmpty == true ? 1 : (roughSentenceSize == 0 ? 1 : (roughSentenceSize / roughMaxSize)); final hasFileAttachment = - widget.message.attachments?.any((it) => it.type == 'file') == true; + widget.message.attachments.any((it) => it.type == 'file') == true; return GestureDetector( behavior: HitTestBehavior.translucent, @@ -134,11 +136,10 @@ class _MessageActionsModalState extends State { children: [ if (widget.showReactions && (widget.message.status == - MessageSendingStatus.sent || - widget.message.status == null)) + MessageSendingStatus.sent)) Align( alignment: Alignment( - user.id == widget.message.user.id + user?.id == widget.message.user?.id ? (divFactor > 1.0 ? 0.0 : (1.0 - divFactor)) @@ -148,7 +149,6 @@ class _MessageActionsModalState extends State { 0.0), child: ReactionPicker( message: widget.message, - messageTheme: widget.messageTheme, ), ), SizedBox(height: 8), @@ -159,8 +159,8 @@ class _MessageActionsModalState extends State { attachmentBorderRadiusGeometry: widget.attachmentBorderRadiusGeometry, message: widget.message.copyWith( - text: widget.message.text.length > 200 - ? '${widget.message.text.substring(0, 200)}...' + text: widget.message.text!.length > 200 + ? '${widget.message.text!.substring(0, 200)}...' : widget.message.text, ), messageTheme: widget.messageTheme, @@ -177,15 +177,14 @@ class _MessageActionsModalState extends State { padding: const EdgeInsets.all(0), textPadding: EdgeInsets.symmetric( vertical: 8.0, - horizontal: widget.message.text.isOnlyEmoji + horizontal: widget.message.text!.isOnlyEmoji ? 0 : 16.0, ), showReactionPickerIndicator: widget.showReactions && (widget.message.status == - MessageSendingStatus.sent || - widget.message.status == null), + MessageSendingStatus.sent), showInChannelIndicator: false, showSendingIndicator: false, shape: widget.messageShape, @@ -212,14 +211,12 @@ class _MessageActionsModalState extends State { CrossAxisAlignment.stretch, children: [ if (widget.showReplyMessage && - widget.message.status == - MessageSendingStatus.sent || - widget.message.status == null) + widget.message.status == + MessageSendingStatus.sent) _buildReplyButton(context), if (widget.showThreadReplyMessage && (widget.message.status == - MessageSendingStatus.sent || - widget.message.status == null) && + MessageSendingStatus.sent) && widget.message.parentId == null) _buildThreadReplyButton(context), if (widget.showResendMessage) @@ -315,7 +312,7 @@ class _MessageActionsModalState extends State { okText: 'OK', ); } catch (err) { - if (json.decode(err?.body ?? {})['code'] == 4) { + if (err is ApiError && json.decode(err.body ?? '{}')['code'] == 4) { await showInfoDialog( context, icon: StreamSvgIcon.flag( @@ -337,7 +334,7 @@ class _MessageActionsModalState extends State { setState(() { _showActions = false; }); - var answer = await showConfirmationDialog( + final answer = await showConfirmationDialog( context, title: 'Delete message', icon: StreamSvgIcon.flag( @@ -349,7 +346,7 @@ class _MessageActionsModalState extends State { cancelText: 'CANCEL', ); - if (answer) { + if (answer == true) { try { Navigator.pop(context); await StreamChannel.of(context).channel.deleteMessage(widget.message); @@ -381,7 +378,7 @@ class _MessageActionsModalState extends State { onTap: () { Navigator.pop(context); if (widget.onReplyTap != null) { - widget.onReplyTap(widget.message); + widget.onReplyTap!(widget.message); } }, child: Padding( @@ -578,7 +575,7 @@ class _MessageActionsModalState extends State { ), ), widget.editMessageInputBuilder != null - ? widget.editMessageInputBuilder(context, widget.message) + ? widget.editMessageInputBuilder!(context, widget.message) : MessageInput( editMessage: widget.message, preMessageSending: (m) { @@ -599,7 +596,7 @@ class _MessageActionsModalState extends State { onTap: () { Navigator.pop(context); if (widget.onThreadReplyTap != null) { - widget.onThreadReplyTap(widget.message); + widget.onThreadReplyTap!(widget.message); } }, child: Padding( diff --git a/packages/stream_chat_flutter/lib/src/message_input.dart b/packages/stream_chat_flutter/lib/src/message_input.dart index b4b2176c..aa14882e 100644 --- a/packages/stream_chat_flutter/lib/src/message_input.dart +++ b/packages/stream_chat_flutter/lib/src/message_input.dart @@ -2,7 +2,6 @@ import 'dart:async'; import 'dart:math'; import 'package:cached_network_image/cached_network_image.dart'; -import 'package:emojis/emoji.dart'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; @@ -12,6 +11,7 @@ import 'package:flutter_svg/flutter_svg.dart'; import 'package:image_picker/image_picker.dart'; import 'package:photo_manager/photo_manager.dart'; import 'package:shimmer/shimmer.dart'; +import 'package:stream_chat_flutter/src/emoji/emoji.dart'; import 'package:stream_chat_flutter/src/media_list_view.dart'; import 'package:stream_chat_flutter/src/message_list_view.dart'; import 'package:stream_chat_flutter/src/stream_chat_theme.dart'; @@ -19,6 +19,7 @@ import 'package:stream_chat_flutter/src/stream_svg_icon.dart'; import 'package:stream_chat_flutter/src/video_service.dart'; import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; import 'package:substring_highlight/substring_highlight.dart'; +import 'package:video_compress/video_compress.dart'; import '../stream_chat_flutter.dart'; import 'attachment/attachment.dart'; @@ -109,7 +110,7 @@ const _kMaxAttachmentSize = 20971520; // 20MB in Bytes class MessageInput extends StatefulWidget { /// Instantiate a new MessageInput MessageInput({ - Key key, + Key? key, this.onMessageSent, this.preMessageSending, this.parentMessage, @@ -135,20 +136,20 @@ class MessageInput extends StatefulWidget { }) : super(key: key); /// Message to edit - final Message editMessage; + final Message? editMessage; /// Message to start with - final Message initialMessage; + final Message? initialMessage; /// Function called after sending the message - final void Function(Message) onMessageSent; + final void Function(Message)? onMessageSent; /// Function called right before sending the message /// Use this to transform the message - final FutureOr Function(Message) preMessageSending; + final FutureOr Function(Message)? preMessageSending; /// Parent message in case of a thread - final Message parentMessage; + final Message? parentMessage; /// Maximum Height for the TextField to grow before it starts scrolling final double maxHeight; @@ -166,25 +167,25 @@ class MessageInput extends StatefulWidget { final bool hideSendAsDm; /// The text controller of the TextField - final TextEditingController textEditingController; + final TextEditingController? textEditingController; /// List of action widgets - final List actions; + final List? actions; /// The location of the custom actions final ActionsLocation actionsLocation; /// Map that defines a thumbnail builder for an attachment type - final Map attachmentThumbnailBuilders; + final Map? attachmentThumbnailBuilders; /// The focus node associated to the TextField - final FocusNode focusNode; + final FocusNode? focusNode; /// - final Message quotedMessage; + final Message? quotedMessage; /// - final VoidCallback onQuotedMessageCleared; + final VoidCallback? onQuotedMessageCleared; /// The location of the send button final SendButtonLocation sendButtonLocation; @@ -193,20 +194,20 @@ class MessageInput extends StatefulWidget { final bool autofocus; /// Send button widget in an idle state - final Widget idleSendButton; + final Widget? idleSendButton; /// Send button widget in an active state - final Widget activeSendButton; + final Widget? activeSendButton; /// Customize the tile for the mentions overlay - final MentionTileBuilder mentionsTileBuilder; + final MentionTileBuilder? mentionsTileBuilder; @override MessageInputState createState() => MessageInputState(); /// Use this method to get the current [StreamChatState] instance static MessageInputState of(BuildContext context) { - MessageInputState messageInputState; + MessageInputState? messageInputState; messageInputState = context.findAncestorStateOfType(); @@ -224,15 +225,15 @@ class MessageInputState extends State { final List _mentionedUsers = []; final _imagePicker = ImagePicker(); - FocusNode _focusNode; + late final FocusNode _focusNode; bool _inputEnabled = true; bool _messageIsPresent = false; bool _animateContainer = true; bool _commandEnabled = false; - OverlayEntry _commandsOverlay, _mentionsOverlay, _emojiOverlay; - Iterable _emojiNames; + OverlayEntry? _commandsOverlay, _mentionsOverlay, _emojiOverlay; + late Iterable _emojiNames; - Command _chosenCommand; + Command? _chosenCommand; bool _actionsShrunk = false; bool _sendAsDm = false; bool _openFilePickerSection = false; @@ -242,7 +243,7 @@ class MessageInputState extends State { KeyboardVisibilityController(); /// The editing controller passed to the input TextField - TextEditingController textEditingController; + late final TextEditingController textEditingController; bool get _hasQuotedMessage => widget.quotedMessage != null; @@ -250,7 +251,8 @@ class MessageInputState extends State { void initState() { super.initState(); _focusNode = widget.focusNode ?? FocusNode(); - _emojiNames = Emoji.all().map((e) => e.name); + _emojiNames = + Emoji.all().where((it) => it.name != null).map((e) => e.name!); if (!kIsWeb) { _keyboardListener = @@ -264,7 +266,7 @@ class MessageInputState extends State { textEditingController = widget.textEditingController ?? TextEditingController(); if (widget.editMessage != null || widget.initialMessage != null) { - _parseExistingMessage(widget.editMessage ?? widget.initialMessage); + _parseExistingMessage(widget.editMessage ?? widget.initialMessage!); } textEditingController.addListener(() { @@ -447,7 +449,7 @@ class MessageInputState extends State { firstChild: sendButton, secondChild: widget.idleSendButton ?? _buildIdleSendButton(context), duration: - StreamChatTheme.of(context).messageInputTheme.sendAnimationDuration, + StreamChatTheme.of(context).messageInputTheme.sendAnimationDuration!, alignment: Alignment.center, ); } @@ -490,9 +492,9 @@ class MessageInputState extends State { widget.editMessage == null && StreamChannel.of(context) .channel - ?.config + .config ?.commands - ?.isNotEmpty == + .isNotEmpty == true) _buildCommandButton(), ...widget.actions ?? [], @@ -571,7 +573,7 @@ class MessageInputState extends State { return InputDecoration( isDense: true, hintText: _getHint(), - hintStyle: theme.messageInputTheme.inputTextStyle.copyWith( + hintStyle: theme.messageInputTheme.inputTextStyle!.copyWith( color: theme.colorTheme.grey, ), border: OutlineInputBorder( @@ -621,7 +623,7 @@ class MessageInputState extends State { size: 16.0, ), Text( - _chosenCommand?.name?.toUpperCase() ?? '', + _chosenCommand?.name.toUpperCase() ?? '', style: StreamChatTheme.of(context) .textTheme .footnoteBold @@ -676,9 +678,9 @@ class MessageInputState extends State { ).merge(passedDecoration); } - Timer _debounce; + Timer? _debounce; - String _previousValue; + String? _previousValue; void _onChanged(BuildContext context, String s) { if (s == _previousValue) { @@ -686,14 +688,14 @@ class MessageInputState extends State { } _previousValue = s; - if (_debounce?.isActive == true) _debounce.cancel(); + if (_debounce?.isActive == true) _debounce!.cancel(); _debounce = Timer( const Duration(milliseconds: 350), () { if (!mounted) { return; } - StreamChannel.of(context).channel.keyStroke()?.catchError((e) {}); + StreamChannel.of(context).channel.keyStroke().catchError((e) {}); setState(() { _messageIsPresent = s.trim().isNotEmpty; @@ -721,7 +723,7 @@ class MessageInputState extends State { } String _getHint() { - if (_commandEnabled && _chosenCommand.name == 'giphy') { + if (_commandEnabled && _chosenCommand!.name == 'giphy') { return 'Search GIFs'; } if (_attachments.isNotEmpty) { @@ -742,7 +744,7 @@ class MessageInputState extends State { final textToSelection = textEditingController.text .substring(0, textEditingController.value.selection.start); final splits = textToSelection.split(':'); - final query = splits[splits.length - 2]?.toLowerCase(); + final query = splits[splits.length - 2].toLowerCase(); final emoji = Emoji.byName(query); if (textToSelection.endsWith(':') && emoji != null) { @@ -751,7 +753,7 @@ class MessageInputState extends State { _emojiOverlay = _buildEmojiOverlay(); if (_emojiOverlay != null) { - Overlay.of(context).insert(_emojiOverlay); + Overlay.of(context)!.insert(_emojiOverlay!); } } } @@ -767,7 +769,7 @@ class MessageInputState extends State { .contains('@')) { _mentionsOverlay = _buildMentionsOverlayEntry(); if (_mentionsOverlay != null) { - Overlay.of(context).insert(_mentionsOverlay); + Overlay.of(context)!.insert(_mentionsOverlay!); } } } @@ -778,8 +780,8 @@ class MessageInputState extends State { .channel .config ?.commands - ?.where((element) => element.name == s.substring(1)) - ?.toList() ?? + .where((element) => element.name == s.substring(1)) + .toList() ?? []; if (matchedCommandsList.length == 1) { @@ -789,32 +791,32 @@ class MessageInputState extends State { setState(() { _commandEnabled = true; }); - _commandsOverlay.remove(); + _commandsOverlay!.remove(); _commandsOverlay = null; } else { _commandsOverlay = _buildCommandsOverlayEntry(); if (_commandsOverlay != null) { - Overlay.of(context).insert(_commandsOverlay); + Overlay.of(context)!.insert(_commandsOverlay!); } } } } - OverlayEntry _buildCommandsOverlayEntry() { + OverlayEntry? _buildCommandsOverlayEntry() { final text = textEditingController.text.trimLeft(); final commands = StreamChannel.of(context) .channel .config ?.commands - ?.where((c) => c.name.contains(text.replaceFirst('/', ''))) - ?.toList() ?? + .where((c) => c.name.contains(text.replaceFirst('/', ''))) + .toList() ?? []; if (commands.isEmpty) { return null; } - RenderBox renderBox = context.findRenderObject(); + final renderBox = context.findRenderObject() as RenderBox; final size = renderBox.size; return OverlayEntry(builder: (context) { @@ -951,7 +953,6 @@ class MessageInputState extends State { .colorTheme .black .withOpacity(0.2)); - break; case 1: return _attachmentContainsFile ? StreamChatTheme.of(context).colorTheme.accentBlue @@ -964,17 +965,14 @@ class MessageInputState extends State { .colorTheme .black .withOpacity(0.2)); - break; case 2: return _attachmentContainsFile && _attachments.isNotEmpty ? StreamChatTheme.of(context).colorTheme.black.withOpacity(0.2) : StreamChatTheme.of(context).colorTheme.black.withOpacity(0.5); - break; case 3: return _attachmentContainsFile && _attachments.isNotEmpty ? StreamChatTheme.of(context).colorTheme.black.withOpacity(0.2) : StreamChatTheme.of(context).colorTheme.black.withOpacity(0.5); - break; default: return Colors.black; } @@ -1111,17 +1109,22 @@ class MessageInputState extends State { onTimeout: () => medium.originFile, ); + if (mediaFile == null) { + return; + } + var file = AttachmentFile( path: mediaFile.path, size: await mediaFile.length(), bytes: mediaFile.readAsBytesSync(), ); - if (file.size > _kMaxAttachmentSize) { - if (medium?.type == AssetType.video) { - final mediaInfo = await VideoService.compressVideo(file.path); + if (file.size! > _kMaxAttachmentSize) { + if (medium.type == AssetType.video) { + final mediaInfo = await (VideoService.compressVideo(file.path) + as FutureOr); - if (mediaInfo.filesize > _kMaxAttachmentSize) { + if (mediaInfo.filesize! > _kMaxAttachmentSize) { _showErrorAlert( 'The file is too large to upload. The file size limit is 20MB. We tried compressing it, but it was not enough.', ); @@ -1129,8 +1132,8 @@ class MessageInputState extends State { } file = AttachmentFile( name: file.name, - size: mediaInfo.filesize, - bytes: await mediaInfo.file.readAsBytes(), + size: mediaInfo.filesize!, + bytes: await mediaInfo.file?.readAsBytes(), path: mediaInfo.path, ); } else { @@ -1159,7 +1162,6 @@ class MessageInputState extends State { size: 24.0, ), ); - break; case 'ban': return CircleAvatar( backgroundColor: StreamChatTheme.of(context).colorTheme.accentBlue, @@ -1169,7 +1171,6 @@ class MessageInputState extends State { color: Colors.white, ), ); - break; case 'flag': return CircleAvatar( backgroundColor: StreamChatTheme.of(context).colorTheme.accentBlue, @@ -1179,7 +1180,6 @@ class MessageInputState extends State { color: Colors.white, ), ); - break; case 'imgur': return CircleAvatar( backgroundColor: StreamChatTheme.of(context).colorTheme.accentBlue, @@ -1190,7 +1190,6 @@ class MessageInputState extends State { ), ), ); - break; case 'mute': return CircleAvatar( backgroundColor: StreamChatTheme.of(context).colorTheme.accentBlue, @@ -1200,7 +1199,6 @@ class MessageInputState extends State { color: Colors.white, ), ); - break; case 'unban': return CircleAvatar( backgroundColor: StreamChatTheme.of(context).colorTheme.accentBlue, @@ -1210,7 +1208,6 @@ class MessageInputState extends State { color: Colors.white, ), ); - break; case 'unmute': return CircleAvatar( backgroundColor: StreamChatTheme.of(context).colorTheme.accentBlue, @@ -1220,7 +1217,6 @@ class MessageInputState extends State { color: Colors.white, ), ); - break; default: return CircleAvatar( backgroundColor: StreamChatTheme.of(context).colorTheme.accentBlue, @@ -1230,36 +1226,34 @@ class MessageInputState extends State { color: Colors.white, ), ); - break; } } - OverlayEntry _buildMentionsOverlayEntry() { + OverlayEntry? _buildMentionsOverlayEntry() { final splits = textEditingController.text .substring(0, textEditingController.value.selection.start) .split('@'); final query = splits.last.toLowerCase(); - Future> queryMembers; + Future>? queryMembers; if (query.isNotEmpty) { - queryMembers = StreamChannel.of(context).channel.queryMembers(filter: { - 'name': { - '\$autocomplete': query, - }, - }).then((res) => res.members); + queryMembers = StreamChannel.of(context) + .channel + .queryMembers(filter: Filter.autoComplete('name', query)) + .then((res) => res.members); } - final members = StreamChannel.of(context).channel.state.members?.where((m) { - return m.user.name.toLowerCase().contains(query); - })?.toList() ?? + final members = StreamChannel.of(context).channel.state?.members.where((m) { + return m.user?.name.toLowerCase().contains(query) == true; + }).toList() ?? []; if (members.isEmpty) { return null; } - RenderBox renderBox = context.findRenderObject(); + final renderBox = context.findRenderObject() as RenderBox; final size = renderBox.size; return OverlayEntry( @@ -1299,7 +1293,9 @@ class MessageInputState extends State { SizedBox( height: 8.0, ), - ...snapshot.data.map( + ...snapshot.data! + .where((it) => it.user != null) + .map( (m) { return Material( color: StreamChatTheme.of(context) @@ -1307,9 +1303,11 @@ class MessageInputState extends State { .white, child: InkWell( onTap: () { - _mentionedUsers.add(m.user); + if (m.user != null) { + _mentionedUsers.add(m.user!); + } - splits[splits.length - 1] = m.user.name; + splits[splits.length - 1] = m.user!.name; final rejoin = splits.join('@'); textEditingController.value = @@ -1322,12 +1320,13 @@ class MessageInputState extends State { offset: rejoin.length, ), ); - _debounce.cancel(); + _debounce!.cancel(); _mentionsOverlay?.remove(); _mentionsOverlay = null; }, child: widget.mentionsTileBuilder != null - ? widget.mentionsTileBuilder(context, m) + ? widget.mentionsTileBuilder!( + context, m) : MentionTile(m), ), ); @@ -1350,7 +1349,7 @@ class MessageInputState extends State { ); } - OverlayEntry _buildEmojiOverlay() { + OverlayEntry? _buildEmojiOverlay() { final splits = textEditingController.text .substring(0, textEditingController.value.selection.start) .split(':'); @@ -1369,7 +1368,7 @@ class MessageInputState extends State { return null; } - RenderBox renderBox = context.findRenderObject(); + final renderBox = context.findRenderObject() as RenderBox; final size = renderBox.size; return OverlayEntry(builder: (context) { @@ -1432,19 +1431,20 @@ class MessageInputState extends State { ); } - final emoji = emojis.elementAt(i - 1); + final emoji = emojis.elementAt(i - 1)!; return ListTile( title: SubstringHighlight( - text: "${emoji.char} ${emoji.name.replaceAll('_', ' ')}", + text: "${emoji.char} ${emoji.name!.replaceAll('_', ' ')}", term: query, textStyleHighlight: - Theme.of(context).textTheme.headline6.copyWith( + Theme.of(context).textTheme.headline6!.copyWith( fontSize: 14.5, fontWeight: FontWeight.bold, ), - textStyle: Theme.of(context).textTheme.headline6.copyWith( - fontSize: 14.5, - ), + textStyle: + Theme.of(context).textTheme.headline6!.copyWith( + fontSize: 14.5, + ), ), onTap: () { _chooseEmoji(splits, emoji); @@ -1458,7 +1458,7 @@ class MessageInputState extends State { } void _chooseEmoji(List splits, Emoji emoji) { - final rejoin = splits.sublist(0, splits.length - 1).join(':') + emoji.char; + final rejoin = splits.sublist(0, splits.length - 1).join(':') + emoji.char!; textEditingController.value = TextEditingValue( text: rejoin + @@ -1486,8 +1486,8 @@ class MessageInputState extends State { Widget _buildReplyToMessage() { if (!_hasQuotedMessage) return Offstage(); - final containsUrl = widget.quotedMessage.attachments - ?.any((element) => element.ogScrapeUrl != null) == + final containsUrl = widget.quotedMessage!.attachments + .any((element) => element.ogScrapeUrl != null) == true; return Transform( transform: Matrix4.rotationY(pi), @@ -1495,7 +1495,7 @@ class MessageInputState extends State { child: QuotedMessageWidget( reverse: true, showBorder: !containsUrl, - message: widget.quotedMessage, + message: widget.quotedMessage!, messageTheme: StreamChatTheme.of(context).otherMessageTheme, padding: const EdgeInsets.fromLTRB(8, 8, 8, 0), ), @@ -1526,7 +1526,7 @@ class MessageInputState extends State { borderRadius: BorderRadius.circular(10), clipBehavior: Clip.antiAlias, child: FileAttachment( - message: null, + message: Message(), // dummy message attachment: e, size: Size( MediaQuery.of(context).size.width * 0.65, @@ -1610,11 +1610,9 @@ class MessageInputState extends State { } Widget _buildAttachment(Attachment attachment) { - if (attachment == null) return Offstage(); - if (widget.attachmentThumbnailBuilders?.containsKey(attachment.type) == true) { - return widget.attachmentThumbnailBuilders[attachment.type]( + return widget.attachmentThumbnailBuilders![attachment.type!]!( context, attachment, ); @@ -1625,7 +1623,7 @@ class MessageInputState extends State { case 'giphy': return attachment.file != null ? Image.memory( - attachment.file.bytes, + attachment.file!.bytes!, fit: BoxFit.cover, errorBuilder: (context, _, __) { return Image.asset( @@ -1637,10 +1635,11 @@ class MessageInputState extends State { : CachedNetworkImage( imageUrl: attachment.imageUrl ?? attachment.assetUrl ?? - attachment.thumbUrl, + attachment.thumbUrl!, fit: BoxFit.cover, errorWidget: (_, obj, trace) { - return getFileTypeImage(attachment.extraData['other']); + return getFileTypeImage( + attachment.extraData['other'] as String?); }, progressIndicatorBuilder: (context, _, progress) { return Shimmer.fromColors( @@ -1662,7 +1661,7 @@ class MessageInputState extends State { VideoThumbnailImage( height: 104, width: 104, - video: attachment.file?.path ?? attachment.assetUrl, + video: (attachment.file?.path ?? attachment.assetUrl)!, fit: BoxFit.cover, ), Positioned( @@ -1718,7 +1717,7 @@ class MessageInputState extends State { setState(() { _commandsOverlay = _buildCommandsOverlayEntry(); if (_commandsOverlay != null) { - Overlay.of(context).insert(_commandsOverlay); + Overlay.of(context)!.insert(_commandsOverlay!); } }); } else { @@ -1853,7 +1852,7 @@ class MessageInputState extends State { void addAttachment(Attachment attachment) { setState(() { _attachments[attachment.id] = attachment.copyWith( - uploadState: attachment.uploadState ?? UploadState.success(), + uploadState: attachment.uploadState, ); }); } @@ -1863,8 +1862,8 @@ class MessageInputState extends State { void pickFile(DefaultAttachmentTypes fileType, [bool camera = false]) async { setState(() => _inputEnabled = false); - AttachmentFile file; - String attachmentType; + AttachmentFile? file; + String? attachmentType; if (fileType == DefaultAttachmentTypes.image) { attachmentType = 'image'; @@ -1875,7 +1874,7 @@ class MessageInputState extends State { } if (camera) { - PickedFile pickedFile; + PickedFile? pickedFile; if (fileType == DefaultAttachmentTypes.image) { pickedFile = await _imagePicker.getImage(source: ImageSource.camera); } else if (fileType == DefaultAttachmentTypes.video) { @@ -1891,7 +1890,7 @@ class MessageInputState extends State { bytes: bytes, ); } else { - FileType type; + late FileType type; if (fileType == DefaultAttachmentTypes.image) { type = FileType.image; } else if (fileType == DefaultAttachmentTypes.video) { @@ -1903,8 +1902,8 @@ class MessageInputState extends State { type: type, withData: true, ); - if (res?.files?.isNotEmpty == true) { - file = res.files.single.toAttachmentFile; + if (res?.files.isNotEmpty == true) { + file = res!.files.single.toAttachmentFile; } } @@ -1912,29 +1911,28 @@ class MessageInputState extends State { if (file == null) return; - final mimeType = file.name?.mimeType ?? file.path.split('/').last.mimeType; + final mimeType = file.name?.mimeType ?? file.path!.split('/').last.mimeType; - final extraDataMap = {}; + final extraDataMap = {}; if (mimeType?.subtype != null) { - extraDataMap['mime_type'] = mimeType.subtype.toLowerCase(); + extraDataMap['mime_type'] = mimeType!.subtype.toLowerCase(); } - if (file.size != null) { - extraDataMap['file_size'] = file.size; - } + extraDataMap['file_size'] = file.size!; final attachment = Attachment( file: file, type: attachmentType, - extraData: extraDataMap.isNotEmpty ? extraDataMap : null, + extraData: extraDataMap, ); - if (file.size > _kMaxAttachmentSize) { + if (file.size! > _kMaxAttachmentSize) { if (attachmentType == 'Video') { - final mediaInfo = await VideoService.compressVideo(file.path); + final mediaInfo = await (VideoService.compressVideo(file.path) + as FutureOr); - if (mediaInfo.filesize > _kMaxAttachmentSize) { + if (mediaInfo.filesize! > _kMaxAttachmentSize) { _showErrorAlert( 'The file is too large to upload. The file size limit is 20MB. We tried compressing it, but it was not enough.', ); @@ -1942,8 +1940,8 @@ class MessageInputState extends State { } file = AttachmentFile( name: file.name, - size: mediaInfo.filesize, - bytes: await mediaInfo.file.readAsBytes(), + size: mediaInfo.filesize!, + bytes: await mediaInfo.file!.readAsBytes(), path: mediaInfo.path, ); } else { @@ -1960,7 +1958,8 @@ class MessageInputState extends State { _attachments.update(attachment.id, (it) { return it.copyWith( file: file, - extraData: {...it.extraData}..update('file_size', (_) => file.size), + extraData: {...it.extraData} + ..update('file_size', ((_) => file!.size!)), ); }); }); @@ -2024,7 +2023,7 @@ class MessageInputState extends State { final shouldUnfocus = _commandEnabled; if (_commandEnabled) { - text = '/${_chosenCommand.name} ' + text; + text = '/${_chosenCommand!.name} ' + text; } final attachments = [..._attachments.values]; @@ -2032,7 +2031,7 @@ class MessageInputState extends State { textEditingController.clear(); _attachments.clear(); if (widget.onQuotedMessageCleared != null) { - widget.onQuotedMessageCleared(); + widget.onQuotedMessageCleared!(); } setState(() { @@ -2048,7 +2047,7 @@ class MessageInputState extends State { Future sendingFuture; Message message; if (widget.editMessage != null) { - message = widget.editMessage.copyWith( + message = widget.editMessage!.copyWith( text: text, attachments: attachments, mentionedUsers: @@ -2067,25 +2066,25 @@ class MessageInputState extends State { if (widget.quotedMessage != null) { message = message.copyWith( - quotedMessageId: widget.quotedMessage.id, + quotedMessageId: widget.quotedMessage!.id, ); } if (widget.preMessageSending != null) { - message = await widget.preMessageSending(message); + message = await widget.preMessageSending!(message); } final streamChannel = StreamChannel.of(context); final channel = streamChannel.channel; - if (!channel.state.isUpToDate) { + if (!channel.state!.isUpToDate) { await streamChannel.reloadChannel(); } _mentionedUsers.clear(); if (widget.editMessage == null || - widget.editMessage.status == MessageSendingStatus.failed || - widget.editMessage.status == MessageSendingStatus.sending) { + widget.editMessage!.status == MessageSendingStatus.failed || + widget.editMessage!.status == MessageSendingStatus.sending) { sendingFuture = channel.sendMessage(message); } else { sendingFuture = channel.updateMessage(message); @@ -2100,12 +2099,12 @@ class MessageInputState extends State { _parseExistingMessage(message); } if (widget.onMessageSent != null) { - widget.onMessageSent(resp.message); + widget.onMessageSent!(resp.message); } }); } - StreamSubscription _keyboardListener; + StreamSubscription? _keyboardListener; void _showErrorAlert(String description) { showModalBottomSheet( @@ -2179,14 +2178,12 @@ class MessageInputState extends State { } void _parseExistingMessage(Message message) { - textEditingController.text = message.text; + textEditingController.text = message.text!; _messageIsPresent = true; - if (message.attachments != null) { - for (final attachment in message.attachments) { - _attachments[attachment.id] = attachment.copyWith( - uploadState: attachment.uploadState ?? UploadState.success(), - ); - } + for (final attachment in message.attachments) { + _attachments[attachment.id] = attachment.copyWith( + uploadState: attachment.uploadState, + ); } } @@ -2267,12 +2264,12 @@ class _PickerWidget extends StatefulWidget { final void Function(AssetEntity) onMediaSelected; const _PickerWidget({ - Key key, - @required this.filePickerIndex, - @required this.containsFile, - @required this.selectedMedias, - @required this.onAddMoreFilesClick, - @required this.onMediaSelected, + Key? key, + required this.filePickerIndex, + required this.containsFile, + required this.selectedMedias, + required this.onAddMoreFilesClick, + required this.onMediaSelected, }) : super(key: key); @override @@ -2280,7 +2277,7 @@ class _PickerWidget extends StatefulWidget { } class __PickerWidgetState extends State<_PickerWidget> { - Future requestPermission; + Future? requestPermission; @override void initState() { @@ -2300,7 +2297,7 @@ class __PickerWidgetState extends State<_PickerWidget> { return const Center(child: CircularProgressIndicator()); } - if (snapshot.data) { + if (snapshot.data!) { if (widget.containsFile) { return GestureDetector( onTap: () { diff --git a/packages/stream_chat_flutter/lib/src/message_list_view.dart b/packages/stream_chat_flutter/lib/src/message_list_view.dart index 423acc3e..01c067da 100644 --- a/packages/stream_chat_flutter/lib/src/message_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/message_list_view.dart @@ -26,14 +26,14 @@ typedef MessageBuilder = Widget Function( ); typedef ParentMessageBuilder = Widget Function( BuildContext, - Message, + Message?, ); typedef SystemMessageBuilder = Widget Function( BuildContext, Message, ); -typedef ThreadBuilder = Widget Function(BuildContext context, Message parent); -typedef ThreadTapCallback = void Function(Message, Widget); +typedef ThreadBuilder = Widget Function(BuildContext context, Message? parent); +typedef ThreadTapCallback = void Function(Message, Widget?); typedef OnMessageSwiped = void Function(Message); typedef OnMessageTap = void Function(Message); @@ -41,13 +41,13 @@ typedef ReplyTapCallback = void Function(Message); class MessageDetails { /// True if the message belongs to the current user - bool isMyMessage; + bool? isMyMessage; /// True if the user message is the same of the previous message - bool isLastUser; + bool? isLastUser; /// True if the user message is the same of the next message - bool isNextUser; + bool? isNextUser; /// The message Message message; @@ -61,11 +61,11 @@ class MessageDetails { List messages, this.index, ) { - isMyMessage = message.user.id == StreamChat.of(context).user.id; + isMyMessage = message.user?.id == StreamChat.of(context).user?.id; isLastUser = index + 1 < messages.length && - message.user.id == messages[index + 1]?.user?.id; + message.user?.id == messages[index + 1].user?.id; isNextUser = - index - 1 >= 0 && message.user.id == messages[index - 1]?.user?.id; + index - 1 >= 0 && message.user!.id == messages[index - 1].user?.id; } } @@ -112,7 +112,7 @@ class MessageDetails { class MessageListView extends StatefulWidget { /// Instantiate a new MessageListView MessageListView({ - Key key, + Key? key, this.showScrollToBottom = true, this.messageBuilder, this.parentMessageBuilder, @@ -146,52 +146,52 @@ class MessageListView extends StatefulWidget { }) : super(key: key); /// Function used to build a custom message widget - final MessageBuilder messageBuilder; + final MessageBuilder? messageBuilder; /// Function used to build a custom system message widget - final SystemMessageBuilder systemMessageBuilder; + final SystemMessageBuilder? systemMessageBuilder; /// Function used to build a custom parent message widget - final ParentMessageBuilder parentMessageBuilder; + final ParentMessageBuilder? parentMessageBuilder; /// Function used to build a custom thread widget - final ThreadBuilder threadBuilder; + final ThreadBuilder? threadBuilder; /// Function called when tapping on a thread /// By default it calls [Navigator.push] using the widget built using [threadBuilder] - final ThreadTapCallback onThreadTap; + final ThreadTapCallback? onThreadTap; /// If true will show a scroll to bottom message when there are new messages and the scroll offset is not zero final bool showScrollToBottom; /// Parent message in case of a thread - final Message parentMessage; + final Message? parentMessage; /// Builder used to render date dividers - final Widget Function(DateTime) dateDividerBuilder; + final Widget Function(DateTime)? dateDividerBuilder; /// Index of an item to initially align within the viewport. - final int initialScrollIndex; + final int? initialScrollIndex; /// Determines where the leading edge of the item at [initialScrollIndex] /// should be placed. - final double initialAlignment; + final double? initialAlignment; /// Controller for jumping or scrolling to an item. - final ItemScrollController scrollController; + final ItemScrollController? scrollController; /// Provides a listenable iterable of [itemPositions] of items that are on /// screen and their locations. - final ItemPositionsListener itemPositionListener; + final ItemPositionsListener? itemPositionListener; /// The ScrollPhysics used by the ListView final ScrollPhysics scrollPhysics; /// Called when message item gets swiped - final OnMessageSwiped onMessageSwiped; + final OnMessageSwiped? onMessageSwiped; /// - final ReplyTapCallback onReplyTap; + final ReplyTapCallback? onReplyTap; /// If true the list will highlight the initialMessage if there is any. /// @@ -199,66 +199,66 @@ class MessageListView extends StatefulWidget { final bool highlightInitialMessage; /// Color used while highlighting initial message - final Color messageHighlightColor; + final Color? messageHighlightColor; - final ShowMessageCallback onShowMessage; + final ShowMessageCallback? onShowMessage; final bool showConnectionStateTile; /// Function called when messages are fetched - final Widget Function(BuildContext, List) messageListBuilder; + final Widget Function(BuildContext, List)? messageListBuilder; /// Function used to build a loading widget - final WidgetBuilder loadingBuilder; + final WidgetBuilder? loadingBuilder; /// Function used to build an empty widget - final WidgetBuilder emptyBuilder; + final WidgetBuilder? emptyBuilder; /// Callback triggered when an error occurs while performing the given request. /// This parameter can be used to display an error message to users in the event /// of a connection failure. - final ErrorBuilder errorWidgetBuilder; + final ErrorBuilder? errorWidgetBuilder; /// Predicate used to filter messages - final bool Function(Message) messageFilter; + final bool Function(Message)? messageFilter; /// Attachment builders for the default message widget /// Please change this in the [MessageWidget] if you are using a custom implementation - final Map customAttachmentBuilders; + final Map? customAttachmentBuilders; /// Called when any message is tapped except a system message (use [onSystemMessageTap] instead) - final OnMessageTap onMessageTap; + final OnMessageTap? onMessageTap; /// Called when system message is tapped - final OnMessageTap onSystemMessageTap; + final OnMessageTap? onSystemMessageTap; /// Customize onTap on attachment - final void Function(Message message, Attachment attachment) onAttachmentTap; + final void Function(Message message, Attachment attachment)? onAttachmentTap; /// Customize the MessageWidget textBuilder - final void Function(BuildContext context, Message message) textBuilder; + final void Function(BuildContext context, Message message)? textBuilder; - final void Function(String link) onLinkTap; + final void Function(String link)? onLinkTap; @override _MessageListViewState createState() => _MessageListViewState(); } class _MessageListViewState extends State { - ItemScrollController _scrollController; - Function _onThreadTap; + ItemScrollController? _scrollController; + Function? _onThreadTap; bool _showScrollToBottom = false; - ItemPositionsListener _itemPositionListener; - int _messageListLength; - StreamChannelState streamChannel; + late final ItemPositionsListener _itemPositionListener; + int? _messageListLength; + StreamChannelState? streamChannel; - int get _initialIndex { + int? get _initialIndex { if (widget.initialScrollIndex != null) return widget.initialScrollIndex; - if (streamChannel.initialMessageId != null) { - final messages = streamChannel.channel.state.messages; + if (streamChannel!.initialMessageId != null) { + final messages = streamChannel!.channel.state!.messages; final totalMessages = messages.length; final messageIndex = messages.indexWhere((e) { - return e.id == streamChannel.initialMessageId; + return e.id == streamChannel!.initialMessageId; }); final index = totalMessages - messageIndex; if (index != 0) return index - 1; @@ -267,24 +267,24 @@ class _MessageListViewState extends State { return 0; } - double get _initialAlignment { + double? get _initialAlignment { if (widget.initialAlignment != null) return widget.initialAlignment; return 0; } bool _isInitialMessage(String id) { - return streamChannel.initialMessageId == id; + return streamChannel!.initialMessageId == id; } - bool get _upToDate => streamChannel.channel.state.isUpToDate; + bool get _upToDate => streamChannel!.channel.state!.isUpToDate; bool get _isThreadConversation => widget.parentMessage != null; bool _topPaginationActive = false; bool _bottomPaginationActive = false; - int initialIndex; - double initialAlignment; + int? initialIndex; + double? initialAlignment; List messages = []; @@ -346,9 +346,9 @@ class _MessageListViewState extends State { if (_messageListLength != null) { if (_bottomPaginationActive || (_inBetweenList && _upToDate)) { - if (_itemPositionListener.itemPositions.value?.isNotEmpty == true) { + if (_itemPositionListener.itemPositions.value.isNotEmpty == true) { final first = _itemPositionListener.itemPositions.value.first; - final diff = newMessagesListLength - _messageListLength; + final diff = newMessagesListLength - _messageListLength!; if (diff > 0) { initialIndex = first.index + diff; initialAlignment = first.itemLeadingEdge; @@ -416,7 +416,7 @@ class _MessageListViewState extends State { _inBetweenList = true; }, child: ScrollablePositionedList.separated( - key: ValueKey(initialIndex + initialAlignment), + key: ValueKey(initialIndex! + initialAlignment!), itemPositionsListener: _itemPositionListener, addAutomaticKeepAlives: true, initialScrollIndex: initialIndex ?? 0, @@ -430,7 +430,7 @@ class _MessageListViewState extends State { if (i == messages.length) return Offstage(); if (i == 0) return SizedBox(height: 30); if (i == messages.length + 1) { - final replyCount = widget.parentMessage.replyCount; + final replyCount = widget.parentMessage!.replyCount; return Container( decoration: BoxDecoration( gradient: @@ -457,7 +457,7 @@ class _MessageListViewState extends State { Units.DAY, )) { final divider = widget.dateDividerBuilder != null - ? widget.dateDividerBuilder( + ? widget.dateDividerBuilder!( nextMessage.createdAt.toLocal(), ) : DateDivider( @@ -475,8 +475,8 @@ class _MessageListViewState extends State { ); final isNextUserSame = - message.user.id == nextMessage.user?.id; - final isThread = message.replyCount > 0; + message.user!.id == nextMessage.user?.id; + final isThread = message.replyCount! > 0; final isDeleted = message.isDeleted; if (timeDiff >= 1 || !isNextUserSame || @@ -489,12 +489,12 @@ class _MessageListViewState extends State { itemBuilder: (context, i) { if (i == messages.length + 2) { if (widget.parentMessageBuilder != null) { - return widget.parentMessageBuilder( + return widget.parentMessageBuilder!( context, widget.parentMessage, ); } else { - return buildParentMessage(widget.parentMessage); + return buildParentMessage(widget.parentMessage!); } } if (i == messages.length + 1) { @@ -531,7 +531,7 @@ class _MessageListViewState extends State { if (widget.messageBuilder != null) { messageWidget = Builder( key: ValueKey('MESSAGE-${message.id}'), - builder: (context) => widget.messageBuilder( + builder: (context) => widget.messageBuilder!( context, MessageDetails( context, @@ -558,7 +558,7 @@ class _MessageListViewState extends State { child: ValueListenableBuilder>( valueListenable: _itemPositionListener.itemPositions, builder: (context, values, _) { - final items = _itemPositionListener.itemPositions?.value; + final items = _itemPositionListener.itemPositions.value; if (items.isEmpty || messages.isEmpty) { return SizedBox(); } @@ -574,7 +574,7 @@ class _MessageListViewState extends State { } return widget.dateDividerBuilder != null - ? widget.dateDividerBuilder( + ? widget.dateDividerBuilder!( messages[index].createdAt.toLocal(), ) : DateDivider( @@ -588,8 +588,8 @@ class _MessageListViewState extends State { } Future _paginateData( - StreamChannelState channel, QueryDirection direction) { - return _messageListController.paginateData(direction: direction); + StreamChannelState? channel, QueryDirection direction) { + return _messageListController.paginateData!(direction: direction); } ItemPosition _getTopElement(Iterable values) { @@ -602,8 +602,8 @@ class _MessageListViewState extends State { Widget _buildScrollToBottom() { return StreamBuilder>( stream: Rx.combineLatest2( - streamChannel.channel.state.isUpToDateStream, - streamChannel.channel.state.unreadCountStream, + streamChannel!.channel.state!.isUpToDateStream, + streamChannel!.channel.state!.unreadCountStream, (bool isUpToDate, int unreadCount) => Tuple2(isUpToDate, unreadCount), ), builder: (_, snapshot) { @@ -612,15 +612,15 @@ class _MessageListViewState extends State { } else if (!snapshot.hasData) { return Offstage(); } - final isUpToDate = snapshot.data.item1; + final isUpToDate = snapshot.data!.item1; final showScrollToBottom = !isUpToDate || _showScrollToBottom; if (!showScrollToBottom) { return Offstage(); } - final unreadCount = snapshot.data.item2; + final unreadCount = snapshot.data!.item2; final showUnreadCount = unreadCount > 0 && - streamChannel.channel.state.members.any( - (e) => e.userId == streamChannel.channel.client.state.user.id); + streamChannel!.channel.state!.members.any((e) => + e.userId == streamChannel!.channel.client.state.user!.id); return Positioned( bottom: 8, right: 8, @@ -633,15 +633,15 @@ class _MessageListViewState extends State { backgroundColor: StreamChatTheme.of(context).colorTheme.white, onPressed: () { if (unreadCount > 0) { - streamChannel.channel.markRead(); + streamChannel!.channel.markRead(); } if (!_upToDate) { _bottomPaginationActive = false; _topPaginationActive = false; - streamChannel.reloadChannel(); + streamChannel!.reloadChannel(); } else { setState(() => _showScrollToBottom = false); - _scrollController.scrollTo( + _scrollController!.scrollTo( index: 0, duration: Duration(seconds: 1), curve: Curves.easeInOut, @@ -679,12 +679,12 @@ class _MessageListViewState extends State { } Widget _buildLoadingIndicator( - StreamChannelState streamChannel, + StreamChannelState? streamChannel, QueryDirection direction, ) { final stream = direction == QueryDirection.top - ? streamChannel.queryTopMessages - : streamChannel.queryBottomMessages; + ? streamChannel!.queryTopMessages + : streamChannel!.queryBottomMessages; return StreamBuilder( key: Key('LOADING-INDICATOR'), stream: stream, @@ -701,7 +701,7 @@ class _MessageListViewState extends State { ), ); } - if (!snapshot.data) { + if (!snapshot.data!) { if (!_isThreadConversation && direction == QueryDirection.top) { return Container( height: 52, @@ -724,13 +724,13 @@ class _MessageListViewState extends State { BuildContext context, Message message, List messages, - StreamChannelState streamChannel, + StreamChannelState? streamChannel, ) { Widget messageWidget; if (widget.messageBuilder != null) { messageWidget = Builder( key: ValueKey('TOP-MESSAGE'), - builder: (_) => widget.messageBuilder( + builder: (_) => widget.messageBuilder!( context, MessageDetails( context, @@ -751,13 +751,13 @@ class _MessageListViewState extends State { BuildContext context, Message message, List messages, - StreamChannelState streamChannel, + StreamChannelState? streamChannel, ) { Widget messageWidget; if (widget.messageBuilder != null) { messageWidget = Builder( key: ValueKey('BOTTOM-MESSAGE-${message.id}'), - builder: (_) => widget.messageBuilder( + builder: (_) => widget.messageBuilder!( context, MessageDetails( context, @@ -777,10 +777,10 @@ class _MessageListViewState extends State { onVisibilityChanged: (visibility) { final isVisible = visibility.visibleBounds != Rect.zero; if (isVisible) { - final channel = streamChannel.channel; + final channel = streamChannel!.channel; if (_upToDate && channel.config?.readEvents == true && - channel.state.unreadCount > 0) { + channel.state!.unreadCount! > 0) { streamChannel.channel.markRead(); } } @@ -795,8 +795,8 @@ class _MessageListViewState extends State { Widget buildParentMessage( Message message, ) { - final isMyMessage = message.user.id == StreamChat.of(context).user.id; - final isOnlyEmoji = message.text.isOnlyEmoji; + final isMyMessage = message.user!.id == StreamChat.of(context).user!.id; + final isOnlyEmoji = message.text!.isOnlyEmoji; return MessageWidget( showThreadReplyIndicator: false, @@ -812,7 +812,7 @@ class _MessageListViewState extends State { showUsername: !isMyMessage, padding: const EdgeInsets.all(8.0), showSendingIndicator: false, - onThreadTap: _onThreadTap, + onThreadTap: _onThreadTap as void Function(Message)?, borderRadiusGeometry: BorderRadius.only( topLeft: Radius.circular(16), bottomLeft: Radius.circular(2), @@ -835,18 +835,19 @@ class _MessageListViewState extends State { break; case ReturnActionType.reply: FocusScope.of(context).unfocus(); - widget.onMessageSwiped(message); + widget.onMessageSwiped?.call(message); break; } }, customAttachmentBuilders: widget.customAttachmentBuilders, onMessageTap: (message) { if (widget.onMessageTap != null) { - widget.onMessageTap(message); + widget.onMessageTap!(message); } FocusScope.of(context).unfocus(); }, - textBuilder: widget.textBuilder, + textBuilder: + widget.textBuilder as Widget Function(BuildContext, Message)?, onLinkTap: widget.onLinkTap, ); } @@ -864,18 +865,18 @@ class _MessageListViewState extends State { message: message, onMessageTap: (message) { if (widget.onSystemMessageTap != null) { - widget.onSystemMessageTap(message); + widget.onSystemMessageTap!(message); } FocusScope.of(context).unfocus(); }, ); } - final userId = StreamChat.of(context).user.id; - final isMyMessage = message.user.id == userId; + final userId = StreamChat.of(context).user!.id; + final isMyMessage = message.user!.id == userId; final nextMessage = index - 2 >= 0 ? messages[index - 2] : null; final isNextUserSame = - nextMessage != null && message.user.id == nextMessage.user.id; + nextMessage != null && message.user!.id == nextMessage.user!.id; num timeDiff = 0; if (nextMessage != null) { @@ -885,27 +886,26 @@ class _MessageListViewState extends State { ); } - final channel = streamChannel.channel; + final channel = streamChannel!.channel; final readList = channel.state?.read?.where((read) { if (read.user.id == userId) return false; return (read.lastRead.isAfter(message.createdAt) || read.lastRead.isAtSameMomentAs(message.createdAt)); - })?.toList() ?? + }).toList() ?? []; final allRead = readList.length >= (channel.memberCount ?? 0) - 1; final hasFileAttachment = - message.attachments?.any((it) => it.type == 'file') == true; + message.attachments.any((it) => it.type == 'file') == true; final isThreadMessage = - message?.parentId != null && message?.showInChannel == true; + message.parentId != null && message.showInChannel == true; - final hasReplies = message.replyCount > 0; + final hasReplies = message.replyCount! > 0; final attachmentBorderRadius = hasFileAttachment ? 12.0 : 14.0; - final showTimeStamp = message.createdAt != null && - (!isThreadMessage || _isThreadConversation) && + final showTimeStamp = (!isThreadMessage || _isThreadConversation) && !hasReplies && (timeDiff >= 1 || !isNextUserSame); @@ -925,10 +925,10 @@ class _MessageListViewState extends State { final showInChannelIndicator = !_isThreadConversation && isThreadMessage; final showThreadReplyIndicator = !_isThreadConversation && hasReplies; - final isOnlyEmoji = message.text.isOnlyEmoji; + final isOnlyEmoji = message.text!.isOnlyEmoji; final hasUrlAttachment = - message.attachments?.any((it) => it.ogScrapeUrl != null) == true; + message.attachments.any((it) => it.ogScrapeUrl != null) == true; final borderSide = isOnlyEmoji || hasUrlAttachment || (isMyMessage && !hasFileAttachment) @@ -958,8 +958,8 @@ class _MessageListViewState extends State { if (messages.map((e) => e.id).contains(quotedMessageId)) { scrollToIndex(); } else { - await streamChannel.loadChannelAtMessage(quotedMessageId).then((_) { - WidgetsBinding.instance.addPostFrameCallback((_) { + await streamChannel!.loadChannelAtMessage(quotedMessageId).then((_) { + WidgetsBinding.instance!.addPostFrameCallback((_) { if (messages.map((e) => e.id).contains(quotedMessageId)) { scrollToIndex(); } @@ -972,7 +972,7 @@ class _MessageListViewState extends State { showThreadReplyMessage: !isThreadMessage, showFlagButton: !isMyMessage, borderSide: borderSide, - onThreadTap: _onThreadTap, + onThreadTap: _onThreadTap as void Function(Message)?, onReplyTap: widget.onReplyTap, attachmentBorderRadiusGeometry: BorderRadius.only( topLeft: Radius.circular(attachmentBorderRadius), @@ -1012,19 +1012,20 @@ class _MessageListViewState extends State { break; case ReturnActionType.reply: FocusScope.of(context).unfocus(); - widget.onMessageSwiped(message); + widget.onMessageSwiped?.call(message); break; } }, customAttachmentBuilders: widget.customAttachmentBuilders, onMessageTap: (message) { if (widget.onMessageTap != null) { - widget.onMessageTap(message); + widget.onMessageTap!(message); } FocusScope.of(context).unfocus(); }, onAttachmentTap: widget.onAttachmentTap, - textBuilder: widget.textBuilder, + textBuilder: + widget.textBuilder as Widget Function(BuildContext, Message)?, onLinkTap: widget.onLinkTap, ); @@ -1038,7 +1039,7 @@ class _MessageListViewState extends State { child: Swipeable( onSwipeEnd: () { FocusScope.of(context).unfocus(); - widget.onMessageSwiped(message); + widget.onMessageSwiped?.call(message); }, backgroundIcon: StreamSvgIcon.reply( color: StreamChatTheme.of(context).colorTheme.accentBlue, @@ -1054,7 +1055,7 @@ class _MessageListViewState extends State { final colorTheme = StreamChatTheme.of(context).colorTheme; final highlightColor = widget.messageHighlightColor ?? colorTheme.highlight; - child = TweenAnimationBuilder( + child = TweenAnimationBuilder( tween: ColorTween( begin: highlightColor, end: colorTheme.white.withOpacity(0), @@ -1076,7 +1077,7 @@ class _MessageListViewState extends State { return child; } - StreamSubscription _messageNewListener; + StreamSubscription? _messageNewListener; @override void initState() { @@ -1090,13 +1091,14 @@ class _MessageListViewState extends State { initialAlignment = _initialAlignment; _messageNewListener = - streamChannel.channel.on(EventType.messageNew).listen((event) { + streamChannel!.channel.on(EventType.messageNew).listen((event) { if (_upToDate) { _bottomPaginationActive = false; _topPaginationActive = false; } - if (event.message.user.id == streamChannel.channel.client.state.user.id) { - WidgetsBinding.instance.addPostFrameCallback((_) { + if (event.message!.user!.id == + streamChannel!.channel.client.state.user!.id) { + WidgetsBinding.instance!.addPostFrameCallback((_) { _scrollController?.jumpTo( index: 0, ); @@ -1105,7 +1107,7 @@ class _MessageListViewState extends State { }); if (_isThreadConversation) { - streamChannel.getReplies(widget.parentMessage.id); + streamChannel!.getReplies(widget.parentMessage!.id); } _getOnThreadTap(); @@ -1115,10 +1117,10 @@ class _MessageListViewState extends State { void _getOnThreadTap() { if (widget.onThreadTap != null) { _onThreadTap = (Message message) { - widget.onThreadTap( + widget.onThreadTap!( message, widget.threadBuilder != null - ? widget.threadBuilder(context, message) + ? widget.threadBuilder!(context, message) : null); }; } else if (widget.threadBuilder != null) { @@ -1127,14 +1129,14 @@ class _MessageListViewState extends State { context, MaterialPageRoute(builder: (_) { return StreamBuilder( - stream: streamChannel.channel.state.messagesStream.map( + stream: streamChannel!.channel.state!.messagesStream.map( (messages) => - messages.firstWhere((m) => m.id == message.id)), + messages!.firstWhere((m) => m.id == message.id)), initialData: message, builder: (_, snapshot) { return StreamChannel( - channel: streamChannel.channel, - child: widget.threadBuilder(context, snapshot.data), + channel: streamChannel!.channel, + child: widget.threadBuilder!(context, snapshot.data), ); }); }), @@ -1146,7 +1148,7 @@ class _MessageListViewState extends State { @override void dispose() { if (!_upToDate) { - streamChannel.reloadChannel(); + streamChannel!.reloadChannel(); } _messageNewListener?.cancel(); super.dispose(); diff --git a/packages/stream_chat_flutter/lib/src/message_reactions_modal.dart b/packages/stream_chat_flutter/lib/src/message_reactions_modal.dart index e2171078..9fa35cb6 100644 --- a/packages/stream_chat_flutter/lib/src/message_reactions_modal.dart +++ b/packages/stream_chat_flutter/lib/src/message_reactions_modal.dart @@ -18,15 +18,15 @@ class MessageReactionsModal extends StatelessWidget { final bool reverse; final bool showReactions; final DisplayWidget showUserAvatar; - final ShapeBorder messageShape; - final ShapeBorder attachmentShape; - final void Function(User) onUserAvatarTap; - final BorderRadius attachmentBorderRadiusGeometry; + final ShapeBorder? messageShape; + final ShapeBorder? attachmentShape; + final void Function(User)? onUserAvatarTap; + final BorderRadius? attachmentBorderRadiusGeometry; const MessageReactionsModal({ - Key key, - @required this.message, - @required this.messageTheme, + Key? key, + required this.message, + required this.messageTheme, this.showReactions = true, this.messageShape, this.attachmentShape, @@ -42,10 +42,10 @@ class MessageReactionsModal extends StatelessWidget { final user = StreamChat.of(context).user; final roughMaxSize = 2 * size.width / 3; - var messageTextLength = message.text.length; + var messageTextLength = message.text!.length; if (message.quotedMessage != null) { - var quotedMessageLength = message.quotedMessage.text.length + 40; - if (message.quotedMessage.attachments?.isNotEmpty == true) { + var quotedMessageLength = message.quotedMessage!.text!.length + 40; + if (message.quotedMessage!.attachments.isNotEmpty == true) { quotedMessageLength += 40; } if (quotedMessageLength > messageTextLength) { @@ -53,8 +53,8 @@ class MessageReactionsModal extends StatelessWidget { } } final roughSentenceSize = - messageTextLength * messageTheme.messageText.fontSize * 1.2; - final divFactor = message.attachments?.isNotEmpty == true + messageTextLength * (messageTheme.messageText?.fontSize ?? 1) * 1.2; + final divFactor = message.attachments.isNotEmpty == true ? 1 : (roughSentenceSize == 0 ? 1 : (roughSentenceSize / roughMaxSize)); @@ -64,7 +64,7 @@ class MessageReactionsModal extends StatelessWidget { curve: Curves.easeInOutBack, builder: (context, val, snapshot) { final hasFileAttachment = - message.attachments?.any((it) => it.type == 'file') == true; + message.attachments.any((it) => it.type == 'file') == true; return GestureDetector( behavior: HitTestBehavior.translucent, onTap: () => Navigator.maybePop(context), @@ -92,11 +92,10 @@ class MessageReactionsModal extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.stretch, children: [ if (showReactions && - (message.status == MessageSendingStatus.sent || - message.status == null)) + (message.status == MessageSendingStatus.sent)) Align( alignment: Alignment( - user.id == message.user.id + user!.id == message.user!.id ? (divFactor > 1.0 ? 0.0 : (1.0 - divFactor)) @@ -106,7 +105,6 @@ class MessageReactionsModal extends StatelessWidget { 0.0), child: ReactionPicker( message: message, - messageTheme: messageTheme, ), ), const SizedBox(height: 8), @@ -115,8 +113,8 @@ class MessageReactionsModal extends StatelessWidget { key: Key('MessageWidget'), reverse: reverse, message: message.copyWith( - text: message.text.length > 200 - ? '${message.text.substring(0, 200)}...' + text: message.text!.length > 200 + ? '${message.text!.substring(0, 200)}...' : message.text, ), messageTheme: messageTheme, @@ -138,12 +136,11 @@ class MessageReactionsModal extends StatelessWidget { showInChannelIndicator: false, textPadding: EdgeInsets.symmetric( vertical: 8.0, - horizontal: message.text.isOnlyEmoji ? 0 : 16.0, + horizontal: + message.text!.isOnlyEmoji ? 0 : 16.0, ), showReactionPickerIndicator: showReactions && - (message.status == - MessageSendingStatus.sent || - message.status == null), + (message.status == MessageSendingStatus.sent), ), ), if (message.latestReactions?.isNotEmpty == true) ...[ @@ -188,10 +185,10 @@ class MessageReactionsModal extends StatelessWidget { spacing: 16, runSpacing: 16, alignment: WrapAlignment.start, - children: message.latestReactions + children: message.latestReactions! .map((e) => _buildReaction( e, - currentUser, + currentUser!, context, )) .toList(), @@ -209,7 +206,7 @@ class MessageReactionsModal extends StatelessWidget { User currentUser, BuildContext context, ) { - final isCurrentUser = reaction.user.id == currentUser.id; + final isCurrentUser = reaction.user?.id == currentUser.id; return ConstrainedBox( constraints: BoxConstraints.loose(Size( 64, @@ -225,7 +222,7 @@ class MessageReactionsModal extends StatelessWidget { children: [ UserAvatar( onTap: onUserAvatarTap, - user: reaction.user, + user: reaction.user!, constraints: BoxConstraints.tightFor( height: 64, width: 64, @@ -246,8 +243,10 @@ class MessageReactionsModal extends StatelessWidget { child: ReactionBubble( reactions: [reaction], flipTail: !reverse, - borderColor: messageTheme.reactionsBorderColor, - backgroundColor: messageTheme.reactionsBackgroundColor, + borderColor: + messageTheme.reactionsBorderColor ?? Colors.transparent, + backgroundColor: messageTheme.reactionsBackgroundColor ?? + Colors.transparent, maskColor: StreamChatTheme.of(context).colorTheme.white, tailCirclesSpacing: 1, highlightOwnReactions: false, @@ -258,7 +257,7 @@ class MessageReactionsModal extends StatelessWidget { ), const SizedBox(height: 8), Text( - reaction.user.name.split(' ')[0], + reaction.user!.name.split(' ')[0], style: StreamChatTheme.of(context).textTheme.footnoteBold, textAlign: TextAlign.center, ), diff --git a/packages/stream_chat_flutter/lib/src/message_search_item.dart b/packages/stream_chat_flutter/lib/src/message_search_item.dart index 4cf7b5ed..87b55aaa 100644 --- a/packages/stream_chat_flutter/lib/src/message_search_item.dart +++ b/packages/stream_chat_flutter/lib/src/message_search_item.dart @@ -12,8 +12,8 @@ import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; class MessageSearchItem extends StatelessWidget { /// Instantiate a new MessageSearchItem const MessageSearchItem({ - Key key, - @required this.getMessageResponse, + Key? key, + required this.getMessageResponse, this.onTap, this.showOnlineStatus = true, }) : super(key: key); @@ -22,7 +22,7 @@ class MessageSearchItem extends StatelessWidget { final GetMessageResponse getMessageResponse; /// Function called when tapping this widget - final VoidCallback onTap; + final VoidCallback? onTap; /// If true the [MessageSearchItem] will show the current online Status final bool showOnlineStatus; @@ -31,8 +31,8 @@ class MessageSearchItem extends StatelessWidget { Widget build(BuildContext context) { final message = getMessageResponse.message; final channel = getMessageResponse.channel; - final channelName = channel.extraData['name']; - final user = message.user; + final channelName = channel?.extraData['name']; + final user = message.user!; return ListTile( onTap: onTap, leading: UserAvatar( @@ -46,7 +46,7 @@ class MessageSearchItem extends StatelessWidget { title: Row( children: [ Text( - user.id == StreamChat.of(context).user.id ? 'You' : user.name, + user.id == StreamChat.of(context).user?.id ? 'You' : user.name, style: StreamChatTheme.of(context).channelPreviewTheme.title, ), if (channelName != null) ...[ @@ -55,12 +55,12 @@ class MessageSearchItem extends StatelessWidget { style: StreamChatTheme.of(context) .channelPreviewTheme .title - .copyWith( + ?.copyWith( fontWeight: FontWeight.normal, ), ), Text( - channelName, + channelName as String, style: StreamChatTheme.of(context).channelPreviewTheme.title, ), ], @@ -96,14 +96,10 @@ class MessageSearchItem extends StatelessWidget { } Widget _buildSubtitle(BuildContext context, Message message) { - if (message == null) { - return SizedBox(); - } - var text = message.text; if (message.isDeleted) { text = 'This message was deleted.'; - } else if (message.attachments != null) { + } else if (message.attachments.isNotEmpty) { final parts = [ ...message.attachments.map((e) { if (e.type == 'image') { @@ -116,7 +112,7 @@ class MessageSearchItem extends StatelessWidget { return e == message.attachments.last ? (e.title ?? 'File') : '${e.title ?? 'File'} , '; - }).where((e) => e != null), + }), message.text ?? '', ]; @@ -125,15 +121,15 @@ class MessageSearchItem extends StatelessWidget { return Text.rich( _getDisplayText( - text, + text!, message.mentionedUsers, message.attachments, - StreamChatTheme.of(context).channelPreviewTheme.subtitle.copyWith( + StreamChatTheme.of(context).channelPreviewTheme.subtitle?.copyWith( fontStyle: (message.isSystem || message.isDeleted) ? FontStyle.italic : FontStyle.normal, ), - StreamChatTheme.of(context).channelPreviewTheme.subtitle.copyWith( + StreamChatTheme.of(context).channelPreviewTheme.subtitle?.copyWith( fontStyle: (message.isSystem || message.isDeleted) ? FontStyle.italic : FontStyle.normal, @@ -149,26 +145,24 @@ class MessageSearchItem extends StatelessWidget { String text, List mentions, List attachments, - TextStyle normalTextStyle, - TextStyle mentionsTextStyle) { + TextStyle? normalTextStyle, + TextStyle? mentionsTextStyle) { var textList = text.split(' '); var resList = []; for (var e in textList) { - if (mentions != null && - mentions.isNotEmpty && + if (mentions.isNotEmpty && mentions.any((element) => '@${element.name}' == e)) { resList.add(TextSpan( text: '$e ', style: mentionsTextStyle, )); - } else if (attachments != null && - attachments.isNotEmpty && + } else if (attachments.isNotEmpty && attachments .where((e) => e.title != null) .any((element) => element.title == e)) { resList.add(TextSpan( text: '$e ', - style: normalTextStyle.copyWith(fontStyle: FontStyle.italic), + style: normalTextStyle?.copyWith(fontStyle: FontStyle.italic), )); } else { resList.add(TextSpan( diff --git a/packages/stream_chat_flutter/lib/src/message_search_list_view.dart b/packages/stream_chat_flutter/lib/src/message_search_list_view.dart index 6431a311..358623e1 100644 --- a/packages/stream_chat_flutter/lib/src/message_search_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/message_search_list_view.dart @@ -10,11 +10,15 @@ typedef MessageSearchItemTapCallback = void Function(GetMessageResponse); /// Builder used to create a custom [ListUserItem] from a [User] typedef MessageSearchItemBuilder = Widget Function( - BuildContext, GetMessageResponse); + BuildContext, + GetMessageResponse, +); /// Builder used when [MessageSearchListView] is empty typedef EmptyMessageSearchBuilder = Widget Function( - BuildContext context, String searchQuery); + BuildContext context, + String searchQuery, +); /// /// It shows the list of searched messages. @@ -47,9 +51,9 @@ typedef EmptyMessageSearchBuilder = Widget Function( class MessageSearchListView extends StatefulWidget { /// Instantiate a new MessageSearchListView const MessageSearchListView({ - Key key, + Key? key, + required this.filters, this.messageQuery, - this.filters, this.sortOptions, this.paginationParams, this.messageFilters, @@ -66,38 +70,38 @@ class MessageSearchListView extends StatefulWidget { }) : super(key: key); /// Message String to search on - final String messageQuery; + final String? messageQuery; /// The query filters to use. /// You can query on any of the custom fields you've defined on the [Channel]. /// You can also filter other built-in channel fields. - final Map filters; + final Filter filters; /// The sorting used for the channels matching the filters. /// Sorting is based on field and direction, multiple sorting options can be provided. /// You can sort based on last_updated, last_message_at, updated_at, created_at or member_count. /// Direction can be ascending or descending. - final List sortOptions; + final List? sortOptions; /// Pagination parameters /// limit: the number of users to return (max is 30) /// offset: the offset (max is 1000) /// message_limit: how many messages should be included to each channel - final PaginationParams paginationParams; + final PaginationParams? paginationParams; /// The message query filters to use. /// You can query on any of the custom fields you've defined on the [Channel]. /// You can also filter other built-in channel fields. - final Map messageFilters; + final Filter? messageFilters; /// Builder used to create a custom item preview - final MessageSearchItemBuilder itemBuilder; + final MessageSearchItemBuilder? itemBuilder; /// Function called when tapping on a [MessageSearchItem] - final MessageSearchItemTapCallback onItemTap; + final MessageSearchItemTapCallback? onItemTap; /// Builder used to create a custom item separator - final IndexedWidgetBuilder separatorBuilder; + final IndexedWidgetBuilder? separatorBuilder; /// Set it to false to hide total results text final bool showResultCount; @@ -108,16 +112,16 @@ class MessageSearchListView extends StatefulWidget { final bool showErrorTile; /// The builder that is used when the search messages are fetched - final Widget Function(List) childBuilder; + final Widget Function(List)? childBuilder; /// The builder used when the channel list is empty. - final WidgetBuilder emptyBuilder; + final WidgetBuilder? emptyBuilder; /// The builder that will be used in case of error - final ErrorBuilder errorBuilder; + final ErrorBuilder? errorBuilder; /// The builder that will be used in case of loading - final WidgetBuilder loadingBuilder; + final WidgetBuilder? loadingBuilder; @override _MessageSearchListViewState createState() => _MessageSearchListViewState(); @@ -202,11 +206,11 @@ class _MessageSearchListViewState extends State { Widget _listItemBuilder( BuildContext context, GetMessageResponse getMessageResponse) { if (widget.itemBuilder != null) { - return widget.itemBuilder(context, getMessageResponse); + return widget.itemBuilder!(context, getMessageResponse); } return MessageSearchItem( getMessageResponse: getMessageResponse, - onTap: () => widget.onItemTap(getMessageResponse), + onTap: () => widget.onItemTap!(getMessageResponse), ); } @@ -235,7 +239,7 @@ class _MessageSearchListViewState extends State { height: 100, padding: EdgeInsets.all(32), child: Center( - child: snapshot.data ? CircularProgressIndicator() : Container(), + child: snapshot.data! ? CircularProgressIndicator() : Container(), ), ); }); @@ -249,7 +253,7 @@ class _MessageSearchListViewState extends State { itemCount: items.isNotEmpty ? items.length + 1 : items.length, separatorBuilder: (_, index) { if (widget.separatorBuilder != null) { - return widget.separatorBuilder(context, index); + return widget.separatorBuilder!(context, index); } return _separatorBuilder(context, index); }, @@ -262,13 +266,13 @@ class _MessageSearchListViewState extends State { ); if (widget.pullToRefresh) { child = RefreshIndicator( - onRefresh: () => _messageSearchListController.loadData(), + onRefresh: () => _messageSearchListController.loadData!(), child: child, ); } child = LazyLoadScrollView( - onEndOfPage: () => _messageSearchListController.paginateData(), + onEndOfPage: () => _messageSearchListController.paginateData!(), child: child, ); diff --git a/packages/stream_chat_flutter/lib/src/message_text.dart b/packages/stream_chat_flutter/lib/src/message_text.dart index 3afa60f9..b0099eff 100644 --- a/packages/stream_chat_flutter/lib/src/message_text.dart +++ b/packages/stream_chat_flutter/lib/src/message_text.dart @@ -1,3 +1,4 @@ +import 'package:collection/collection.dart' show IterableExtension; import 'package:flutter/material.dart'; import 'package:flutter_markdown/flutter_markdown.dart'; import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; @@ -7,43 +8,45 @@ import 'utils.dart'; class MessageText extends StatelessWidget { const MessageText({ - Key key, - @required this.message, - @required this.messageTheme, + Key? key, + required this.message, + required this.messageTheme, this.onMentionTap, this.onLinkTap, }) : super(key: key); final Message message; - final void Function(User) onMentionTap; - final void Function(String) onLinkTap; + final void Function(User)? onMentionTap; + final void Function(String)? onLinkTap; final MessageTheme messageTheme; @override Widget build(BuildContext context) { - final text = _replaceMentions(message.text).replaceAll('\n', '\\\n'); + final text = _replaceMentions(message.text ?? '').replaceAll('\n', '\\\n'); return MarkdownBody( data: text, onTapLink: ( String link, - String href, + String? href, String title, ) { if (link.startsWith('@')) { - final mentionedUser = message.mentionedUsers.firstWhere( + final mentionedUser = message.mentionedUsers.firstWhereOrNull( (u) => '@${u.name}' == link, - orElse: () => null, ); + if (mentionedUser == null) { + return; + } if (onMentionTap != null) { - onMentionTap(mentionedUser); + onMentionTap!(mentionedUser); } else { print('tap on ${mentionedUser.name}'); } } else { if (onLinkTap != null) { - onLinkTap(link); + onLinkTap!(link); } else { launchURL(context, link); } @@ -52,11 +55,11 @@ class MessageText extends StatelessWidget { styleSheet: MarkdownStyleSheet.fromTheme( Theme.of(context).copyWith( textTheme: Theme.of(context).textTheme.apply( - bodyColor: messageTheme.messageText.color, - decoration: messageTheme.messageText.decoration, - decorationColor: messageTheme.messageText.decorationColor, - decorationStyle: messageTheme.messageText.decorationStyle, - fontFamily: messageTheme.messageText.fontFamily, + bodyColor: messageTheme.messageText?.color, + decoration: messageTheme.messageText?.decoration, + decorationColor: messageTheme.messageText?.decorationColor, + decorationStyle: messageTheme.messageText?.decorationStyle, + fontFamily: messageTheme.messageText?.fontFamily, ), ), ).copyWith( @@ -67,7 +70,7 @@ class MessageText extends StatelessWidget { } String _replaceMentions(String text) { - message.mentionedUsers?.map((u) => u.name)?.toSet()?.forEach((userName) { + message.mentionedUsers.map((u) => u.name).toSet().forEach((userName) { text = text.replaceAll( '@$userName', '[@$userName](@${userName.replaceAll(' ', '')})'); }); diff --git a/packages/stream_chat_flutter/lib/src/message_widget.dart b/packages/stream_chat_flutter/lib/src/message_widget.dart index 09feeb74..b05762ba 100644 --- a/packages/stream_chat_flutter/lib/src/message_widget.dart +++ b/packages/stream_chat_flutter/lib/src/message_widget.dart @@ -26,7 +26,7 @@ typedef AttachmentBuilder = Widget Function( Message, List, ); -typedef OnQuotedMessageTap = void Function(String); +typedef OnQuotedMessageTap = void Function(String?); /// The display behaviour of a widget enum DisplayWidget { @@ -51,16 +51,16 @@ enum DisplayWidget { /// Modify it to change the widget appearance. class MessageWidget extends StatefulWidget { /// Function called on mention tap - final void Function(User) onMentionTap; + final void Function(User)? onMentionTap; /// The function called when tapping on replies - final void Function(Message) onThreadTap; - final void Function(Message) onReplyTap; - final Widget Function(BuildContext, Message) editMessageInputBuilder; - final Widget Function(BuildContext, Message) textBuilder; + final void Function(Message)? onThreadTap; + final void Function(Message)? onReplyTap; + final Widget Function(BuildContext, Message)? editMessageInputBuilder; + final Widget Function(BuildContext, Message)? textBuilder; /// Function called on long press - final void Function(BuildContext, Message) onMessageActions; + final void Function(BuildContext, Message)? onMessageActions; /// The message final Message message; @@ -72,28 +72,28 @@ class MessageWidget extends StatefulWidget { final bool reverse; /// The shape of the message text - final ShapeBorder shape; + final ShapeBorder? shape; /// The shape of an attachment - final ShapeBorder attachmentShape; + final ShapeBorder? attachmentShape; /// The borderside of the message text - final BorderSide borderSide; + final BorderSide? borderSide; /// The borderside of an attachment - final BorderSide attachmentBorderSide; + final BorderSide? attachmentBorderSide; /// The border radius of the message text - final BorderRadiusGeometry borderRadiusGeometry; + final BorderRadiusGeometry? borderRadiusGeometry; /// The border radius of an attachment - final BorderRadiusGeometry attachmentBorderRadiusGeometry; + final BorderRadiusGeometry? attachmentBorderRadiusGeometry; /// The padding of the widget - final EdgeInsetsGeometry padding; + final EdgeInsetsGeometry? padding; /// The internal padding of the message text - final EdgeInsetsGeometry textPadding; + final EdgeInsets textPadding; /// The internal padding of an attachment final EdgeInsetsGeometry attachmentPadding; @@ -116,18 +116,18 @@ class MessageWidget extends StatefulWidget { final bool showInChannelIndicator; /// The function called when tapping on UserAvatar - final void Function(User) onUserAvatarTap; + final void Function(User)? onUserAvatarTap; /// The function called when tapping on a link - final void Function(String) onLinkTap; + final void Function(String)? onLinkTap; /// Used in [MessageReactionsModal] and [MessageActionsModal] final bool showReactionPickerIndicator; - final List readList; + final List? readList; - final ShowMessageCallback onShowMessage; - final ValueChanged onReturnAction; + final ShowMessageCallback? onShowMessage; + final ValueChanged? onReturnAction; /// If true show the users username next to the timestamp of the message final bool showUsername; @@ -147,22 +147,22 @@ class MessageWidget extends StatefulWidget { final bool translateUserAvatar; /// Function called when quotedMessage is tapped - final OnQuotedMessageTap onQuotedMessageTap; + final OnQuotedMessageTap? onQuotedMessageTap; /// Function called when message is tapped - final void Function(Message) onMessageTap; + final void Function(Message)? onMessageTap; /// List of custom actions shown on message long tap final List customActions; // Customize onTap on attachment - final void Function(Message message, Attachment attachment) onAttachmentTap; + final void Function(Message message, Attachment attachment)? onAttachmentTap; /// MessageWidget({ - Key key, - @required this.message, - @required this.messageTheme, + Key? key, + required this.message, + required this.messageTheme, this.reverse = false, this.translateUserAvatar = true, this.shape, @@ -197,7 +197,7 @@ class MessageWidget extends StatefulWidget { this.editMessageInputBuilder, this.textBuilder, this.onReturnAction, - Map customAttachmentBuilders, + Map? customAttachmentBuilders, this.readList, this.padding, this.textPadding = const EdgeInsets.symmetric( @@ -236,7 +236,8 @@ class MessageWidget extends StatefulWidget { ), border, reverse, - attachmentBorderRadiusGeometry ?? BorderRadius.zero, + attachmentBorderRadiusGeometry as BorderRadius? ?? + BorderRadius.zero, ), ); } @@ -255,13 +256,14 @@ class MessageWidget extends StatefulWidget { onReturnAction: onReturnAction, onAttachmentTap: onAttachmentTap != null ? () { - onAttachmentTap?.call(message, attachments[0]); + onAttachmentTap.call(message, attachments[0]); } : null, ), border, reverse, - attachmentBorderRadiusGeometry ?? BorderRadius.zero, + attachmentBorderRadiusGeometry as BorderRadius? ?? + BorderRadius.zero, ); }, 'video': (context, message, attachments) { @@ -286,7 +288,7 @@ class MessageWidget extends StatefulWidget { onReturnAction: onReturnAction, onAttachmentTap: onAttachmentTap != null ? () { - onAttachmentTap?.call(message, attachment); + onAttachmentTap(message, attachment); } : null, ); @@ -294,11 +296,12 @@ class MessageWidget extends StatefulWidget { ), border, reverse, - attachmentBorderRadiusGeometry ?? BorderRadius.zero, + attachmentBorderRadiusGeometry as BorderRadius? ?? + BorderRadius.zero, ); }, 'giphy': (context, message, attachments) { - var border = RoundedRectangleBorder( + final border = RoundedRectangleBorder( side: BorderSide.none, borderRadius: attachmentBorderRadiusGeometry ?? BorderRadius.zero, ); @@ -309,7 +312,6 @@ class MessageWidget extends StatefulWidget { children: attachments.map((attachment) { return GiphyAttachment( attachment: attachment, - messageTheme: messageTheme, message: message, size: Size( MediaQuery.of(context).size.width * 0.8, @@ -322,7 +324,8 @@ class MessageWidget extends StatefulWidget { ), border, reverse, - attachmentBorderRadiusGeometry ?? BorderRadius.zero, + attachmentBorderRadiusGeometry as BorderRadius? ?? + BorderRadius.zero, ); }, 'file': (context, message, attachments) { @@ -349,7 +352,8 @@ class MessageWidget extends StatefulWidget { ), border, reverse, - attachmentBorderRadiusGeometry ?? BorderRadius.zero, + attachmentBorderRadiusGeometry as BorderRadius? ?? + BorderRadius.zero, ); }) .insertBetween(SizedBox( @@ -381,7 +385,7 @@ class _MessageWidgetState extends State bool get showInChannel => widget.showInChannelIndicator; - bool get hasQuotedMessage => widget.message?.quotedMessage != null; + bool get hasQuotedMessage => widget.message.quotedMessage != null; bool get isSendFailed => widget.message.status == MessageSendingStatus.failed; @@ -394,17 +398,17 @@ class _MessageWidgetState extends State bool get isFailedState => isSendFailed || isUpdateFailed || isDeleteFailed; bool get isGiphy => - widget.message.attachments?.any((element) => element.type == 'giphy') == + widget.message.attachments.any((element) => element.type == 'giphy') == true; bool get hasNonUrlAttachments => widget.message.attachments - ?.where((it) => it.ogScrapeUrl == null) - ?.isNotEmpty == + .where((it) => it.ogScrapeUrl == null) + .isNotEmpty == true; bool get hasUrlAttachments => - widget.message.attachments?.any((it) => it.ogScrapeUrl != null) == true; + widget.message.attachments.any((it) => it.ogScrapeUrl != null) == true; bool get showBottomRow => showThreadReplyIndicator || @@ -415,12 +419,13 @@ class _MessageWidgetState extends State isDeleted; @override - bool get wantKeepAlive => widget.message.attachments?.isNotEmpty == true; + bool get wantKeepAlive => widget.message.attachments.isNotEmpty == true; @override Widget build(BuildContext context) { super.build(context); - final avatarWidth = widget.messageTheme.avatarTheme.constraints.maxWidth; + final avatarWidth = + widget.messageTheme.avatarTheme?.constraints.maxWidth ?? 40; var leftPadding = widget.showUserAvatar != DisplayWidget.gone ? avatarWidth + 8.5 : 0.5; @@ -429,7 +434,7 @@ class _MessageWidgetState extends State child: Portal( child: InkWell( onTap: () { - widget.onMessageTap(widget.message); + widget.onMessageTap!(widget.message); }, onLongPress: widget.message.isDeleted && !isFailedState ? null @@ -441,7 +446,7 @@ class _MessageWidgetState extends State transform: Matrix4.rotationY(widget.reverse ? pi : 0), child: FractionallySizedBox( alignment: Alignment.centerLeft, - widthFactor: 0.75, + widthFactor: 0.78, child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, @@ -460,7 +465,8 @@ class _MessageWidgetState extends State mainAxisSize: MainAxisSize.min, children: [ if (widget.showUserAvatar == - DisplayWidget.show) ...[ + DisplayWidget.show && + widget.message.user != null) ...[ _buildUserAvatar(), SizedBox(width: 4), ], @@ -533,13 +539,14 @@ class _MessageWidgetState extends State ), shape: widget.shape ?? RoundedRectangleBorder( - side: - widget.borderSide ?? - BorderSide( - color: widget + side: widget + .borderSide ?? + BorderSide( + color: widget .messageTheme - .messageBorderColor, - ), + .messageBorderColor ?? + Colors.grey, + ), borderRadius: widget .borderRadiusGeometry ?? BorderRadius.zero, @@ -616,14 +623,14 @@ class _MessageWidgetState extends State Widget _buildQuotedMessage() { final isMyMessage = - widget.message.user.id == StreamChat.of(context).user.id; - final onTap = widget.message?.quotedMessage?.isDeleted != true && + widget.message.user?.id == StreamChat.of(context).user?.id; + final onTap = widget.message.quotedMessage?.isDeleted != true && widget.onQuotedMessageTap != null - ? () => widget.onQuotedMessageTap(widget.message.quotedMessageId) + ? () => widget.onQuotedMessageTap!(widget.message.quotedMessageId) : null; return QuotedMessageWidget( onTap: onTap, - message: widget.message.quotedMessage, + message: widget.message.quotedMessage!, messageTheme: isMyMessage ? StreamChatTheme.of(context).otherMessageTheme : StreamChatTheme.of(context).ownMessageTheme, @@ -660,12 +667,12 @@ class _MessageWidgetState extends State var children = []; - final threadParticipants = widget.message?.threadParticipants?.take(2); + final threadParticipants = widget.message.threadParticipants?.take(2); final showThreadParticipants = threadParticipants?.isNotEmpty == true; final replyCount = widget.message.replyCount; var msg = 'Thread Reply'; - if (showThreadReplyIndicator && replyCount > 1) { + if (showThreadReplyIndicator && replyCount! > 1) { msg = '$replyCount Thread Replies'; } @@ -674,9 +681,9 @@ class _MessageWidgetState extends State var message = widget.message; if (showInChannel) { final channel = StreamChannel.of(context); - message = await channel.getMessage(widget.message.parentId); + message = await channel.getMessage(widget.message.parentId!); } - return widget.onThreadTap(message); + return widget.onThreadTap!(message); } catch (e, stk) { print(e); print(stk); @@ -690,17 +697,17 @@ class _MessageWidgetState extends State if (showInChannel || showThreadReplyIndicator) ...[ if (showThreadParticipants) SizedBox.fromSize( - size: Size((threadParticipants.length * 8.0) + 8, 16), + size: Size((threadParticipants!.length * 8.0) + 8, 16), child: _buildThreadParticipantsIndicator(threadParticipants), ), InkWell( onTap: widget.onThreadTap != null ? onThreadTap : null, - child: Text(msg, style: widget.messageTheme?.replies), + child: Text(msg, style: widget.messageTheme.replies), ), ], if (showUsername) Text( - widget.message.user.name, + widget.message.user!.name, maxLines: 1, key: usernameKey, style: widget.messageTheme.messageAuthor, @@ -724,7 +731,7 @@ class _MessageWidgetState extends State Container( margin: EdgeInsets.only( bottom: context.textScaleFactor * - (widget.messageTheme.replies.fontSize / 2), + ((widget.messageTheme.replies?.fontSize ?? 1) / 2), ), child: CustomPaint( size: Size(16, 32) * context.textScaleFactor, @@ -758,7 +765,7 @@ class _MessageWidgetState extends State var urlAttachment = widget.message.attachments .firstWhere((element) => element.ogScrapeUrl != null); - var host = Uri.parse(urlAttachment.ogScrapeUrl).host; + var host = Uri.parse(urlAttachment.ogScrapeUrl!).host; var splitList = host.split('.'); var hostName = splitList.length == 3 ? splitList[1] : splitList[0]; var hostDisplayName = urlAttachment.authorName?.capitalize() ?? @@ -801,15 +808,16 @@ class _MessageWidgetState extends State Widget _buildReactionIndicator( BuildContext context, ) { - final ownId = StreamChat.of(context).user.id; + final ownId = StreamChat.of(context).user!.id; final reactionsMap = {}; widget.message.latestReactions?.forEach((element) { - if (!reactionsMap.containsKey(element.type) || element.user.id == ownId) { + if (!reactionsMap.containsKey(element.type) || + element.user!.id == ownId) { reactionsMap[element.type] = element; } }); final reactionsList = reactionsMap.values.toList() - ..sort((a, b) => a.user.id == ownId ? 1 : -1); + ..sort((a, b) => a.user!.id == ownId ? 1 : -1); return AnimatedSwitcher( duration: Duration(milliseconds: 300), @@ -822,9 +830,12 @@ class _MessageWidgetState extends State key: ValueKey('${widget.message.id}.reactions'), reverse: widget.reverse, flipTail: widget.reverse, - backgroundColor: widget.messageTheme.reactionsBackgroundColor, - borderColor: widget.messageTheme.reactionsBorderColor, - maskColor: widget.messageTheme.reactionsMaskColor, + backgroundColor: widget.messageTheme.reactionsBackgroundColor ?? + Colors.transparent, + borderColor: widget.messageTheme.reactionsBorderColor ?? + Colors.transparent, + maskColor: widget.messageTheme.reactionsMaskColor ?? + Colors.transparent, reactions: reactionsList, ), ) @@ -845,9 +856,9 @@ class _MessageWidgetState extends State onCopyTap: (message) => Clipboard.setData(ClipboardData(text: message.text)), attachmentBorderRadiusGeometry: - widget.attachmentBorderRadiusGeometry, + widget.attachmentBorderRadiusGeometry as BorderRadius?, showUserAvatar: - widget.message.user.id == channel.client.state.user.id + widget.message.user!.id == channel.client.state.user!.id ? DisplayWidget.gone : DisplayWidget.show, messageTheme: widget.messageTheme, @@ -864,11 +875,11 @@ class _MessageWidgetState extends State widget.showResendMessage && (isSendFailed || isUpdateFailed), showCopyMessage: widget.showCopyMessage && !isFailedState && - widget.message.text?.trim()?.isNotEmpty == true, + widget.message.text?.trim().isNotEmpty == true, showEditMessage: widget.showEditMessage && !isDeleteFailed && widget.message.attachments - ?.any((element) => element.type == 'giphy') != + .any((element) => element.type == 'giphy') != true, showReactions: widget.showReactions, showReplyMessage: widget.showReplyMessage && @@ -894,9 +905,9 @@ class _MessageWidgetState extends State channel: channel, child: MessageReactionsModal( attachmentBorderRadiusGeometry: - widget.attachmentBorderRadiusGeometry, + widget.attachmentBorderRadiusGeometry as BorderRadius?, showUserAvatar: - widget.message.user.id == channel.client.state.user.id + widget.message.user!.id == channel.client.state.user!.id ? DisplayWidget.gone : DisplayWidget.show, onUserAvatarTap: widget.onUserAvatarTap, @@ -914,7 +925,7 @@ class _MessageWidgetState extends State ShapeBorder _getDefaultAttachmentShape(BuildContext context) { final hasFiles = - widget.message.attachments?.any((it) => it.type == 'file') == true; + widget.message.attachments.any((it) => it.type == 'file') == true; return RoundedRectangleBorder( side: hasFiles ? widget.attachmentBorderSide ?? @@ -940,13 +951,13 @@ class _MessageWidgetState extends State final attachmentGroups = >{}; widget.message.attachments - .where((element) => element.ogScrapeUrl == null) + .where((element) => element.ogScrapeUrl == null && element.type != null) .forEach((e) { if (attachmentGroups[e.type] == null) { - attachmentGroups[e.type] = []; + attachmentGroups[e.type!] = []; } - attachmentGroups[e.type].add(e); + attachmentGroups[e.type]?.add(e); }); final attachmentList = []; @@ -954,7 +965,7 @@ class _MessageWidgetState extends State attachmentGroups.forEach((type, attachments) { final attachmentBuilder = widget.attachmentBuilders[type]; - if (attachmentBuilder == null) return SizedBox(); + if (attachmentBuilder == null) return; final attachmentWidget = attachmentBuilder( context, widget.message, @@ -967,10 +978,9 @@ class _MessageWidgetState extends State padding: widget.attachmentPadding, child: Column( mainAxisSize: MainAxisSize.min, - children: attachmentList?.insertBetween(SizedBox( - height: widget.attachmentPadding.vertical / 2, - )) ?? - [], + children: attachmentList.insertBetween(SizedBox( + height: widget.attachmentPadding.vertical / 2, + )), ), ); } @@ -982,7 +992,7 @@ class _MessageWidgetState extends State } if (widget.onMessageActions != null) { - widget.onMessageActions(context, widget.message); + widget.onMessageActions!(context, widget.message); } else { _showMessageActionModalBottomSheet(context); } @@ -1002,8 +1012,8 @@ class _MessageWidgetState extends State }).length; if (uploadRemaining == 0) { return StreamSvgIcon.check( - size: style.fontSize, - color: IconTheme.of(context).color.withOpacity(0.5), + size: style!.fontSize, + color: IconTheme.of(context).color!.withOpacity(0.5), ); } return Text( @@ -1015,14 +1025,14 @@ class _MessageWidgetState extends State Widget child = SendingIndicator( message: message, isMessageRead: isMessageRead, - size: style.fontSize, + size: style!.fontSize, ); if (isMessageRead) { child = Row( children: [ - if (StreamChannel.of(context).channel.memberCount > 2) + if (StreamChannel.of(context).channel.memberCount! > 2) Text( - widget.readList.length.toString(), + widget.readList!.length.toString(), style: style.copyWith( color: StreamChatTheme.of(context).colorTheme.accentBlue, ), @@ -1042,21 +1052,23 @@ class _MessageWidgetState extends State offset: Offset( 0, widget.translateUserAvatar - ? widget.messageTheme.avatarTheme.constraints.maxHeight / 2 + ? (widget.messageTheme.avatarTheme?.constraints.maxHeight ?? + 40) / + 2 : 0, ), child: UserAvatar( - user: widget.message.user, + user: widget.message.user!, onTap: widget.onUserAvatarTap, - constraints: widget.messageTheme.avatarTheme.constraints, - borderRadius: widget.messageTheme.avatarTheme.borderRadius, + constraints: widget.messageTheme.avatarTheme!.constraints, + borderRadius: widget.messageTheme.avatarTheme!.borderRadius, showOnlineStatus: false, ), ), ); Widget _buildTextBubble() { - if (widget.message.text.trim().isEmpty) return Offstage(); + if (widget.message.text!.trim().isEmpty) return Offstage(); return Transform( transform: Matrix4.rotationY(widget.reverse ? pi : 0), alignment: Alignment.center, @@ -1066,7 +1078,7 @@ class _MessageWidgetState extends State Padding( padding: isOnlyEmoji ? EdgeInsets.zero : widget.textPadding, child: widget.textBuilder != null - ? widget.textBuilder(context, widget.message) + ? widget.textBuilder!(context, widget.message) : MessageText( onLinkTap: widget.onLinkTap, message: widget.message, @@ -1074,7 +1086,7 @@ class _MessageWidgetState extends State messageTheme: isOnlyEmoji ? widget.messageTheme.copyWith( messageText: - widget.messageTheme.messageText.copyWith( + widget.messageTheme.messageText!.copyWith( fontSize: 42, )) : widget.messageTheme, @@ -1086,9 +1098,9 @@ class _MessageWidgetState extends State ); } - bool get isOnlyEmoji => widget.message.text.isOnlyEmoji; + bool get isOnlyEmoji => widget.message.text!.isOnlyEmoji; - Color _getBackgroundColor() { + Color? _getBackgroundColor() { if (hasQuotedMessage) { return widget.messageTheme.messageBackgroundColor; } @@ -1127,15 +1139,15 @@ class _MessageWidgetState extends State } class _ThreadReplyPainter extends CustomPainter { - final Color color; - final BuildContext context; + final Color? color; + final BuildContext? context; - const _ThreadReplyPainter({this.context, @required this.color}); + const _ThreadReplyPainter({this.context, required this.color}); @override void paint(Canvas canvas, Size size) { final paint = Paint() - ..color = color ?? StreamChatTheme.of(context).colorTheme.greyGainsboro + ..color = color ?? StreamChatTheme.of(context!).colorTheme.greyGainsboro ..style = PaintingStyle.stroke ..strokeWidth = 1 ..strokeCap = StrokeCap.round; diff --git a/packages/stream_chat_flutter/lib/src/option_list_tile.dart b/packages/stream_chat_flutter/lib/src/option_list_tile.dart index 61ce939e..03af584b 100644 --- a/packages/stream_chat_flutter/lib/src/option_list_tile.dart +++ b/packages/stream_chat_flutter/lib/src/option_list_tile.dart @@ -2,14 +2,14 @@ import 'package:flutter/material.dart'; import 'package:stream_chat_flutter/src/stream_chat_theme.dart'; class OptionListTile extends StatelessWidget { - final String title; - final Widget leading; - final Widget trailing; - final VoidCallback onTap; - final Color titleColor; - final Color tileColor; - final Color separatorColor; - final TextStyle titleTextStyle; + final String? title; + final Widget? leading; + final Widget? trailing; + final VoidCallback? onTap; + final Color? titleColor; + final Color? tileColor; + final Color? separatorColor; + final TextStyle? titleTextStyle; OptionListTile({ this.title, @@ -47,7 +47,7 @@ class OptionListTile extends StatelessWidget { Expanded( flex: 4, child: Text( - title, + title!, style: titleTextStyle ?? (titleColor == null ? StreamChatTheme.of(context).textTheme.bodyBold diff --git a/packages/stream_chat_flutter/lib/src/quoted_message_widget.dart b/packages/stream_chat_flutter/lib/src/quoted_message_widget.dart index 4d2b2e38..43b198eb 100644 --- a/packages/stream_chat_flutter/lib/src/quoted_message_widget.dart +++ b/packages/stream_chat_flutter/lib/src/quoted_message_widget.dart @@ -22,8 +22,8 @@ class _VideoAttachmentThumbnail extends StatefulWidget { final Attachment attachment; const _VideoAttachmentThumbnail({ - Key key, - @required this.attachment, + Key? key, + required this.attachment, this.size = const Size(32, 32), }) : super(key: key); @@ -33,12 +33,12 @@ class _VideoAttachmentThumbnail extends StatefulWidget { } class _VideoAttachmentThumbnailState extends State<_VideoAttachmentThumbnail> { - VideoPlayerController _controller; + late VideoPlayerController _controller; @override void initState() { super.initState(); - _controller = VideoPlayerController.network(widget.attachment.assetUrl) + _controller = VideoPlayerController.network(widget.attachment.assetUrl!) ..initialize().then((_) { setState(() {}); //when your thumbnail will show. }); @@ -79,18 +79,18 @@ class QuotedMessageWidget extends StatelessWidget { final int textLimit; /// Map that defines a thumbnail builder for an attachment type - final Map + final Map? attachmentThumbnailBuilders; final EdgeInsetsGeometry padding; - final GestureTapCallback onTap; + final GestureTapCallback? onTap; /// QuotedMessageWidget({ - Key key, - @required this.message, - @required this.messageTheme, + Key? key, + required this.message, + required this.messageTheme, this.reverse = false, this.showBorder = false, this.textLimit = 170, @@ -99,13 +99,12 @@ class QuotedMessageWidget extends StatelessWidget { this.onTap, }) : super(key: key); - bool get _hasAttachments => message.attachments?.isNotEmpty == true; + bool get _hasAttachments => message.attachments.isNotEmpty == true; bool get _containsScrapeUrl => - message.attachments?.any((element) => element.ogScrapeUrl != null) == - true; + message.attachments.any((element) => element.ogScrapeUrl != null) == true; - bool get _containsText => message?.text?.isNotEmpty == true; + bool get _containsText => message.text?.isNotEmpty == true; @override Widget build(BuildContext context) { @@ -119,7 +118,7 @@ class QuotedMessageWidget extends StatelessWidget { children: [ Flexible(child: _buildMessage(context)), SizedBox(width: 8), - _buildUserAvatar(), + if (message.user != null) _buildUserAvatar(), ], ), ), @@ -127,17 +126,17 @@ class QuotedMessageWidget extends StatelessWidget { } Widget _buildMessage(BuildContext context) { - final isOnlyEmoji = message.text.isOnlyEmoji; + final isOnlyEmoji = message.text!.isOnlyEmoji; var msg = _hasAttachments && !_containsText - ? message.copyWith(text: message.attachments.last?.title ?? '') + ? message.copyWith(text: message.attachments.last.title ?? '') : message; - if (msg.text.length > textLimit) { - msg = msg.copyWith(text: '${msg.text.substring(0, textLimit - 3)}...'); + if (msg.text!.length > textLimit) { + msg = msg.copyWith(text: '${msg.text!.substring(0, textLimit - 3)}...'); } final children = [ if (_hasAttachments) _parseAttachments(context), - if (msg.text.isNotEmpty) + if (msg.text!.isNotEmpty) Flexible( child: Transform( transform: Matrix4.rotationY(reverse ? pi : 0), @@ -146,11 +145,11 @@ class QuotedMessageWidget extends StatelessWidget { message: msg, messageTheme: isOnlyEmoji && _containsText ? messageTheme.copyWith( - messageText: messageTheme.messageText.copyWith( + messageText: messageTheme.messageText?.copyWith( fontSize: 32, )) : messageTheme.copyWith( - messageText: messageTheme.messageText.copyWith( + messageText: messageTheme.messageText?.copyWith( fontSize: 12, )), ), @@ -193,7 +192,7 @@ class QuotedMessageWidget extends StatelessWidget { image: DecorationImage( fit: BoxFit.cover, image: CachedNetworkImageProvider( - attachment.imageUrl, + attachment.imageUrl!, ), ), ), @@ -211,16 +210,17 @@ class QuotedMessageWidget extends StatelessWidget { ); child = _buildUrlAttachment(attachment); } else { - QuotedMessageAttachmentThumbnailBuilder attachmentBuilder; + QuotedMessageAttachmentThumbnailBuilder? attachmentBuilder; attachment = message.attachments.last; - if (attachmentThumbnailBuilders?.containsKey(attachment?.type) == true) { - attachmentBuilder = attachmentThumbnailBuilders[attachment?.type]; + if (attachmentThumbnailBuilders?.containsKey(attachment.type) == true) { + attachmentBuilder = attachmentThumbnailBuilders![attachment.type]; } - attachmentBuilder = _defaultAttachmentBuilder[attachment?.type]; + attachmentBuilder = _defaultAttachmentBuilder[attachment.type]; if (attachmentBuilder == null) { child = Offstage(); + } else { + child = attachmentBuilder(context, attachment); } - child = attachmentBuilder(context, attachment); } child = AbsorbPointer(child: child); return Transform( @@ -247,7 +247,7 @@ class QuotedMessageWidget extends StatelessWidget { transform: Matrix4.rotationY(reverse ? pi : 0), alignment: Alignment.center, child: UserAvatar( - user: message.user, + user: message.user!, constraints: BoxConstraints.tightFor( height: 24, width: 24, @@ -277,19 +277,20 @@ class QuotedMessageWidget extends StatelessWidget { 'giphy': (_, attachment) { final size = Size(32, 32); return CachedNetworkImage( - height: size?.height, - width: size?.width, + height: size.height, + width: size.width, placeholder: (_, __) { return Container( - width: size?.width, - height: size?.height, + width: size.width, + height: size.height, child: Center( child: CircularProgressIndicator(), ), ); }, - imageUrl: - attachment.thumbUrl ?? attachment.imageUrl ?? attachment.assetUrl, + imageUrl: attachment.thumbUrl ?? + attachment.imageUrl ?? + attachment.assetUrl!, errorWidget: (context, url, error) { return AttachmentError(size: size); }, @@ -300,13 +301,13 @@ class QuotedMessageWidget extends StatelessWidget { return Container( height: 32, width: 32, - child: getFileTypeImage(attachment.extraData['mime_type']), + child: getFileTypeImage(attachment.extraData['mime_type'] as String?), ); }, }; } - Color _getBackgroundColor(BuildContext context) { + Color? _getBackgroundColor(BuildContext context) { if (_containsScrapeUrl) { return StreamChatTheme.of(context).colorTheme.blueAlice; } diff --git a/packages/stream_chat_flutter/lib/src/reaction_bubble.dart b/packages/stream_chat_flutter/lib/src/reaction_bubble.dart index 160c0579..bcf3127c 100644 --- a/packages/stream_chat_flutter/lib/src/reaction_bubble.dart +++ b/packages/stream_chat_flutter/lib/src/reaction_bubble.dart @@ -1,5 +1,6 @@ import 'dart:math'; +import 'package:collection/collection.dart' show IterableExtension; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/widgets.dart'; @@ -9,11 +10,11 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart'; class ReactionBubble extends StatelessWidget { const ReactionBubble({ - Key key, - @required this.reactions, - @required this.borderColor, - @required this.backgroundColor, - @required this.maskColor, + Key? key, + required this.reactions, + required this.borderColor, + required this.backgroundColor, + required this.maskColor, this.reverse = false, this.flipTail = false, this.highlightOwnReactions = true, @@ -37,70 +38,68 @@ class ReactionBubble extends StatelessWidget { return Transform( transform: Matrix4.rotationY(reverse ? pi : 0), alignment: Alignment.center, - child: Center( - child: Stack( - alignment: Alignment.center, - fit: StackFit.loose, - children: [ - Transform.translate( - offset: Offset(reverse ? offset : -offset, 0), + child: Stack( + alignment: Alignment.center, + fit: StackFit.loose, + children: [ + Transform.translate( + offset: Offset(reverse ? offset : -offset, 0), + child: Container( + padding: const EdgeInsets.all(2), + decoration: BoxDecoration( + color: maskColor, + borderRadius: BorderRadius.all(Radius.circular(16)), + ), child: Container( - padding: const EdgeInsets.all(2), - decoration: BoxDecoration( - color: maskColor, - borderRadius: BorderRadius.all(Radius.circular(16)), + padding: EdgeInsets.symmetric( + vertical: 4, + horizontal: totalReactions > 1 ? 4 : 0, ), - child: Container( - padding: EdgeInsets.symmetric( - vertical: 4, - horizontal: totalReactions > 1 ? 4 : 0, - ), - decoration: BoxDecoration( - border: Border.all( - color: borderColor, - ), - color: backgroundColor, - borderRadius: BorderRadius.all(Radius.circular(14)), - ), - child: LayoutBuilder( - builder: (context, constraints) { - return Flex( - direction: Axis.horizontal, - mainAxisSize: MainAxisSize.min, - children: [ - if (constraints.maxWidth < double.infinity) - ...reactions - .take((constraints.maxWidth) ~/ 24) - .map((reaction) { - return _buildReaction( - reactionIcons, - reaction, - context, - ); - }).toList(), - if (constraints.maxWidth == double.infinity) - ...reactions.map((reaction) { - return _buildReaction( - reactionIcons, - reaction, - context, - ); - }).toList(), - ], - ); - }, + decoration: BoxDecoration( + border: Border.all( + color: borderColor, ), + color: backgroundColor, + borderRadius: BorderRadius.all(Radius.circular(14)), + ), + child: LayoutBuilder( + builder: (context, constraints) { + return Flex( + direction: Axis.horizontal, + mainAxisSize: MainAxisSize.min, + children: [ + if (constraints.maxWidth < double.infinity) + ...reactions + .take((constraints.maxWidth) ~/ 24) + .map((reaction) { + return _buildReaction( + reactionIcons, + reaction, + context, + ); + }).toList(), + if (constraints.maxWidth == double.infinity) + ...reactions.map((reaction) { + return _buildReaction( + reactionIcons, + reaction, + context, + ); + }).toList(), + ], + ); + }, ), ), ), - Positioned( - bottom: 2, - left: reverse ? null : 13, - right: !reverse ? null : 13, - child: _buildReactionsTail(context), - ), - ], - ), + ), + Positioned( + bottom: 2, + left: reverse ? null : 13, + right: !reverse ? null : 13, + child: _buildReactionsTail(context), + ), + ], ), ); } @@ -110,9 +109,8 @@ class ReactionBubble extends StatelessWidget { Reaction reaction, BuildContext context, ) { - final reactionIcon = reactionIcons.firstWhere( + final reactionIcon = reactionIcons.firstWhereOrNull( (r) => r.type == reaction.type, - orElse: () => null, ); return Padding( @@ -125,7 +123,7 @@ class ReactionBubble extends StatelessWidget { width: 16, height: 16, color: (!highlightOwnReactions || - reaction.user.id == StreamChat.of(context).user.id) + reaction.user?.id == StreamChat.of(context).user?.id) ? StreamChatTheme.of(context).colorTheme.accentBlue : StreamChatTheme.of(context) .colorTheme @@ -136,7 +134,7 @@ class ReactionBubble extends StatelessWidget { Icons.help_outline_rounded, size: 16, color: (!highlightOwnReactions || - reaction.user.id == StreamChat.of(context).user.id) + reaction.user?.id == StreamChat.of(context).user?.id) ? StreamChatTheme.of(context).colorTheme.accentBlue : StreamChatTheme.of(context) .colorTheme diff --git a/packages/stream_chat_flutter/lib/src/reaction_icon.dart b/packages/stream_chat_flutter/lib/src/reaction_icon.dart index 99b93328..66e19fe7 100644 --- a/packages/stream_chat_flutter/lib/src/reaction_icon.dart +++ b/packages/stream_chat_flutter/lib/src/reaction_icon.dart @@ -3,7 +3,7 @@ class ReactionIcon { final String assetName; ReactionIcon({ - this.type, - this.assetName, + required this.type, + required this.assetName, }); } diff --git a/packages/stream_chat_flutter/lib/src/reaction_picker.dart b/packages/stream_chat_flutter/lib/src/reaction_picker.dart index a33e7b52..cd358b2a 100644 --- a/packages/stream_chat_flutter/lib/src/reaction_picker.dart +++ b/packages/stream_chat_flutter/lib/src/reaction_picker.dart @@ -16,13 +16,11 @@ import 'extension.dart'; class ReactionPicker extends StatefulWidget { const ReactionPicker({ - Key key, - @required this.message, - @required this.messageTheme, + Key? key, + required this.message, }) : super(key: key); final Message message; - final MessageTheme messageTheme; @override _ReactionPickerState createState() => _ReactionPickerState(); @@ -98,7 +96,8 @@ class _ReactionPickerState extends State if (ownReactionIndex != -1) { removeReaction( context, - widget.message.ownReactions[ownReactionIndex], + widget + .message.ownReactions![ownReactionIndex], ); } else { sendReaction( @@ -129,7 +128,7 @@ class _ReactionPickerState extends State .accentBlue : Theme.of(context) .iconTheme - .color + .color! .withOpacity(.5), ), ); @@ -181,7 +180,7 @@ class _ReactionPickerState extends State @override void dispose() { for (var a in animations) { - a?.dispose(); + a.dispose(); } super.dispose(); } diff --git a/packages/stream_chat_flutter/lib/src/sending_indicator.dart b/packages/stream_chat_flutter/lib/src/sending_indicator.dart index 3fca2fa9..68fc5a0d 100644 --- a/packages/stream_chat_flutter/lib/src/sending_indicator.dart +++ b/packages/stream_chat_flutter/lib/src/sending_indicator.dart @@ -5,11 +5,11 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart'; class SendingIndicator extends StatelessWidget { final Message message; final bool isMessageRead; - final double size; + final double? size; const SendingIndicator({ - Key key, - this.message, + Key? key, + required this.message, this.isMessageRead = false, this.size = 12, }) : super(key: key); @@ -22,10 +22,10 @@ class SendingIndicator extends StatelessWidget { color: StreamChatTheme.of(context).colorTheme.accentBlue, ); } - if (message.status == MessageSendingStatus.sent || message.status == null) { + if (message.status == MessageSendingStatus.sent) { return StreamSvgIcon.check( size: size, - color: IconTheme.of(context).color.withOpacity(0.5), + color: IconTheme.of(context).color!.withOpacity(0.5), ); } if (message.status == MessageSendingStatus.sending || diff --git a/packages/stream_chat_flutter/lib/src/stream_chat.dart b/packages/stream_chat_flutter/lib/src/stream_chat.dart index 470591e7..f5461895 100644 --- a/packages/stream_chat_flutter/lib/src/stream_chat.dart +++ b/packages/stream_chat_flutter/lib/src/stream_chat.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:ui' as ui; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -7,7 +8,6 @@ import 'package:jiffy/jiffy.dart'; import 'package:stream_chat_flutter/src/stream_chat_theme.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; -import 'dart:ui' as ui; /// Widget used to provide information about the chat to the widget tree /// @@ -32,8 +32,8 @@ import 'dart:ui' as ui; /// Use [StreamChat.of] to get the current [StreamChatState] instance. class StreamChat extends StatefulWidget { final StreamChatClient client; - final Widget child; - final StreamChatThemeData streamChatThemeData; + final Widget? child; + final StreamChatThemeData? streamChatThemeData; /// The amount of time that will pass before disconnecting the client in the background final Duration backgroundKeepAlive; @@ -41,12 +41,12 @@ class StreamChat extends StatefulWidget { /// Handler called whenever the [client] receives a new [Event] while the app /// is in background. Can be used to display various notifications depending /// upon the [Event.type] - final EventHandler onBackgroundEventReceived; + final EventHandler? onBackgroundEventReceived; StreamChat({ - Key key, - @required this.client, - @required this.child, + Key? key, + required this.client, + required this.child, this.streamChatThemeData, this.onBackgroundEventReceived, this.backgroundKeepAlive = const Duration(minutes: 1), @@ -59,7 +59,7 @@ class StreamChat extends StatefulWidget { /// Use this method to get the current [StreamChatState] instance static StreamChatState of(BuildContext context) { - StreamChatState streamChatState; + StreamChatState? streamChatState; streamChatState = context.findAncestorStateOfType(); @@ -96,7 +96,7 @@ class StreamChatState extends State { client: client, onBackgroundEventReceived: widget.onBackgroundEventReceived, backgroundKeepAlive: widget.backgroundKeepAlive, - child: widget.child, + child: widget.child ?? Offstage(), ), ); }, @@ -107,17 +107,18 @@ class StreamChatState extends State { StreamChatThemeData _getTheme( BuildContext context, - StreamChatThemeData themeData, + StreamChatThemeData? themeData, ) { - final defaultTheme = StreamChatThemeData.getDefaultTheme(Theme.of(context)); - return defaultTheme.merge(themeData) ?? themeData; + final appBrightness = Theme.of(context).brightness; + final defaultTheme = StreamChatThemeData(brightness: appBrightness); + return defaultTheme.merge(themeData); } /// The current user - User get user => widget.client.state.user; + User? get user => widget.client.state.user; /// The current user as a stream - Stream get userStream => widget.client.state.userStream; + Stream get userStream => widget.client.state.userStream; @override void initState() { diff --git a/packages/stream_chat_flutter/lib/src/stream_chat_theme.dart b/packages/stream_chat_flutter/lib/src/stream_chat_theme.dart index e54c0fbe..9b28d1f6 100644 --- a/packages/stream_chat_flutter/lib/src/stream_chat_theme.dart +++ b/packages/stream_chat_flutter/lib/src/stream_chat_theme.dart @@ -2,10 +2,11 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:stream_chat_flutter/src/channel_header.dart'; import 'package:stream_chat_flutter/src/channel_preview.dart'; +import 'package:stream_chat_flutter/src/extension.dart'; import 'package:stream_chat_flutter/src/message_input.dart'; import 'package:stream_chat_flutter/src/reaction_icon.dart'; import 'package:stream_chat_flutter/src/utils.dart'; -import 'package:stream_chat_flutter/src/extension.dart'; +import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; /// Inherited widget providing the [StreamChatThemeData] to the widget tree @@ -13,9 +14,9 @@ class StreamChatTheme extends InheritedWidget { final StreamChatThemeData data; StreamChatTheme({ - Key key, - @required this.data, - Widget child, + Key? key, + required this.data, + required Widget child, }) : super( key: key, child: child, @@ -31,13 +32,12 @@ class StreamChatTheme extends InheritedWidget { final streamChatTheme = context.dependOnInheritedWidgetOfExactType(); - if (streamChatTheme == null) { - throw Exception( - 'You must have a StreamChatTheme widget at the top of your widget tree', - ); - } + assert( + streamChatTheme != null, + 'You must have a StreamChatTheme widget at the top of your widget tree', + ); - return streamChatTheme.data; + return streamChatTheme!.data; } } @@ -80,49 +80,96 @@ class StreamChatThemeData { final List reactionIcons; /// Create a theme from scratch - const StreamChatThemeData({ - this.textTheme, - this.colorTheme, - this.channelListHeaderTheme, - this.channelPreviewTheme, - this.channelTheme, - this.otherMessageTheme, - this.ownMessageTheme, - this.messageInputTheme, - this.defaultChannelImage, - this.defaultUserImage, - this.primaryIconTheme, - this.reactionIcons, + factory StreamChatThemeData({ + Brightness? brightness, + TextTheme? textTheme, + ColorTheme? colorTheme, + ChannelListHeaderTheme? channelListHeaderTheme, + ChannelPreviewTheme? channelPreviewTheme, + ChannelTheme? channelTheme, + MessageTheme? otherMessageTheme, + MessageTheme? ownMessageTheme, + MessageInputTheme? messageInputTheme, + Widget Function(BuildContext, Channel)? defaultChannelImage, + Widget Function(BuildContext, User)? defaultUserImage, + IconThemeData? primaryIconTheme, + List? reactionIcons, + }) { + brightness ??= colorTheme?.brightness ?? Brightness.light; + final isDark = brightness == Brightness.dark; + textTheme ??= isDark ? TextTheme.dark() : TextTheme.light(); + colorTheme ??= isDark ? ColorTheme.dark() : ColorTheme.light(); + + final defaultData = fromColorAndTextTheme( + colorTheme, + textTheme, + ); + + final customizedData = defaultData.copyWith( + channelListHeaderTheme: channelListHeaderTheme, + channelPreviewTheme: channelPreviewTheme, + channelTheme: channelTheme, + otherMessageTheme: otherMessageTheme, + ownMessageTheme: ownMessageTheme, + messageInputTheme: messageInputTheme, + defaultChannelImage: defaultChannelImage, + defaultUserImage: defaultUserImage, + primaryIconTheme: primaryIconTheme, + reactionIcons: reactionIcons, + ); + + return defaultData.merge(customizedData); + } + + factory StreamChatThemeData.light() => + StreamChatThemeData(brightness: Brightness.light); + + factory StreamChatThemeData.dark() => + StreamChatThemeData(brightness: Brightness.dark); + + const StreamChatThemeData.raw({ + required this.textTheme, + required this.colorTheme, + required this.channelListHeaderTheme, + required this.channelPreviewTheme, + required this.channelTheme, + required this.otherMessageTheme, + required this.ownMessageTheme, + required this.messageInputTheme, + required this.defaultChannelImage, + required this.defaultUserImage, + required this.primaryIconTheme, + required this.reactionIcons, }); /// Create a theme from a Material [Theme] factory StreamChatThemeData.fromTheme(ThemeData theme) { - final defaultTheme = getDefaultTheme(theme); + final defaultTheme = StreamChatThemeData(brightness: theme.brightness); final customizedTheme = StreamChatThemeData.fromColorAndTextTheme( defaultTheme.colorTheme.copyWith( accentBlue: theme.accentColor, ), defaultTheme.textTheme, ); - return defaultTheme.merge(customizedTheme) ?? customizedTheme; + return defaultTheme.merge(customizedTheme); } /// Creates a copy of [StreamChatThemeData] with specified attributes overridden. StreamChatThemeData copyWith({ - TextTheme textTheme, - ColorTheme colorTheme, - ChannelPreviewTheme channelPreviewTheme, - ChannelTheme channelTheme, - MessageTheme ownMessageTheme, - MessageTheme otherMessageTheme, - MessageInputTheme messageInputTheme, - Widget Function(BuildContext, Channel) defaultChannelImage, - Widget Function(BuildContext, User) defaultUserImage, - IconThemeData primaryIconTheme, - ChannelListHeaderTheme channelListHeaderTheme, - List reactionIcons, + TextTheme? textTheme, + ColorTheme? colorTheme, + ChannelPreviewTheme? channelPreviewTheme, + ChannelTheme? channelTheme, + MessageTheme? ownMessageTheme, + MessageTheme? otherMessageTheme, + MessageInputTheme? messageInputTheme, + Widget Function(BuildContext, Channel)? defaultChannelImage, + Widget Function(BuildContext, User)? defaultUserImage, + IconThemeData? primaryIconTheme, + ChannelListHeaderTheme? channelListHeaderTheme, + List? reactionIcons, }) => - StreamChatThemeData( + StreamChatThemeData.raw( channelListHeaderTheme: channelListHeaderTheme ?? this.channelListHeaderTheme, textTheme: textTheme ?? this.textTheme, @@ -138,28 +185,21 @@ class StreamChatThemeData { reactionIcons: reactionIcons ?? this.reactionIcons, ); - StreamChatThemeData merge(StreamChatThemeData other) { + StreamChatThemeData merge(StreamChatThemeData? other) { if (other == null) return this; return copyWith( channelListHeaderTheme: - channelListHeaderTheme?.merge(other.channelListHeaderTheme) ?? - other.channelListHeaderTheme, - textTheme: textTheme?.merge(other.textTheme) ?? other.textTheme, - colorTheme: colorTheme?.merge(other.colorTheme) ?? other.colorTheme, + channelListHeaderTheme.merge(other.channelListHeaderTheme), + textTheme: textTheme.merge(other.textTheme), + colorTheme: colorTheme.merge(other.colorTheme), primaryIconTheme: other.primaryIconTheme, defaultChannelImage: other.defaultChannelImage, defaultUserImage: other.defaultUserImage, - channelPreviewTheme: - channelPreviewTheme?.merge(other.channelPreviewTheme) ?? - other.channelPreviewTheme, - channelTheme: - channelTheme?.merge(other.channelTheme) ?? other.channelTheme, - ownMessageTheme: ownMessageTheme?.merge(other.ownMessageTheme) ?? - other.ownMessageTheme, - otherMessageTheme: otherMessageTheme?.merge(other.otherMessageTheme) ?? - other.otherMessageTheme, - messageInputTheme: messageInputTheme?.merge(other.messageInputTheme) ?? - other.messageInputTheme, + channelPreviewTheme: channelPreviewTheme.merge(other.channelPreviewTheme), + channelTheme: channelTheme.merge(other.channelTheme), + ownMessageTheme: ownMessageTheme.merge(other.ownMessageTheme), + otherMessageTheme: otherMessageTheme.merge(other.otherMessageTheme), + messageInputTheme: messageInputTheme.merge(other.messageInputTheme), reactionIcons: other.reactionIcons, ); } @@ -169,7 +209,7 @@ class StreamChatThemeData { TextTheme textTheme, ) { final accentColor = colorTheme.accentBlue; - return StreamChatThemeData( + return StreamChatThemeData.raw( textTheme: textTheme, colorTheme: colorTheme, primaryIconTheme: IconThemeData(color: colorTheme.black.withOpacity(.5)), @@ -314,22 +354,6 @@ class StreamChatThemeData { ], ); } - - /// Get the default Stream Chat theme - static StreamChatThemeData getDefaultTheme(ThemeData theme) { - final isDark = theme.brightness == Brightness.dark; - final textTheme = isDark ? TextTheme.dark() : TextTheme.light(); - final colorTheme = isDark ? ColorTheme.dark() : ColorTheme.light(); - return fromColorAndTextTheme( - colorTheme, - textTheme, - ); - } -} - -enum TextThemeType { - light, - dark, } class TextTheme { @@ -427,17 +451,17 @@ class TextTheme { }); TextTheme copyWith({ - TextThemeType type = TextThemeType.light, - TextStyle body, - TextStyle title, - TextStyle headlineBold, - TextStyle headline, - TextStyle bodyBold, - TextStyle footnoteBold, - TextStyle footnote, - TextStyle captionBold, + Brightness brightness = Brightness.light, + TextStyle? body, + TextStyle? title, + TextStyle? headlineBold, + TextStyle? headline, + TextStyle? bodyBold, + TextStyle? footnoteBold, + TextStyle? footnote, + TextStyle? captionBold, }) { - return type == TextThemeType.light + return brightness == Brightness.light ? TextTheme.light( body: body ?? this.body, title: title ?? this.title, @@ -460,28 +484,21 @@ class TextTheme { ); } - TextTheme merge(TextTheme other) { + TextTheme merge(TextTheme? other) { if (other == null) return this; return copyWith( - body: body?.merge(other.body) ?? other.body, - title: title?.merge(other.title) ?? other.title, - headlineBold: - headlineBold?.merge(other.headlineBold) ?? other.headlineBold, - headline: headline?.merge(other.headline) ?? other.headline, - bodyBold: bodyBold?.merge(other.bodyBold) ?? other.bodyBold, - footnoteBold: - footnoteBold?.merge(other.footnoteBold) ?? other.footnoteBold, - footnote: footnote?.merge(other.footnote) ?? other.footnote, - captionBold: captionBold?.merge(other.captionBold) ?? other.captionBold, + body: body.merge(other.body), + title: title.merge(other.title), + headlineBold: headlineBold.merge(other.headlineBold), + headline: headline.merge(other.headline), + bodyBold: bodyBold.merge(other.bodyBold), + footnoteBold: footnoteBold.merge(other.footnoteBold), + footnote: footnote.merge(other.footnote), + captionBold: captionBold.merge(other.captionBold), ); } } -enum ColorThemeType { - light, - dark, -} - class ColorTheme { final Color black; final Color grey; @@ -502,6 +519,7 @@ class ColorTheme { final Color overlay; final Color overlayDark; final Gradient bgGradient; + final Brightness brightness; ColorTheme.light({ this.black = const Color(0xff000000), @@ -536,7 +554,7 @@ class ColorTheme { sigmaX: 0, sigmaY: 2, color: Color(0xff000000), alpha: 0.5, blur: 4.0), this.modalShadow = const Effect( sigmaX: 0, sigmaY: 0, color: Color(0xff000000), alpha: 1, blur: 8.0), - }); + }) : brightness = Brightness.light; ColorTheme.dark({ this.black = const Color(0xffffffff), @@ -551,13 +569,32 @@ class ColorTheme { this.accentRed = const Color(0xffFF3742), this.accentGreen = const Color(0xff20E070), this.borderTop = const Effect( - sigmaX: 0, sigmaY: -1, color: Color(0xff141924), blur: 0.0), + sigmaX: 0, + sigmaY: -1, + color: Color(0xff141924), + blur: 0.0, + ), this.borderBottom = const Effect( - sigmaX: 0, sigmaY: 1, color: Color(0xff141924), blur: 0.0, alpha: 1.0), + sigmaX: 0, + sigmaY: 1, + color: Color(0xff141924), + blur: 0.0, + alpha: 1.0, + ), this.shadowIconButton = const Effect( - sigmaX: 0, sigmaY: 2, color: Color(0xff000000), alpha: 0.5, blur: 4.0), + sigmaX: 0, + sigmaY: 2, + color: Color(0xff000000), + alpha: 0.5, + blur: 4.0, + ), this.modalShadow = const Effect( - sigmaX: 0, sigmaY: 0, color: Color(0xff000000), alpha: 1, blur: 8.0), + sigmaX: 0, + sigmaY: 0, + color: Color(0xff000000), + alpha: 1, + blur: 8.0, + ), this.highlight = const Color(0xff302d22), this.overlay = const Color.fromRGBO(0, 0, 0, 0.4), this.overlayDark = const Color.fromRGBO(255, 255, 255, 0.6), @@ -570,31 +607,31 @@ class ColorTheme { ], stops: [0, 1], ), - }); + }) : brightness = Brightness.dark; ColorTheme copyWith({ - ColorThemeType type = ColorThemeType.light, - Color black, - Color grey, - Color greyGainsboro, - Color greyWhisper, - Color whiteSmoke, - Color whiteSnow, - Color white, - Color blueAlice, - Color accentBlue, - Color accentRed, - Color accentGreen, - Effect borderTop, - Effect borderBottom, - Effect shadowIconButton, - Effect modalShadow, - Color highlight, - Color overlay, - Color overlayDark, - Gradient bgGradient, + Brightness brightness = Brightness.light, + Color? black, + Color? grey, + Color? greyGainsboro, + Color? greyWhisper, + Color? whiteSmoke, + Color? whiteSnow, + Color? white, + Color? blueAlice, + Color? accentBlue, + Color? accentRed, + Color? accentGreen, + Effect? borderTop, + Effect? borderBottom, + Effect? shadowIconButton, + Effect? modalShadow, + Color? highlight, + Color? overlay, + Color? overlayDark, + Gradient? bgGradient, }) { - return type == ColorThemeType.light + return brightness == Brightness.light ? ColorTheme.light( black: black ?? this.black, grey: grey ?? this.grey, @@ -639,7 +676,7 @@ class ColorTheme { ); } - ColorTheme merge(ColorTheme other) { + ColorTheme merge(ColorTheme? other) { if (other == null) return this; return copyWith( black: other.black, @@ -671,65 +708,77 @@ class ChannelTheme { final ChannelHeaderTheme channelHeaderTheme; ChannelTheme({ - this.channelHeaderTheme, + required this.channelHeaderTheme, }); /// Creates a copy of [ChannelTheme] with specified attributes overridden. ChannelTheme copyWith({ - ChannelHeaderTheme channelHeaderTheme, + ChannelHeaderTheme? channelHeaderTheme, }) => ChannelTheme( channelHeaderTheme: channelHeaderTheme ?? this.channelHeaderTheme, ); - ChannelTheme merge(ChannelTheme other) { + ChannelTheme merge(ChannelTheme? other) { if (other == null) return this; return copyWith( - channelHeaderTheme: channelHeaderTheme?.merge(other.channelHeaderTheme) ?? - other.channelHeaderTheme, + channelHeaderTheme: channelHeaderTheme.merge(other.channelHeaderTheme), ); } } class AvatarTheme { - final BoxConstraints constraints; - final BorderRadius borderRadius; + final BoxConstraints? _constraints; + final BorderRadius? _borderRadius; + + BoxConstraints get constraints { + return _constraints ?? + BoxConstraints.tightFor( + height: 32, + width: 32, + ); + } + + BorderRadius get borderRadius { + return _borderRadius ?? BorderRadius.circular(20); + } AvatarTheme({ - this.constraints, - this.borderRadius, - }); + BoxConstraints? constraints, + BorderRadius? borderRadius, + }) : _constraints = constraints, + _borderRadius = borderRadius; AvatarTheme copyWith({ - BoxConstraints constraints, - BorderRadius borderRadius, + BoxConstraints? constraints, + BorderRadius? borderRadius, }) => AvatarTheme( - constraints: constraints ?? this.constraints, - borderRadius: borderRadius ?? this.borderRadius, + constraints: constraints ?? _constraints, + borderRadius: borderRadius ?? _borderRadius, ); - AvatarTheme merge(AvatarTheme other) { + AvatarTheme merge(AvatarTheme? other) { if (other == null) return this; return copyWith( - constraints: other.constraints, - borderRadius: other.borderRadius, + constraints: other._constraints, + borderRadius: other._borderRadius, ); } } class MessageTheme { - final TextStyle messageText; - final TextStyle messageAuthor; - final TextStyle messageLinks; - final TextStyle createdAt; - final TextStyle replies; - final Color messageBackgroundColor; - final Color messageBorderColor; - final Color reactionsBackgroundColor; - final Color reactionsBorderColor; - final Color reactionsMaskColor; - final AvatarTheme avatarTheme; + final TextStyle? messageText; + final TextStyle? messageAuthor; + final TextStyle? messageLinks; + final TextStyle? createdAt; + final TextStyle? replies; + final Color? messageBackgroundColor; + final Color? messageBorderColor; + final Color? reactionsBackgroundColor; + final Color? reactionsBorderColor; + final Color? reactionsMaskColor; + final AvatarTheme? avatarTheme; const MessageTheme({ this.replies, @@ -746,17 +795,17 @@ class MessageTheme { }); MessageTheme copyWith({ - TextStyle messageText, - TextStyle messageAuthor, - TextStyle messageLinks, - TextStyle createdAt, - TextStyle replies, - Color messageBackgroundColor, - Color messageBorderColor, - AvatarTheme avatarTheme, - Color reactionsBackgroundColor, - Color reactionsBorderColor, - Color reactionsMaskColor, + TextStyle? messageText, + TextStyle? messageAuthor, + TextStyle? messageLinks, + TextStyle? createdAt, + TextStyle? replies, + Color? messageBackgroundColor, + Color? messageBorderColor, + AvatarTheme? avatarTheme, + Color? reactionsBackgroundColor, + Color? reactionsBorderColor, + Color? reactionsMaskColor, }) => MessageTheme( messageText: messageText ?? this.messageText, @@ -774,7 +823,7 @@ class MessageTheme { reactionsMaskColor: reactionsMaskColor ?? this.reactionsMaskColor, ); - MessageTheme merge(MessageTheme other) { + MessageTheme merge(MessageTheme? other) { if (other == null) return this; return copyWith( messageText: messageText?.merge(other.messageText) ?? other.messageText, @@ -795,12 +844,12 @@ class MessageTheme { } class ChannelPreviewTheme { - final TextStyle title; - final TextStyle subtitle; - final TextStyle lastMessageAt; - final AvatarTheme avatarTheme; - final Color unreadCounterColor; - final double indicatorIconSize; + final TextStyle? title; + final TextStyle? subtitle; + final TextStyle? lastMessageAt; + final AvatarTheme? avatarTheme; + final Color? unreadCounterColor; + final double? indicatorIconSize; const ChannelPreviewTheme({ this.title, @@ -812,12 +861,12 @@ class ChannelPreviewTheme { }); ChannelPreviewTheme copyWith({ - TextStyle title, - TextStyle subtitle, - TextStyle lastMessageAt, - AvatarTheme avatarTheme, - Color unreadCounterColor, - double indicatorIconSize, + TextStyle? title, + TextStyle? subtitle, + TextStyle? lastMessageAt, + AvatarTheme? avatarTheme, + Color? unreadCounterColor, + double? indicatorIconSize, }) => ChannelPreviewTheme( title: title ?? this.title, @@ -828,7 +877,7 @@ class ChannelPreviewTheme { indicatorIconSize: indicatorIconSize ?? this.indicatorIconSize, ); - ChannelPreviewTheme merge(ChannelPreviewTheme other) { + ChannelPreviewTheme merge(ChannelPreviewTheme? other) { if (other == null) return this; return copyWith( title: title?.merge(other.title) ?? other.title, @@ -842,10 +891,10 @@ class ChannelPreviewTheme { } class ChannelHeaderTheme { - final TextStyle title; - final TextStyle subtitle; - final AvatarTheme avatarTheme; - final Color color; + final TextStyle? title; + final TextStyle? subtitle; + final AvatarTheme? avatarTheme; + final Color? color; const ChannelHeaderTheme({ this.title, @@ -855,10 +904,10 @@ class ChannelHeaderTheme { }); ChannelHeaderTheme copyWith({ - TextStyle title, - TextStyle subtitle, - AvatarTheme avatarTheme, - Color color, + TextStyle? title, + TextStyle? subtitle, + AvatarTheme? avatarTheme, + Color? color, }) => ChannelHeaderTheme( title: title ?? this.title, @@ -867,7 +916,7 @@ class ChannelHeaderTheme { color: color ?? this.color, ); - ChannelHeaderTheme merge(ChannelHeaderTheme other) { + ChannelHeaderTheme merge(ChannelHeaderTheme? other) { if (other == null) return this; return copyWith( title: title?.merge(other.title) ?? other.title, @@ -881,13 +930,13 @@ class ChannelHeaderTheme { /// Theme dedicated to the [ChannelListHeader] class ChannelListHeaderTheme { /// Style of the title text - final TextStyle title; + final TextStyle? title; /// Theme dedicated to the userAvatar - final AvatarTheme avatarTheme; + final AvatarTheme? avatarTheme; /// Background color of the appbar - final Color color; + final Color? color; /// Returns a new [ChannelListHeaderTheme] const ChannelListHeaderTheme({ @@ -898,9 +947,9 @@ class ChannelListHeaderTheme { /// Returns a new [ChannelListHeaderTheme] replacing some of its properties ChannelListHeaderTheme copyWith({ - TextStyle title, - AvatarTheme avatarTheme, - Color color, + TextStyle? title, + AvatarTheme? avatarTheme, + Color? color, }) => ChannelListHeaderTheme( title: title ?? this.title, @@ -909,7 +958,7 @@ class ChannelListHeaderTheme { ); /// Merges [this] [ChannelListHeaderTheme] with the [other] - ChannelListHeaderTheme merge(ChannelListHeaderTheme other) { + ChannelListHeaderTheme merge(ChannelListHeaderTheme? other) { if (other == null) return this; return copyWith( title: title?.merge(other.title) ?? other.title, @@ -922,40 +971,40 @@ class ChannelListHeaderTheme { /// Defines the theme dedicated to the [MessageInput] widget class MessageInputTheme { /// Duration of the [MessageInput] send button animation - final Duration sendAnimationDuration; + final Duration? sendAnimationDuration; /// Background color of [MessageInput] send button - final Color sendButtonColor; + final Color? sendButtonColor; /// Background color of [MessageInput] action buttons - final Color actionButtonColor; + final Color? actionButtonColor; /// Background color of [MessageInput] send button - final Color sendButtonIdleColor; + final Color? sendButtonIdleColor; /// Background color of [MessageInput] action buttons - final Color actionButtonIdleColor; + final Color? actionButtonIdleColor; /// Background color of [MessageInput] expand button - final Color expandButtonColor; + final Color? expandButtonColor; /// Background color of [MessageInput] - final Color inputBackground; + final Color? inputBackground; /// TextStyle of [MessageInput] - final TextStyle inputTextStyle; + final TextStyle? inputTextStyle; /// InputDecoration of [MessageInput] - final InputDecoration inputDecoration; + final InputDecoration? inputDecoration; /// Border gradient when the [MessageInput] is not focused - final Gradient idleBorderGradient; + final Gradient? idleBorderGradient; /// Border gradient when the [MessageInput] is focused - final Gradient activeBorderGradient; + final Gradient? activeBorderGradient; /// Border radius of [MessageInput] - final BorderRadius borderRadius; + final BorderRadius? borderRadius; /// Returns a new [MessageInputTheme] const MessageInputTheme({ @@ -975,18 +1024,18 @@ class MessageInputTheme { /// Returns a new [MessageInputTheme] replacing some of its properties MessageInputTheme copyWith({ - Duration sendAnimationDuration, - Color inputBackground, - Color actionButtonColor, - Color sendButtonColor, - Color actionButtonIdleColor, - Color sendButtonIdleColor, - Color expandButtonColor, - TextStyle inputTextStyle, - InputDecoration inputDecoration, - Gradient activeBorderGradient, - Gradient idleBorderGradient, - BorderRadius borderRadius, + Duration? sendAnimationDuration, + Color? inputBackground, + Color? actionButtonColor, + Color? sendButtonColor, + Color? actionButtonIdleColor, + Color? sendButtonIdleColor, + Color? expandButtonColor, + TextStyle? inputTextStyle, + InputDecoration? inputDecoration, + Gradient? activeBorderGradient, + Gradient? idleBorderGradient, + BorderRadius? borderRadius, }) => MessageInputTheme( sendAnimationDuration: @@ -1006,7 +1055,7 @@ class MessageInputTheme { ); /// Merges [this] [MessageInputTheme] with the [other] - MessageInputTheme merge(MessageInputTheme other) { + MessageInputTheme merge(MessageInputTheme? other) { if (other == null) return this; return copyWith( sendAnimationDuration: other.sendAnimationDuration, @@ -1027,11 +1076,11 @@ class MessageInputTheme { } class Effect { - final double sigmaX; - final double sigmaY; - final Color color; - final double alpha; - final double blur; + final double? sigmaX; + final double? sigmaY; + final Color? color; + final double? alpha; + final double? blur; const Effect({ this.sigmaX, @@ -1042,17 +1091,17 @@ class Effect { }); Effect copyWith({ - double sigmaX, - double sigmaY, - Color color, - double alpha, - double blur, + double? sigmaX, + double? sigmaY, + Color? color, + double? alpha, + double? blur, }) => Effect( sigmaX: sigmaX ?? this.sigmaX, sigmaY: sigmaY ?? this.sigmaY, color: color ?? this.color, - alpha: color ?? this.alpha, + alpha: color as double? ?? this.alpha, blur: blur ?? this.blur, ); } diff --git a/packages/stream_chat_flutter/lib/src/stream_neumorphic_button.dart b/packages/stream_chat_flutter/lib/src/stream_neumorphic_button.dart index 35e98960..95b3c70e 100644 --- a/packages/stream_chat_flutter/lib/src/stream_neumorphic_button.dart +++ b/packages/stream_chat_flutter/lib/src/stream_neumorphic_button.dart @@ -5,8 +5,8 @@ class StreamNeumorphicButton extends StatelessWidget { final Color backgroundColor; const StreamNeumorphicButton({ - Key key, - @required this.child, + Key? key, + required this.child, this.backgroundColor = Colors.white, }) : super(key: key); @@ -21,7 +21,7 @@ class StreamNeumorphicButton extends StatelessWidget { shape: BoxShape.circle, boxShadow: [ BoxShadow( - color: Colors.grey[700], + color: Colors.grey.shade700, offset: Offset(0, 1.0), blurRadius: 0.5, spreadRadius: 0, diff --git a/packages/stream_chat_flutter/lib/src/stream_svg_icon.dart b/packages/stream_chat_flutter/lib/src/stream_svg_icon.dart index 374b9ba1..8dc38ba2 100644 --- a/packages/stream_chat_flutter/lib/src/stream_svg_icon.dart +++ b/packages/stream_chat_flutter/lib/src/stream_svg_icon.dart @@ -3,10 +3,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; class StreamSvgIcon extends StatelessWidget { - final String assetName; - final double width; - final double height; - final Color color; + final String? assetName; + final double? width; + final double? height; + final Color? color; const StreamSvgIcon({ this.assetName, @@ -30,8 +30,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.settings({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'settings.svg', @@ -42,8 +42,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.down({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'Icon_down.svg', @@ -54,8 +54,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.attach({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'Icon_attach.svg', @@ -66,8 +66,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.smile({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'Icon_smile.svg', @@ -78,8 +78,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.mentions({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'mentions.svg', @@ -90,8 +90,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.record({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'Icon_record.svg', @@ -102,8 +102,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.camera({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'Icon_camera.svg', @@ -114,8 +114,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.files({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'files.svg', @@ -126,8 +126,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.pictures({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'pictures.svg', @@ -138,8 +138,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.left({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'Icon_left.svg', @@ -150,8 +150,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.user({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'Icon_user.svg', @@ -162,8 +162,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.userAdd({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'Icon_User_add.svg', @@ -174,8 +174,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.check({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'Icon_check.svg', @@ -186,8 +186,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.checkAll({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'Icon_check_all.svg', @@ -198,8 +198,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.checkSend({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'Icon_check_send.svg', @@ -210,8 +210,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.penWrite({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'Icon_pen-write.svg', @@ -222,8 +222,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.contacts({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'Icon_contacts.svg', @@ -234,8 +234,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.close({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'Icon_close.svg', @@ -246,8 +246,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.search({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'Icon_search.svg', @@ -258,8 +258,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.right({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'Icon_right.svg', @@ -270,8 +270,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.mute({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'Icon_mute.svg', @@ -282,8 +282,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.userRemove({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'Icon_User_deselect.svg', @@ -294,8 +294,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.lightning({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'Icon_lightning-command runner.svg', @@ -306,8 +306,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.emptyCircleLeft({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'Icon_empty_circle_left.svg', @@ -318,8 +318,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.message({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'Icon_message.svg', @@ -330,8 +330,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.thread({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'Icon_Thread_Reply.svg', @@ -342,8 +342,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.reply({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'Icon_curve_line_left_up_big.svg', @@ -354,8 +354,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.edit({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'Icon_edit.svg', @@ -366,8 +366,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.download({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'Icon_download.svg', @@ -378,8 +378,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.cloudDownload({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'Icon_cloud_download.svg', @@ -390,8 +390,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.copy({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'Icon_copy.svg', @@ -402,8 +402,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.delete({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'Icon_delete.svg', @@ -414,8 +414,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.eye({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'Icon_eye-off.svg', @@ -426,8 +426,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.arrowRight({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'Icon_arrow_right.svg', @@ -438,8 +438,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.closeSmall({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'Icon_close_sml.svg', @@ -450,8 +450,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.iconCurveLineLeftUp({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'Icon_curve_line_left_up.svg', @@ -462,8 +462,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.iconMoon({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'icon_moon.svg', @@ -474,8 +474,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.iconShare({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'icon_SHARE.svg', @@ -486,8 +486,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.iconGrid({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'Icon_grid.svg', @@ -498,8 +498,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.iconSendMessage({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'Icon_send_message.svg', @@ -510,8 +510,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.iconMenuPoint({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'Icon_menu_point_v.svg', @@ -522,8 +522,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.iconSave({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'Icon_save.svg', @@ -534,8 +534,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.shareArrow({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'share_arrow.svg', @@ -546,8 +546,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.filetype7z({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'filetype_7z.svg', @@ -558,8 +558,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.filetypeCsv({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'filetype_CSV.svg', @@ -570,8 +570,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.filetypeDoc({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'filetype_DOC.svg', @@ -582,8 +582,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.filetypeDocx({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'filetype_DOCX.svg', @@ -594,8 +594,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.filetypeGeneric({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'filetype_Generic.svg', @@ -606,8 +606,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.filetypeHtml({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'filetype_html.svg', @@ -618,8 +618,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.filetypeMd({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'filetype_MD.svg', @@ -630,8 +630,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.filetypeOdt({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'filetype_ODT.svg', @@ -642,8 +642,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.filetypePdf({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'filetype_PDF.svg', @@ -654,8 +654,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.filetypePpt({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'filetype_PPT.svg', @@ -666,8 +666,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.filetypePptx({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'filetype_PPTX.svg', @@ -678,8 +678,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.filetypeRar({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'filetype_RAR.svg', @@ -690,8 +690,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.filetypeRtf({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'filetype_RTF.svg', @@ -702,8 +702,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.filetypeTar({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'filetype_TAR.svg', @@ -714,8 +714,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.filetypeTxt({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'filetype_TXT.svg', @@ -726,8 +726,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.filetypeXls({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'filetype_XLS.svg', @@ -738,8 +738,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.filetypeXlsx({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'filetype_XLSX.svg', @@ -750,8 +750,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.filetypeZip({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'filetype_ZIP.svg', @@ -762,8 +762,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.iconGroup({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'Icon_group.svg', @@ -774,8 +774,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.iconNotification({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'Icon_notification.svg', @@ -786,8 +786,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.iconUserDelete({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'Icon_user_delete.svg', @@ -798,8 +798,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.error({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'Icon_error.svg', @@ -810,8 +810,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.circleUp({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'Icon_circle_up.svg', @@ -822,8 +822,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.iconUserSettings({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'Icon_user_settings.svg', @@ -834,8 +834,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.giphyIcon({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'giphy_icon.svg', @@ -846,8 +846,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.imgur({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'imgur.svg', @@ -858,8 +858,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.volumeUp({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'volume-up.svg', @@ -870,8 +870,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.flag({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'flag.svg', @@ -882,8 +882,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.iconFlag({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'icon_flag.svg', @@ -894,8 +894,8 @@ class StreamSvgIcon extends StatelessWidget { } factory StreamSvgIcon.retry({ - double size, - Color color, + double? size, + Color? color, }) { return StreamSvgIcon( assetName: 'icon_retry.svg', diff --git a/packages/stream_chat_flutter/lib/src/swipeable.dart b/packages/stream_chat_flutter/lib/src/swipeable.dart index 8280bf24..90146e4c 100644 --- a/packages/stream_chat_flutter/lib/src/swipeable.dart +++ b/packages/stream_chat_flutter/lib/src/swipeable.dart @@ -8,15 +8,15 @@ import 'stream_chat_theme.dart'; class Swipeable extends StatefulWidget { final Widget child; final Widget backgroundIcon; - final VoidCallback onSwipeStart; - final VoidCallback onSwipeCancel; - final VoidCallback onSwipeEnd; + final VoidCallback? onSwipeStart; + final VoidCallback? onSwipeCancel; + final VoidCallback? onSwipeEnd; final double threshold; /// const Swipeable({ - @required this.child, - @required this.backgroundIcon, + required this.child, + required this.backgroundIcon, this.onSwipeStart, this.onSwipeCancel, this.onSwipeEnd, @@ -29,11 +29,11 @@ class Swipeable extends StatefulWidget { class _SwipeableState extends State with TickerProviderStateMixin { double _dragExtent = 0.0; - AnimationController _moveController; - AnimationController _iconMoveController; - Animation _moveAnimation; - Animation _iconTransitionAnimation; - Animation _iconFadeAnimation; + late AnimationController _moveController; + late AnimationController _iconMoveController; + late Animation _moveAnimation; + late Animation _iconTransitionAnimation; + late Animation _iconFadeAnimation; bool _pastThreshold = false; final _animationDuration = const Duration(milliseconds: 200); @@ -67,18 +67,18 @@ class _SwipeableState extends State with TickerProviderStateMixin { void _handleDragStart(DragStartDetails details) { if (widget.onSwipeStart != null) { - widget.onSwipeStart(); + widget.onSwipeStart!(); } } void _handleDragUpdate(DragUpdateDetails details) { - final delta = details.primaryDelta; + final delta = details.primaryDelta!; _dragExtent += delta; if (_dragExtent.isNegative) return; var movePastThresholdPixels = widget.threshold; - var newPos = _dragExtent.abs() / context.size.width; + var newPos = _dragExtent.abs() / context.size!.width; if (_dragExtent.abs() > movePastThresholdPixels) { // how many "thresholds" past the threshold we are. 1 = the threshold 2 @@ -90,7 +90,7 @@ class _SwipeableState extends State with TickerProviderStateMixin { var reducedThreshold = math.pow(n, 0.3); var adjustedPixelPos = movePastThresholdPixels * reducedThreshold; - newPos = adjustedPixelPos / context.size.width; + newPos = adjustedPixelPos / context.size!.width; if (_dragExtent > 0 && !_pastThreshold) { _iconMoveController.value = 1; @@ -100,7 +100,7 @@ class _SwipeableState extends State with TickerProviderStateMixin { // Send a cancel event if the user has swiped back underneath the // threshold if (_pastThreshold && widget.onSwipeCancel != null) { - widget.onSwipeCancel(); + widget.onSwipeCancel!(); } _pastThreshold = false; } @@ -115,7 +115,7 @@ class _SwipeableState extends State with TickerProviderStateMixin { _iconMoveController.animateTo(0.0, duration: _animationDuration); _dragExtent = 0.0; if (_pastThreshold && widget.onSwipeEnd != null) { - widget.onSwipeEnd(); + widget.onSwipeEnd!(); } } diff --git a/packages/stream_chat_flutter/lib/src/system_message.dart b/packages/stream_chat_flutter/lib/src/system_message.dart index ac3176ce..cdb9294f 100644 --- a/packages/stream_chat_flutter/lib/src/system_message.dart +++ b/packages/stream_chat_flutter/lib/src/system_message.dart @@ -7,11 +7,11 @@ class SystemMessage extends StatelessWidget { final Message message; /// The function called when tapping on the message when the message is not failed - final void Function(Message) onMessageTap; + final void Function(Message)? onMessageTap; const SystemMessage({ - Key key, - @required this.message, + Key? key, + required this.message, this.onMessageTap, }) : super(key: key); @@ -22,11 +22,11 @@ class SystemMessage extends StatelessWidget { behavior: HitTestBehavior.opaque, onTap: () { if (onMessageTap != null) { - onMessageTap(message); + onMessageTap!(message); } }, child: Text( - message.text, + message.text!, textAlign: TextAlign.center, softWrap: true, style: theme.textTheme.captionBold.copyWith( diff --git a/packages/stream_chat_flutter/lib/src/thread_header.dart b/packages/stream_chat_flutter/lib/src/thread_header.dart index 449d4584..9dc8f98c 100644 --- a/packages/stream_chat_flutter/lib/src/thread_header.dart +++ b/packages/stream_chat_flutter/lib/src/thread_header.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; import 'package:stream_chat_flutter/src/stream_chat_theme.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; +import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; import 'back_button.dart'; import 'channel_name.dart'; @@ -61,30 +61,30 @@ class ThreadHeader extends StatelessWidget implements PreferredSizeWidget { /// Callback to call when pressing the back button. /// By default it calls [Navigator.pop] - final VoidCallback onBackPressed; + final VoidCallback? onBackPressed; /// Callback to call when the title is tapped. - final VoidCallback onTitleTap; + final VoidCallback? onTitleTap; /// The message parent of this thread final Message parent; /// Title widget - final Widget title; + final Widget? title; /// Subtitle widget - final Widget subtitle; + final Widget? subtitle; /// Leading widget - final Widget leading; + final Widget? leading; /// AppBar actions - final List actions; + final List? actions; /// Instantiate a new ThreadHeader ThreadHeader({ - Key key, - @required this.parent, + Key? key, + required this.parent, this.showBackButton = true, this.onBackPressed, this.title, diff --git a/packages/stream_chat_flutter/lib/src/typing_indicator.dart b/packages/stream_chat_flutter/lib/src/typing_indicator.dart index 8a53c3e8..aecc3bf1 100644 --- a/packages/stream_chat_flutter/lib/src/typing_indicator.dart +++ b/packages/stream_chat_flutter/lib/src/typing_indicator.dart @@ -6,7 +6,7 @@ import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; class TypingIndicator extends StatelessWidget { /// Instantiate a new TypingIndicator const TypingIndicator({ - Key key, + Key? key, this.channel, this.alternativeWidget, this.style, @@ -15,13 +15,13 @@ class TypingIndicator extends StatelessWidget { }) : super(key: key); /// Style of the text widget - final TextStyle style; + final TextStyle? style; /// List of typing users - final Channel channel; + final Channel? channel; /// Widget built when no typings is happening - final Widget alternativeWidget; + final Widget? alternativeWidget; /// The padding of this widget final EdgeInsets padding; @@ -31,7 +31,7 @@ class TypingIndicator extends StatelessWidget { @override Widget build(BuildContext context) { final channelState = - channel?.state ?? StreamChannel.of(context).channel.state; + channel?.state ?? StreamChannel.of(context).channel.state!; return StreamBuilder>( initialData: channelState.typingEvents, stream: channelState.typingEventsStream, @@ -53,7 +53,7 @@ class TypingIndicator extends StatelessWidget { height: 4, ), Text( - ' ${snapshot.data[0].name}${snapshot.data.length == 1 ? '' : ' and ${snapshot.data.length - 1} more'} ${snapshot.data.length == 1 ? 'is' : 'are'} typing', + ' ${snapshot.data![0].name}${snapshot.data!.length == 1 ? '' : ' and ${snapshot.data!.length - 1} more'} ${snapshot.data!.length == 1 ? 'is' : 'are'} typing', maxLines: 1, style: style, ), diff --git a/packages/stream_chat_flutter/lib/src/unread_indicator.dart b/packages/stream_chat_flutter/lib/src/unread_indicator.dart index 0c30e9a1..d55cd4f1 100644 --- a/packages/stream_chat_flutter/lib/src/unread_indicator.dart +++ b/packages/stream_chat_flutter/lib/src/unread_indicator.dart @@ -4,23 +4,23 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart'; class UnreadIndicator extends StatelessWidget { const UnreadIndicator({ - Key key, + Key? key, this.cid, }) : super(key: key); /// Channel cid used to retrieve unread count - final String cid; + final String? cid; @override Widget build(BuildContext context) { final client = StreamChat.of(context).client; return IgnorePointer( - child: StreamBuilder( + child: StreamBuilder( stream: cid != null - ? client.state.channels[cid].state.unreadCountStream + ? client.state.channels[cid]?.state?.unreadCountStream : client.state.totalUnreadCountStream, initialData: cid != null - ? client.state.channels[cid].state.unreadCount + ? client.state.channels[cid]?.state?.unreadCount : client.state.totalUnreadCount, builder: (context, snapshot) { if (!snapshot.hasData || snapshot.data == 0) { @@ -40,7 +40,7 @@ class UnreadIndicator extends StatelessWidget { ), child: Center( child: Text( - '${snapshot.data > 99 ? '99+' : snapshot.data}', + '${snapshot.data! > 99 ? '99+' : snapshot.data}', style: TextStyle( fontSize: 11, color: Colors.white, diff --git a/packages/stream_chat_flutter/lib/src/upload_progress_indicator.dart b/packages/stream_chat_flutter/lib/src/upload_progress_indicator.dart index 53c121e2..e0e7e306 100644 --- a/packages/stream_chat_flutter/lib/src/upload_progress_indicator.dart +++ b/packages/stream_chat_flutter/lib/src/upload_progress_indicator.dart @@ -8,14 +8,19 @@ class UploadProgressIndicator extends StatelessWidget { final Color progressIndicatorColor; final EdgeInsetsGeometry padding; final bool showBackground; - final TextStyle textStyle; + final TextStyle? textStyle; - const UploadProgressIndicator({ - Key key, - @required this.uploaded, - @required this.total, + UploadProgressIndicator({ + Key? key, + required this.uploaded, + required this.total, this.progressIndicatorColor = const Color(0xffb2b2b2), - this.padding = const EdgeInsets.only(top: 5, bottom: 5, right: 11, left: 5), + this.padding = const EdgeInsets.only( + top: 5, + bottom: 5, + right: 11, + left: 5, + ), this.showBackground = true, this.textStyle, }) : super(key: key); diff --git a/packages/stream_chat_flutter/lib/src/url_attachment.dart b/packages/stream_chat_flutter/lib/src/url_attachment.dart index 9dc5cdc8..e8e7b8a8 100644 --- a/packages/stream_chat_flutter/lib/src/url_attachment.dart +++ b/packages/stream_chat_flutter/lib/src/url_attachment.dart @@ -9,18 +9,23 @@ class UrlAttachment extends StatelessWidget { final EdgeInsets textPadding; UrlAttachment({ - @required this.urlAttachment, - @required this.hostDisplayName, - @required this.textPadding, + required this.urlAttachment, + required this.hostDisplayName, + this.textPadding = const EdgeInsets.symmetric( + horizontal: 16.0, + vertical: 8.0, + ), }); @override Widget build(BuildContext context) { return GestureDetector( - onTap: () => launchURL( - context, - urlAttachment.ogScrapeUrl, - ), + onTap: () { + launchURL( + context, + urlAttachment.ogScrapeUrl, + ); + }, child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ @@ -35,7 +40,7 @@ class UrlAttachment extends StatelessWidget { children: [ CachedNetworkImage( width: double.infinity, - imageUrl: urlAttachment.imageUrl, + imageUrl: urlAttachment.imageUrl!, fit: BoxFit.cover, ), Positioned( @@ -78,7 +83,7 @@ class UrlAttachment extends StatelessWidget { children: [ if (urlAttachment.title != null) Text( - urlAttachment.title.trim(), + urlAttachment.title!.trim(), maxLines: 1, overflow: TextOverflow.ellipsis, style: StreamChatTheme.of(context) @@ -88,7 +93,7 @@ class UrlAttachment extends StatelessWidget { ), if (urlAttachment.text != null) Text( - urlAttachment.text, + urlAttachment.text!, style: StreamChatTheme.of(context) .textTheme .body diff --git a/packages/stream_chat_flutter/lib/src/user_avatar.dart b/packages/stream_chat_flutter/lib/src/user_avatar.dart index 41860a7f..96753b99 100644 --- a/packages/stream_chat_flutter/lib/src/user_avatar.dart +++ b/packages/stream_chat_flutter/lib/src/user_avatar.dart @@ -6,8 +6,8 @@ import '../stream_chat_flutter.dart'; class UserAvatar extends StatelessWidget { const UserAvatar({ - Key key, - @required this.user, + Key? key, + required this.user, this.constraints, this.onlineIndicatorConstraints, this.onTap, @@ -22,19 +22,19 @@ class UserAvatar extends StatelessWidget { final User user; final Alignment onlineIndicatorAlignment; - final BoxConstraints constraints; - final BorderRadius borderRadius; - final BoxConstraints onlineIndicatorConstraints; - final void Function(User) onTap; - final void Function(User) onLongPress; + final BoxConstraints? constraints; + final BorderRadius? borderRadius; + final BoxConstraints? onlineIndicatorConstraints; + final void Function(User)? onTap; + final void Function(User)? onLongPress; final bool showOnlineStatus; final bool selected; - final Color selectionColor; + final Color? selectionColor; final double selectionThickness; @override Widget build(BuildContext context) { - final hasImage = user.extraData?.containsKey('image') == true && + final hasImage = user.extraData.containsKey('image') && user.extraData['image'] != null && user.extraData['image'] != ''; final streamChatTheme = StreamChatTheme.of(context); @@ -44,17 +44,17 @@ class UserAvatar extends StatelessWidget { child: ClipRRect( clipBehavior: Clip.antiAlias, borderRadius: borderRadius ?? - streamChatTheme.ownMessageTheme.avatarTheme.borderRadius, + streamChatTheme.ownMessageTheme.avatarTheme?.borderRadius, child: Container( constraints: constraints ?? - streamChatTheme.ownMessageTheme.avatarTheme.constraints, + streamChatTheme.ownMessageTheme.avatarTheme?.constraints, decoration: BoxDecoration( color: streamChatTheme.colorTheme.accentBlue, ), child: hasImage ? CachedNetworkImage( filterQuality: FilterQuality.high, - imageUrl: user.extraData['image'], + imageUrl: user.extraData['image'] as String, errorWidget: (_, __, ___) { return streamChatTheme.defaultUserImage(context, user); }, @@ -68,11 +68,12 @@ class UserAvatar extends StatelessWidget { if (selected) { avatar = ClipRRect( borderRadius: (borderRadius ?? - streamChatTheme.ownMessageTheme.avatarTheme.borderRadius) + + streamChatTheme.ownMessageTheme.avatarTheme?.borderRadius ?? + BorderRadius.zero) + BorderRadius.circular(selectionThickness), child: Container( constraints: constraints ?? - streamChatTheme.ownMessageTheme.avatarTheme.constraints, + streamChatTheme.ownMessageTheme.avatarTheme?.constraints, color: selectionColor ?? StreamChatTheme.of(context).colorTheme.accentBlue, child: Padding( @@ -83,12 +84,12 @@ class UserAvatar extends StatelessWidget { ); } return GestureDetector( - onTap: onTap != null ? () => onTap(user) : null, - onLongPress: onLongPress != null ? () => onLongPress(user) : null, + onTap: onTap != null ? () => onTap!(user) : null, + onLongPress: onLongPress != null ? () => onLongPress!(user) : null, child: Stack( children: [ avatar, - if (showOnlineStatus && user.online == true) + if (showOnlineStatus && user.online) Positioned.fill( child: Align( alignment: onlineIndicatorAlignment, diff --git a/packages/stream_chat_flutter/lib/src/user_item.dart b/packages/stream_chat_flutter/lib/src/user_item.dart index cb82e311..258855d8 100644 --- a/packages/stream_chat_flutter/lib/src/user_item.dart +++ b/packages/stream_chat_flutter/lib/src/user_item.dart @@ -1,9 +1,9 @@ import 'package:flutter/material.dart'; import 'package:jiffy/jiffy.dart'; -import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; import 'package:stream_chat_flutter/src/stream_svg_icon.dart'; import 'package:stream_chat_flutter/src/user_list_view.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; +import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; import 'stream_chat_theme.dart'; @@ -19,8 +19,8 @@ import 'stream_chat_theme.dart'; class UserItem extends StatelessWidget { /// Instantiate a new UserItem const UserItem({ - Key key, - @required this.user, + Key? key, + required this.user, this.onTap, this.onLongPress, this.onImageTap, @@ -29,16 +29,16 @@ class UserItem extends StatelessWidget { }) : super(key: key); /// Function called when tapping this widget - final void Function(User) onTap; + final void Function(User)? onTap; /// Function called when long pressing this widget - final void Function(User) onLongPress; + final void Function(User)? onLongPress; /// User displayed final User user; /// The function called when the image is tapped - final void Function(User) onImageTap; + final void Function(User)? onImageTap; /// If true the [UserItem] will show a trailing checkmark final bool selected; @@ -51,12 +51,12 @@ class UserItem extends StatelessWidget { return ListTile( onTap: () { if (onTap != null) { - onTap(user); + onTap!(user); } }, onLongPress: () { if (onLongPress != null) { - onLongPress(user); + onLongPress!(user); } }, leading: UserAvatar( @@ -64,7 +64,7 @@ class UserItem extends StatelessWidget { showOnlineStatus: true, onTap: (user) { if (onImageTap != null) { - onImageTap(user); + onImageTap!(user); } }, constraints: BoxConstraints.tightFor( diff --git a/packages/stream_chat_flutter/lib/src/user_list_view.dart b/packages/stream_chat_flutter/lib/src/user_list_view.dart index d4bc2f72..2f0e13b9 100644 --- a/packages/stream_chat_flutter/lib/src/user_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/user_list_view.dart @@ -5,7 +5,7 @@ import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; import 'user_item.dart'; /// Callback called when tapping on a user -typedef UserTapCallback = void Function(User, Widget); +typedef UserTapCallback = void Function(User, Widget?); /// Builder used to create a custom [ListUserItem] from a [User] typedef UserItemBuilder = Widget Function(BuildContext, User, bool); @@ -44,7 +44,7 @@ typedef UserItemBuilder = Widget Function(BuildContext, User, bool); class UserListView extends StatefulWidget { /// Instantiate a new UserListView const UserListView({ - Key key, + Key? key, this.filter, this.options, this.sort, @@ -72,51 +72,51 @@ class UserListView extends StatefulWidget { /// The query filters to use. /// You can query on any of the custom fields you've defined on the [Channel]. /// You can also filter other built-in channel fields. - final Map filter; + final Filter? filter; /// Query channels options. /// /// state: if true returns the Channel state /// watch: if true listen to changes to this Channel in real time. - final Map options; + final Map? options; /// The sorting used for the channels matching the filters. /// Sorting is based on field and direction, multiple sorting options can be provided. /// You can sort based on last_updated, last_message_at, updated_at, created_at or member_count. /// Direction can be ascending or descending. - final List sort; + final List? sort; /// Pagination parameters /// limit: the number of users to return (max is 30) /// offset: the offset (max is 1000) /// message_limit: how many messages should be included to each channel - final PaginationParams pagination; + final PaginationParams? pagination; /// Function called when tapping on a channel /// By default it calls [Navigator.push] building a [MaterialPageRoute] /// with the widget [userWidget] as child. - final UserTapCallback onUserTap; + final UserTapCallback? onUserTap; /// Function called when long pressing on a channel - final Function(User) onUserLongPress; + final Function(User)? onUserLongPress; /// Widget used when opening a channel - final Widget userWidget; + final Widget? userWidget; /// Builder used to create a custom user preview - final UserItemBuilder userItemBuilder; + final UserItemBuilder? userItemBuilder; /// Builder used to create a custom item separator - final Function(BuildContext, int) separatorBuilder; + final Function(BuildContext, int)? separatorBuilder; /// The function called when the image is tapped - final Function(User) onImageTap; + final Function(User)? onImageTap; /// Set it to false to disable the pull-to-refresh widget final bool pullToRefresh; /// Sets a blue trailing checkMark in [ListUserItem] for all the [selectedUsers] - final Set selectedUsers; + final Set? selectedUsers; /// Set it to true to group users by their first character /// @@ -127,16 +127,17 @@ class UserListView extends StatefulWidget { final int crossAxisCount; /// The builder that will be used in case of error - final Widget Function(Error error) errorBuilder; + final Widget Function(Error error)? errorBuilder; /// The builder that will be used to build the list - final Widget Function(BuildContext context, List users) listBuilder; + final Widget Function(BuildContext context, List users)? + listBuilder; /// The builder that will be used for loading - final WidgetBuilder loadingBuilder; + final WidgetBuilder? loadingBuilder; /// The builder used when the channel list is empty. - final WidgetBuilder emptyBuilder; + final WidgetBuilder? emptyBuilder; @override _UserListViewState createState() => _UserListViewState(); @@ -151,9 +152,9 @@ class _UserListViewState extends State @override Widget build(BuildContext context) { var child = UserListCore( - errorBuilder: widget.errorBuilder ?? + errorBuilder: widget.errorBuilder as Widget Function(Object)? ?? (err) { - return _buildError(err); + return _buildError(err as Error); }, emptyBuilder: widget.emptyBuilder ?? (context) { @@ -193,7 +194,7 @@ class _UserListViewState extends State return child; } else { return RefreshIndicator( - onRefresh: () => _userListController.loadData(), + onRefresh: () => _userListController.loadData!(), child: child, ); } @@ -208,7 +209,7 @@ class _UserListViewState extends State var message = error.toString(); if (error is DioError) { final dioError = error as DioError; - if (dioError.type == DioErrorType.RESPONSE) { + if (dioError.type == DioErrorType.response) { message = dioError.message; } else { message = 'Check your connection and retry'; @@ -241,7 +242,7 @@ class _UserListViewState extends State child: Text(message), ), TextButton( - onPressed: () => _userListController.loadData(), + onPressed: () => _userListController.loadData!(), child: Text('Retry'), ), ], @@ -276,7 +277,7 @@ class _UserListViewState extends State itemCount: items.isNotEmpty ? items.length + 1 : items.length, separatorBuilder: (_, index) { if (widget.separatorBuilder != null) { - return widget.separatorBuilder(context, index); + return widget.separatorBuilder!(context, index); } return _separatorBuilder(context, index); }, @@ -296,7 +297,7 @@ class _UserListViewState extends State ); return LazyLoadScrollView( - onEndOfPage: () => _userListController.paginateData(), + onEndOfPage: () => _userListController.paginateData!(), child: child, ); } @@ -329,10 +330,10 @@ class _UserListViewState extends State return Container( key: ValueKey('USER-${user.id}'), child: widget.userItemBuilder != null - ? widget.userItemBuilder(context, user, selected) + ? widget.userItemBuilder!(context, user, selected) : UserItem( user: user, - onTap: (user) => widget.onUserTap(user, widget.userWidget), + onTap: (user) => widget.onUserTap!(user, widget.userWidget), onLongPress: widget.onUserLongPress, onImageTap: widget.onImageTap, selected: selected, @@ -356,7 +357,7 @@ class _UserListViewState extends State return Container( key: ValueKey('USER-${user.id}'), child: widget.userItemBuilder != null - ? widget.userItemBuilder(context, user, selected) + ? widget.userItemBuilder!(context, user, selected) : Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, @@ -374,7 +375,7 @@ class _UserListViewState extends State width: 12, ), onTap: (user) => - widget.onUserTap(user, widget.userWidget), + widget.onUserTap!(user, widget.userWidget), onLongPress: widget.onUserLongPress, ), SizedBox(height: 4), @@ -424,7 +425,7 @@ class _UserListViewState extends State height: 100, padding: EdgeInsets.all(32), child: Center( - child: snapshot.data ? CircularProgressIndicator() : Container(), + child: snapshot.data! ? CircularProgressIndicator() : Container(), ), ); }); diff --git a/packages/stream_chat_flutter/lib/src/user_reaction_display.dart b/packages/stream_chat_flutter/lib/src/user_reaction_display.dart index af2797ca..492baef5 100644 --- a/packages/stream_chat_flutter/lib/src/user_reaction_display.dart +++ b/packages/stream_chat_flutter/lib/src/user_reaction_display.dart @@ -1,12 +1,12 @@ import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; import 'package:stream_chat_flutter/src/user_avatar.dart'; +import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; class UserReactionDisplay extends StatelessWidget { const UserReactionDisplay({ - Key key, - @required this.reactionToEmoji, - @required this.message, + Key? key, + required this.reactionToEmoji, + required this.message, this.size = 30, }) : super(key: key); @@ -23,12 +23,13 @@ class UserReactionDisplay extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.min, children: reactionToEmoji.keys.map((reactionType) { - var firstUserReaction = message.latestReactions.firstWhere( - (element) => element.type == reactionType, orElse: () { - return null; - }); + var firstUserReaction = message.latestReactions! + .firstWhere((element) => element.type == reactionType, + orElse: () { + return null; + } as Reaction Function()?); - if (firstUserReaction == null) { + if (firstUserReaction.user == null) { return IconButton( iconSize: size, icon: Container(), @@ -39,7 +40,7 @@ class UserReactionDisplay extends StatelessWidget { return IconButton( iconSize: size, icon: UserAvatar( - user: firstUserReaction.user, + user: firstUserReaction.user!, constraints: BoxConstraints( maxHeight: size - 5, maxWidth: size - 5, diff --git a/packages/stream_chat_flutter/lib/src/utils.dart b/packages/stream_chat_flutter/lib/src/utils.dart index 8950a77c..79bd210e 100644 --- a/packages/stream_chat_flutter/lib/src/utils.dart +++ b/packages/stream_chat_flutter/lib/src/utils.dart @@ -7,8 +7,8 @@ import 'package:url_launcher/url_launcher.dart'; import '../stream_chat_flutter.dart'; import 'stream_svg_icon.dart'; -Future launchURL(BuildContext context, String url) async { - if (await canLaunch(url)) { +Future launchURL(BuildContext context, String? url) async { + if (url != null && await canLaunch(url)) { await launch(url); } else { // ignore: deprecated_member_use @@ -20,13 +20,13 @@ Future launchURL(BuildContext context, String url) async { } } -Future showConfirmationDialog( +Future showConfirmationDialog( BuildContext context, { - String title, - Widget icon, - String question, - String okText, - String cancelText, + required String title, + required String okText, + Widget? icon, + String? question, + String? cancelText, }) { return showModalBottomSheet( backgroundColor: StreamChatTheme.of(context).colorTheme.white, @@ -50,38 +50,40 @@ Future showConfirmationDialog( style: StreamChatTheme.of(context).textTheme.headlineBold, ), SizedBox(height: 7.0), - Text( - question, - textAlign: TextAlign.center, - ), + if (question != null) + Text( + question, + textAlign: TextAlign.center, + ), SizedBox(height: 36.0), Container( - color: effect.color.withOpacity(effect.alpha ?? 1), + color: effect.color!.withOpacity(effect.alpha ?? 1), height: 1, ), Row( children: [ - Flexible( - child: Container( - alignment: Alignment.center, - child: TextButton( - onPressed: () { - Navigator.of(context).pop(false); - }, - child: Text( - cancelText, - style: StreamChatTheme.of(context) - .textTheme - .bodyBold - .copyWith( - color: StreamChatTheme.of(context) - .colorTheme - .black - .withOpacity(0.5)), + if (cancelText != null) + Flexible( + child: Container( + alignment: Alignment.center, + child: TextButton( + onPressed: () { + Navigator.of(context).pop(false); + }, + child: Text( + cancelText, + style: StreamChatTheme.of(context) + .textTheme + .bodyBold + .copyWith( + color: StreamChatTheme.of(context) + .colorTheme + .black + .withOpacity(0.5)), + ), ), ), ), - ), Flexible( child: Container( alignment: Alignment.center, @@ -110,17 +112,17 @@ Future showConfirmationDialog( }); } -Future showInfoDialog( +Future showInfoDialog( BuildContext context, { - String title, - Widget icon, - String details, - String okText, - StreamChatThemeData theme, + required String title, + required String okText, + Widget? icon, + String? details, + StreamChatThemeData? theme, }) { return showModalBottomSheet( - backgroundColor: theme?.colorTheme?.white ?? - StreamChatTheme.of(context).colorTheme.white, + backgroundColor: + theme?.colorTheme.white ?? StreamChatTheme.of(context).colorTheme.white, context: context, shape: RoundedRectangleBorder( borderRadius: BorderRadius.only( @@ -141,18 +143,18 @@ Future showInfoDialog( ), Text( title, - style: theme?.textTheme?.headlineBold ?? + style: theme?.textTheme.headlineBold ?? StreamChatTheme.of(context).textTheme.headlineBold, ), SizedBox( height: 7.0, ), - Text(details), + if (details != null) Text(details), SizedBox( height: 36.0, ), Container( - color: theme?.colorTheme?.black?.withOpacity(.08) ?? + color: theme?.colorTheme.black.withOpacity(.08) ?? StreamChatTheme.of(context).colorTheme.black.withOpacity(.08), height: 1.0, ), @@ -164,7 +166,7 @@ Future showInfoDialog( child: Text( okText, style: TextStyle( - color: theme?.colorTheme?.black?.withOpacity(0.5) ?? + color: theme?.colorTheme.black.withOpacity(0.5) ?? StreamChatTheme.of(context).colorTheme.accentBlue, fontWeight: FontWeight.w400, ), @@ -183,7 +185,7 @@ String getRandomPicUrl(User user) => 'https://getstream.io/random_png/?id=${user.id}&name=${user.name}'; /// Get websiteName from [hostName] -String getWebsiteName(String hostName) { +String? getWebsiteName(String hostName) { switch (hostName) { case 'reddit': return 'Reddit'; @@ -292,62 +294,44 @@ String fileSize(dynamic size, [int round = 2]) { } /// -StreamSvgIcon getFileTypeImage(String type) { +StreamSvgIcon getFileTypeImage(String? type) { switch (type) { case '7z': return StreamSvgIcon.filetype7z(); - break; case 'csv': return StreamSvgIcon.filetypeCsv(); - break; case 'doc': return StreamSvgIcon.filetypeDoc(); - break; case 'docx': return StreamSvgIcon.filetypeDocx(); - break; case 'html': return StreamSvgIcon.filetypeHtml(); - break; case 'md': return StreamSvgIcon.filetypeMd(); - break; case 'odt': return StreamSvgIcon.filetypeOdt(); - break; case 'pdf': return StreamSvgIcon.filetypePdf(); - break; case 'ppt': return StreamSvgIcon.filetypePpt(); - break; case 'pptx': return StreamSvgIcon.filetypePptx(); - break; case 'rar': return StreamSvgIcon.filetypeRar(); - break; case 'rtf': return StreamSvgIcon.filetypeRtf(); - break; case 'tar': return StreamSvgIcon.filetypeTar(); - break; case 'txt': return StreamSvgIcon.filetypeTxt(); - break; case 'xls': return StreamSvgIcon.filetypeXls(); - break; case 'xlsx': return StreamSvgIcon.filetypeXlsx(); - break; case 'zip': return StreamSvgIcon.filetypeZip(); - break; default: return StreamSvgIcon.filetypeGeneric(); - break; } } diff --git a/packages/stream_chat_flutter/lib/src/video_service.dart b/packages/stream_chat_flutter/lib/src/video_service.dart index 30d06c55..a4d0d1ff 100644 --- a/packages/stream_chat_flutter/lib/src/video_service.dart +++ b/packages/stream_chat_flutter/lib/src/video_service.dart @@ -4,7 +4,6 @@ import 'dart:typed_data'; import 'package:synchronized/synchronized.dart'; import 'package:video_compress/video_compress.dart'; import 'package:video_thumbnail/video_thumbnail.dart'; -import 'package:meta/meta.dart'; class IVideoService { static final IVideoService instance = IVideoService._(); @@ -27,10 +26,10 @@ class IVideoService { /// ); /// debugPrint(info.toJson()); /// ``` - Future compressVideo(String path) async { + Future compressVideo(String? path) async { return _lock.synchronized(() { return VideoCompress.compressVideo( - path, + path!, ); }); } @@ -39,8 +38,8 @@ class IVideoService { /// The video can be a local video file, or an URL repreents iOS or Android native supported video format. /// Speicify the maximum height or width for the thumbnail or 0 for same resolution as the original video. /// The lower quality value creates lower quality of the thumbnail image, but it gets ignored for PNG format. - Future generateVideoThumbnail({ - @required String video, + Future generateVideoThumbnail({ + required String video, ImageFormat imageFormat = ImageFormat.PNG, int maxHeight = 0, int maxWidth = 0, diff --git a/packages/stream_chat_flutter/lib/src/video_thumbnail_image.dart b/packages/stream_chat_flutter/lib/src/video_thumbnail_image.dart index 80607b8e..3d9d5429 100644 --- a/packages/stream_chat_flutter/lib/src/video_thumbnail_image.dart +++ b/packages/stream_chat_flutter/lib/src/video_thumbnail_image.dart @@ -10,16 +10,16 @@ import 'video_service.dart'; class VideoThumbnailImage extends StatefulWidget { final String video; - final double width; - final double height; - final BoxFit fit; + final double? width; + final double? height; + final BoxFit? fit; final ImageFormat format; - final Widget Function(BuildContext, Object) errorBuilder; - final WidgetBuilder placeholderBuilder; + final Widget Function(BuildContext, Object?)? errorBuilder; + final WidgetBuilder? placeholderBuilder; const VideoThumbnailImage({ - Key key, - @required this.video, + Key? key, + required this.video, this.width, this.height, this.fit, @@ -33,7 +33,7 @@ class VideoThumbnailImage extends StatefulWidget { } class _VideoThumbnailImageState extends State { - Future thumbnailFuture; + late Future thumbnailFuture; @override void initState() { @@ -57,13 +57,13 @@ class _VideoThumbnailImageState extends State { @override Widget build(BuildContext context) { - return FutureBuilder( + return FutureBuilder( future: thumbnailFuture, builder: (context, snapshot) { return AnimatedSwitcher( duration: const Duration(milliseconds: 350), child: Builder( - key: ValueKey>(snapshot), + key: ValueKey>(snapshot), builder: (_) { if (snapshot.hasError) { return widget.errorBuilder?.call(context, snapshot.error) ?? @@ -90,7 +90,7 @@ class _VideoThumbnailImageState extends State { ); } return Image.memory( - snapshot.data, + snapshot.data!, fit: widget.fit, height: widget.height, width: widget.width, diff --git a/packages/stream_chat_flutter/pubspec.yaml b/packages/stream_chat_flutter/pubspec.yaml index cccd7f4b..c8c4c469 100644 --- a/packages/stream_chat_flutter/pubspec.yaml +++ b/packages/stream_chat_flutter/pubspec.yaml @@ -1,53 +1,49 @@ name: stream_chat_flutter homepage: https://github.com/GetStream/stream-chat-flutter description: Stream Chat official Flutter SDK. Build your own chat experience using Dart and Flutter. -version: 1.5.3 +version: 2.0.0-nullsafety.0 repository: https://github.com/GetStream/stream-chat-flutter issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues environment: - sdk: ">=2.7.0 <3.0.0" + sdk: '>=2.12.0 <3.0.0' dependencies: flutter: sdk: flutter stream_chat_flutter_core: ^1.5.2 - photo_view: ^0.11.0 - rxdart: ^0.25.0 - scrollable_positioned_list: ^0.1.8 - jiffy: ^3.0.1 - flutter_svg: ^0.19.3 - flutter_portal: ^0.3.0 - cached_network_image: ^2.5.0 - shimmer: ^1.1.2 - flutter_markdown: ^0.5.2 - url_launcher: ^5.7.10 - emojis: ^0.9.3 - video_player: ^2.0.0 + photo_view: ^0.11.1 + rxdart: ^0.26.0 + scrollable_positioned_list: ^0.2.0-nullsafety.0 + jiffy: ^4.1.0 + flutter_svg: ^0.22.0 + flutter_portal: ^0.4.0 + cached_network_image: ^3.0.0 + shimmer: ^2.0.0 + flutter_markdown: ^0.6.1 + url_launcher: ^6.0.3 + video_player: ^2.1.1 chewie: ^1.0.0 - file_picker: ^2.1.5 - image_picker: ^0.6.7+17 - flutter_keyboard_visibility: ^4.0.2 - video_compress: ^2.1.1 - visibility_detector: ^0.1.5 - meta: ^1.2.4 - lottie: ^0.7.0+1 - substring_highlight: ^0.1.2 - flutter_slidable: ^0.5.7 - image_gallery_saver: ^1.6.7 - share_plus: ^1.2.0 - photo_manager: ^1.0.0 - ezanimation: ^0.4.1 - synchronized: ^2.1.0 - characters: ^1.0.0 - dio: ^3.0.10 - path_provider: ^1.6.27 - video_thumbnail: ^0.2.5+1 - - -dependency_overrides: - stream_chat_flutter_core: - path: ../stream_chat_flutter_core + file_picker: ^3.0.1 + image_picker: ^0.7.4 + flutter_keyboard_visibility: ^5.0.1 + video_compress: ^3.0.0 + visibility_detector: ^0.2.0 + http_parser: ^4.0.0 + meta: ^1.3.0 + lottie: ^1.0.1 + substring_highlight: ^1.0.26 + flutter_slidable: ^0.6.0 + image_gallery_saver: ^1.6.9 + share_plus: ^2.0.3 + photo_manager: ^1.1.4 + ezanimation: ^0.5.0 + synchronized: ^3.0.0 + dio: ^4.0.0 + characters: ^1.1.0 + path_provider: ^2.0.1 + video_thumbnail: ^0.3.3 + collection: ^1.15.0 flutter: assets: @@ -60,7 +56,7 @@ flutter: dev_dependencies: flutter_test: sdk: flutter - mockito: ^4.1.3 - pedantic: ^1.9.2 - golden_toolkit: ^0.6.0 - \ No newline at end of file + mocktail: ^0.1.2 + pedantic: ^1.11.0 + golden_toolkit: ^0.9.0 + diff --git a/packages/stream_chat_flutter/test/src/attachment_actions_modal_test.dart b/packages/stream_chat_flutter/test/src/attachment_actions_modal_test.dart index 1ef02cd9..762d4fe9 100644 --- a/packages/stream_chat_flutter/test/src/attachment_actions_modal_test.dart +++ b/packages/stream_chat_flutter/test/src/attachment_actions_modal_test.dart @@ -2,19 +2,19 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:mockito/mockito.dart'; +import 'package:mocktail/mocktail.dart'; import 'package:stream_chat_flutter/src/attachment_actions_modal.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'mocks.dart'; class MockAttachmentDownloader extends Mock { - ProgressCallback progressCallback; + ProgressCallback? progressCallback; Completer completer = Completer(); Future call( Attachment attachment, { - ProgressCallback progressCallback, + ProgressCallback? progressCallback, }) { this.progressCallback = progressCallback; return completer.future; @@ -22,17 +22,22 @@ class MockAttachmentDownloader extends Mock { } void main() { + setUpAll(() { + registerFallbackValue(MaterialPageRoute(builder: (context) => SizedBox())); + registerFallbackValue(Message()); + }); + testWidgets( 'it should show all the actions', (WidgetTester tester) async { final client = MockClient(); final clientState = MockClientState(); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); final themeData = ThemeData(); - final streamTheme = StreamChatThemeData.getDefaultTheme(themeData); + final streamTheme = StreamChatThemeData.fromTheme(themeData); await tester.pumpWidget( MaterialApp( theme: themeData, @@ -70,11 +75,11 @@ void main() { final client = MockClient(); final clientState = MockClientState(); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id2')); + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id2')); final themeData = ThemeData(); - final streamTheme = StreamChatThemeData.getDefaultTheme(themeData); + final streamTheme = StreamChatThemeData.fromTheme(themeData); await tester.pumpWidget( MaterialApp( theme: themeData, @@ -112,11 +117,11 @@ void main() { final client = MockClient(); final clientState = MockClientState(); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); final themeData = ThemeData(); - final streamTheme = StreamChatThemeData.getDefaultTheme(themeData); + final streamTheme = StreamChatThemeData.fromTheme(themeData); await tester.pumpWidget( MaterialApp( theme: themeData, @@ -153,11 +158,11 @@ void main() { final client = MockClient(); final clientState = MockClientState(); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); final themeData = ThemeData(); - final streamTheme = StreamChatThemeData.getDefaultTheme(themeData); + final streamTheme = StreamChatThemeData.fromTheme(themeData); final mockObserver = MockNavigatorObserver(); @@ -190,7 +195,7 @@ void main() { ), ); await tester.tap(find.text('Reply')); - verify(mockObserver.didPop(any, any)); + verify(() => mockObserver.didPop(any(), any())); }, ); @@ -200,11 +205,11 @@ void main() { final client = MockClient(); final clientState = MockClientState(); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); final themeData = ThemeData(); - final streamTheme = StreamChatThemeData.getDefaultTheme(themeData); + final streamTheme = StreamChatThemeData.fromTheme(themeData); final onShowMessage = MockVoidCallback(); await tester.pumpWidget( @@ -234,7 +239,7 @@ void main() { ), ); await tester.tap(find.text('Show in Chat')); - verify(onShowMessage.call()).called(1); + verify(() => onShowMessage.call()).called(1); }, ); @@ -245,11 +250,11 @@ void main() { final clientState = MockClientState(); final mockChannel = MockChannel(); - when(mockChannel.updateMessage(any)).thenAnswer((_) { - return; + when(() => mockChannel.updateMessage(any())).thenAnswer((_) async { + return UpdateMessageResponse(); }); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); final message = Message( text: 'test', @@ -273,7 +278,7 @@ void main() { builder: (context, child) { return StreamChat( client: client, - child: child, + child: child!, ); }, home: StreamChannel( @@ -287,11 +292,11 @@ void main() { ), ); await tester.tap(find.text('Delete')); - verify(mockChannel.updateMessage(message.copyWith( - attachments: [ - message.attachments[1], - ], - ))).called(1); + verify(() => mockChannel.updateMessage(message.copyWith( + attachments: [ + message.attachments[1], + ], + ))).called(1); }, ); @@ -302,11 +307,11 @@ void main() { final clientState = MockClientState(); final mockChannel = MockChannel(); - when(mockChannel.updateMessage(any)).thenAnswer((_) { - return; + when(() => mockChannel.updateMessage(any())).thenAnswer((_) async { + return UpdateMessageResponse(); }); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); final message = Message( text: 'test', @@ -326,7 +331,7 @@ void main() { builder: (context, child) { return StreamChat( client: client, - child: child, + child: child!, ); }, home: StreamChannel( @@ -340,9 +345,9 @@ void main() { ), ); await tester.tap(find.text('Delete')); - verify(mockChannel.updateMessage(message.copyWith( - attachments: [], - ))).called(1); + verify(() => mockChannel.updateMessage(message.copyWith( + attachments: [], + ))).called(1); }, ); @@ -353,11 +358,11 @@ void main() { final clientState = MockClientState(); final mockChannel = MockChannel(); - when(mockChannel.deleteMessage(any)).thenAnswer((_) { - return; + when(() => mockChannel.deleteMessage(any())).thenAnswer((_) async { + return EmptyResponse(); }); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); final message = Message( user: User( @@ -376,7 +381,7 @@ void main() { builder: (context, child) { return StreamChat( client: client, - child: child, + child: child!, ); }, home: StreamChannel( @@ -390,7 +395,7 @@ void main() { ), ); await tester.tap(find.text('Delete')); - verify(mockChannel.deleteMessage(message)).called(1); + verify(() => mockChannel.deleteMessage(message)).called(1); }, ); @@ -400,8 +405,8 @@ void main() { final client = MockClient(); final clientState = MockClientState(); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); final imageDownloader = MockAttachmentDownloader(); @@ -410,7 +415,7 @@ void main() { builder: (context, child) { return StreamChat( client: client, - child: child, + child: child!, ); }, home: Container( @@ -435,15 +440,15 @@ void main() { await tester.tap(find.text('Save Image')); - imageDownloader.progressCallback(0, 100); + imageDownloader.progressCallback!(0, 100); await tester.pump(); expect(find.text('0%'), findsOneWidget); - imageDownloader.progressCallback(50, 100); + imageDownloader.progressCallback!(50, 100); await tester.pump(); expect(find.text('50%'), findsOneWidget); - imageDownloader.progressCallback(100, 100); + imageDownloader.progressCallback!(100, 100); imageDownloader.completer.complete('path'); await tester.pump(); expect(find.byKey(Key('completedIcon')), findsOneWidget); @@ -457,8 +462,8 @@ void main() { final client = MockClient(); final clientState = MockClientState(); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); final fileDownloader = MockAttachmentDownloader(); @@ -467,7 +472,7 @@ void main() { builder: (context, child) { return StreamChat( client: client, - child: child, + child: child!, ); }, home: Container( @@ -492,15 +497,15 @@ void main() { await tester.tap(find.text('Save Video')); - fileDownloader.progressCallback(0, 100); + fileDownloader.progressCallback!(0, 100); await tester.pump(); expect(find.text('0%'), findsOneWidget); - fileDownloader.progressCallback(50, 100); + fileDownloader.progressCallback!(50, 100); await tester.pump(); expect(find.text('50%'), findsOneWidget); - fileDownloader.progressCallback(100, 100); + fileDownloader.progressCallback!(100, 100); fileDownloader.completer.complete('path'); await tester.pump(); expect(find.byKey(Key('completedIcon')), findsOneWidget); diff --git a/packages/stream_chat_flutter/test/src/attachment_widgets_test.dart b/packages/stream_chat_flutter/test/src/attachment_widgets_test.dart index b547e80f..b1eb70e2 100644 --- a/packages/stream_chat_flutter/test/src/attachment_widgets_test.dart +++ b/packages/stream_chat_flutter/test/src/attachment_widgets_test.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:mockito/mockito.dart'; +import 'package:mocktail/mocktail.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'mocks.dart'; @@ -12,10 +12,10 @@ void main() { final channel = MockChannel(); final channelState = MockChannelState(); - when(channel.state).thenReturn(channelState); + when(() => channel.state).thenReturn(channelState); final themeData = ThemeData(); - final streamTheme = StreamChatThemeData.getDefaultTheme(themeData); + final streamTheme = StreamChatThemeData.fromTheme(themeData); await tester.pumpWidget( MaterialApp( diff --git a/packages/stream_chat_flutter/test/src/back_button_test.dart b/packages/stream_chat_flutter/test/src/back_button_test.dart index 3ee88ed1..a9c5f724 100644 --- a/packages/stream_chat_flutter/test/src/back_button_test.dart +++ b/packages/stream_chat_flutter/test/src/back_button_test.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:mockito/mockito.dart'; +import 'package:mocktail/mocktail.dart'; import 'package:stream_chat_flutter/src/back_button.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; @@ -19,7 +19,7 @@ void main() { return Material( child: Center( child: StreamChatTheme( - data: StreamChatThemeData.getDefaultTheme(theme), + data: StreamChatThemeData.fromTheme(theme), child: StreamBackButton(), ), ), @@ -51,7 +51,7 @@ void main() { home: Material( child: Center( child: StreamChatTheme( - data: StreamChatThemeData.getDefaultTheme(theme), + data: StreamChatThemeData.fromTheme(theme), child: StreamBackButton(), ), ), @@ -82,7 +82,7 @@ void main() { return Material( child: Center( child: StreamChatTheme( - data: StreamChatThemeData.getDefaultTheme(theme), + data: StreamChatThemeData.fromTheme(theme), child: StreamBackButton( onPressed: () => customCallbackWasCalled = true, ), @@ -121,7 +121,9 @@ void main() { final client = MockClient(); final clientState = MockClientState(); - when(client.state).thenReturn(clientState); + when(() => client.state).thenReturn(clientState); + when(() => clientState.totalUnreadCountStream) + .thenAnswer((i) => Stream.value(0)); await tester.pumpWidget( MaterialApp( diff --git a/packages/stream_chat_flutter/test/src/channel_header_test.dart b/packages/stream_chat_flutter/test/src/channel_header_test.dart index 1a5cb79d..c284cad5 100644 --- a/packages/stream_chat_flutter/test/src/channel_header_test.dart +++ b/packages/stream_chat_flutter/test/src/channel_header_test.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:mockito/mockito.dart'; +import 'package:mocktail/mocktail.dart'; import 'package:stream_chat_flutter/src/channel_info.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; @@ -16,28 +16,33 @@ void main() { final channelState = MockChannelState(); final lastMessageAt = DateTime.parse('2020-06-22 12:00:00'); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); - when(channel.lastMessageAt).thenReturn(lastMessageAt); - when(channel.state).thenReturn(channelState); - when(channel.client).thenReturn(client); - when(channel.isMuted).thenReturn(false); - when(channel.isMutedStream).thenAnswer((i) => Stream.value(false)); - when(channel.extraDataStream).thenAnswer((i) => Stream.value({ + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => channel.lastMessageAt).thenReturn(lastMessageAt); + when(() => channel.state).thenReturn(channelState); + when(() => channel.client).thenReturn(client); + when(() => channel.isMuted).thenReturn(false); + when(() => channel.isMutedStream).thenAnswer((i) => Stream.value(false)); + when(() => channel.extraDataStream).thenAnswer((i) => Stream.value({ 'name': 'test', })); - when(channel.extraData).thenReturn({ + when(() => channel.extraData).thenReturn({ 'name': 'test', }); - when(channelState.unreadCount).thenReturn(1); - when(channelState.unreadCountStream).thenAnswer((i) => Stream.value(1)); - when(channelState.membersStream).thenAnswer((i) => Stream.value([ + when(() => channelState.unreadCount).thenReturn(1); + when(() => client.wsConnectionStatusStream) + .thenAnswer((_) => Stream.value(ConnectionStatus.connected)); + when(() => channelState.unreadCountStream) + .thenAnswer((i) => Stream.value(1)); + when(() => clientState.totalUnreadCountStream) + .thenAnswer((i) => Stream.value(1)); + when(() => channelState.membersStream).thenAnswer((i) => Stream.value([ Member( userId: 'user-id', user: User(id: 'user-id'), ) ])); - when(channelState.members).thenReturn([ + when(() => channelState.members).thenReturn([ Member( userId: 'user-id', user: User(id: 'user-id'), @@ -72,35 +77,38 @@ void main() { final channelState = MockChannelState(); final lastMessageAt = DateTime.parse('2020-06-22 12:00:00'); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); - when(channel.lastMessageAt).thenReturn(lastMessageAt); - when(channel.state).thenReturn(channelState); - when(channel.client).thenReturn(client); - when(channel.isMuted).thenReturn(false); - when(channel.isMutedStream).thenAnswer((i) => Stream.value(false)); - when(channel.extraDataStream).thenAnswer((i) => Stream.value({ + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => channel.lastMessageAt).thenReturn(lastMessageAt); + when(() => channel.state).thenReturn(channelState); + when(() => channel.client).thenReturn(client); + when(() => channel.isMuted).thenReturn(false); + when(() => channel.isMutedStream).thenAnswer((i) => Stream.value(false)); + when(() => channel.extraDataStream).thenAnswer((i) => Stream.value({ 'name': 'test', })); - when(channel.extraData).thenReturn({ + when(() => channel.extraData).thenReturn({ 'name': 'test', }); - when(channelState.unreadCount).thenReturn(1); - when(channelState.unreadCountStream).thenAnswer((i) => Stream.value(1)); - when(channelState.membersStream).thenAnswer((i) => Stream.value([ + when(() => channelState.unreadCount).thenReturn(1); + when(() => channelState.unreadCountStream) + .thenAnswer((i) => Stream.value(1)); + when(() => channelState.membersStream).thenAnswer((i) => Stream.value([ Member( userId: 'user-id', user: User(id: 'user-id'), ) ])); - when(channelState.members).thenReturn([ + when(() => channelState.members).thenReturn([ Member( userId: 'user-id', user: User(id: 'user-id'), ), ]); - when(client.wsConnectionStatusStream) + when(() => client.wsConnectionStatusStream) .thenAnswer((_) => Stream.value(ConnectionStatus.disconnected)); + when(() => clientState.totalUnreadCountStream) + .thenAnswer((i) => Stream.value(1)); await tester.pumpWidget(MaterialApp( home: StreamChat( @@ -131,36 +139,38 @@ void main() { final channelState = MockChannelState(); final lastMessageAt = DateTime.parse('2020-06-22 12:00:00'); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); - when(channel.lastMessageAt).thenReturn(lastMessageAt); - when(channel.state).thenReturn(channelState); - when(channel.client).thenReturn(client); - when(channel.isMuted).thenReturn(false); - when(channel.isMutedStream).thenAnswer((i) => Stream.value(false)); - when(channel.extraDataStream).thenAnswer((i) => Stream.value({ + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => channel.lastMessageAt).thenReturn(lastMessageAt); + when(() => channel.state).thenReturn(channelState); + when(() => channel.client).thenReturn(client); + when(() => channel.isMuted).thenReturn(false); + when(() => channel.isMutedStream).thenAnswer((i) => Stream.value(false)); + when(() => channel.extraDataStream).thenAnswer((i) => Stream.value({ 'name': 'test', })); - when(channel.extraData).thenReturn({ + when(() => channel.extraData).thenReturn({ 'name': 'test', }); - when(channelState.unreadCount).thenReturn(1); - when(channelState.unreadCountStream).thenAnswer((i) => Stream.value(1)); - when(channelState.membersStream).thenAnswer((i) => Stream.value([ + when(() => channelState.unreadCount).thenReturn(1); + when(() => channelState.unreadCountStream) + .thenAnswer((i) => Stream.value(1)); + when(() => channelState.membersStream).thenAnswer((i) => Stream.value([ Member( userId: 'user-id', user: User(id: 'user-id'), ) ])); - when(channelState.members).thenReturn([ + when(() => channelState.members).thenReturn([ Member( userId: 'user-id', user: User(id: 'user-id'), ), ]); - when(client.wsConnectionStatusStream) + when(() => client.wsConnectionStatusStream) .thenAnswer((_) => Stream.value(ConnectionStatus.connecting)); - when(channel.initialized).thenAnswer((_) => Future.value(true)); + when(() => clientState.totalUnreadCountStream) + .thenAnswer((i) => Stream.value(1)); await tester.pumpWidget(MaterialApp( home: StreamChat( @@ -194,33 +204,38 @@ void main() { final channelState = MockChannelState(); final lastMessageAt = DateTime.parse('2020-06-22 12:00:00'); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); - when(channel.lastMessageAt).thenReturn(lastMessageAt); - when(channel.state).thenReturn(channelState); - when(channel.client).thenReturn(client); - when(channel.isMuted).thenReturn(false); - when(channel.isMutedStream).thenAnswer((i) => Stream.value(false)); - when(channel.extraDataStream).thenAnswer((i) => Stream.value({ + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => channel.lastMessageAt).thenReturn(lastMessageAt); + when(() => channel.state).thenReturn(channelState); + when(() => channel.client).thenReturn(client); + when(() => channel.isMuted).thenReturn(false); + when(() => channel.isMutedStream).thenAnswer((i) => Stream.value(false)); + when(() => channel.extraDataStream).thenAnswer((i) => Stream.value({ 'name': 'test', })); - when(channel.extraData).thenReturn({ + when(() => channel.extraData).thenReturn({ 'name': 'test', }); - when(channelState.unreadCount).thenReturn(1); - when(channelState.unreadCountStream).thenAnswer((i) => Stream.value(1)); - when(channelState.membersStream).thenAnswer((i) => Stream.value([ + when(() => channelState.unreadCount).thenReturn(1); + when(() => channelState.unreadCountStream) + .thenAnswer((i) => Stream.value(1)); + when(() => channelState.membersStream).thenAnswer((i) => Stream.value([ Member( userId: 'user-id', user: User(id: 'user-id'), ) ])); - when(channelState.members).thenReturn([ + when(() => channelState.members).thenReturn([ Member( userId: 'user-id', user: User(id: 'user-id'), ), ]); + when(() => client.wsConnectionStatusStream) + .thenAnswer((_) => Stream.value(ConnectionStatus.connecting)); + when(() => clientState.totalUnreadCountStream) + .thenAnswer((i) => Stream.value(1)); await tester.pumpWidget(MaterialApp( home: StreamChat( @@ -263,34 +278,35 @@ void main() { final channelState = MockChannelState(); final lastMessageAt = DateTime.parse('2020-06-22 12:00:00'); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); - when(channel.lastMessageAt).thenReturn(lastMessageAt); - when(channel.state).thenReturn(channelState); - when(channel.client).thenReturn(client); - when(channel.isMuted).thenReturn(false); - when(channel.isMutedStream).thenAnswer((i) => Stream.value(false)); - when(channel.extraDataStream).thenAnswer((i) => Stream.value({ + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => channel.lastMessageAt).thenReturn(lastMessageAt); + when(() => channel.state).thenReturn(channelState); + when(() => channel.client).thenReturn(client); + when(() => channel.isMuted).thenReturn(false); + when(() => channel.isMutedStream).thenAnswer((i) => Stream.value(false)); + when(() => channel.extraDataStream).thenAnswer((i) => Stream.value({ 'name': 'test', })); - when(channel.extraData).thenReturn({ + when(() => channel.extraData).thenReturn({ 'name': 'test', }); - when(channelState.unreadCount).thenReturn(1); - when(channelState.unreadCountStream).thenAnswer((i) => Stream.value(1)); - when(channelState.membersStream).thenAnswer((i) => Stream.value([ + when(() => channelState.unreadCount).thenReturn(1); + when(() => channelState.unreadCountStream) + .thenAnswer((i) => Stream.value(1)); + when(() => channelState.membersStream).thenAnswer((i) => Stream.value([ Member( userId: 'user-id', user: User(id: 'user-id'), ) ])); - when(channelState.members).thenReturn([ + when(() => channelState.members).thenReturn([ Member( userId: 'user-id', user: User(id: 'user-id'), ), ]); - when(client.wsConnectionStatusStream) + when(() => client.wsConnectionStatusStream) .thenAnswer((_) => Stream.value(ConnectionStatus.disconnected)); await tester.pumpWidget(MaterialApp( @@ -328,33 +344,38 @@ void main() { final channelState = MockChannelState(); final lastMessageAt = DateTime.parse('2020-06-22 12:00:00'); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); - when(channel.lastMessageAt).thenReturn(lastMessageAt); - when(channel.state).thenReturn(channelState); - when(channel.client).thenReturn(client); - when(channel.isMuted).thenReturn(false); - when(channel.isMutedStream).thenAnswer((i) => Stream.value(false)); - when(channel.extraDataStream).thenAnswer((i) => Stream.value({ + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => channel.lastMessageAt).thenReturn(lastMessageAt); + when(() => channel.state).thenReturn(channelState); + when(() => channel.client).thenReturn(client); + when(() => channel.isMuted).thenReturn(false); + when(() => channel.isMutedStream).thenAnswer((i) => Stream.value(false)); + when(() => channel.extraDataStream).thenAnswer((i) => Stream.value({ 'name': 'test', })); - when(channel.extraData).thenReturn({ + when(() => channel.extraData).thenReturn({ 'name': 'test', }); - when(channelState.unreadCount).thenReturn(1); - when(channelState.unreadCountStream).thenAnswer((i) => Stream.value(1)); - when(channelState.membersStream).thenAnswer((i) => Stream.value([ + when(() => channelState.unreadCount).thenReturn(1); + when(() => channelState.unreadCountStream) + .thenAnswer((i) => Stream.value(1)); + when(() => channelState.membersStream).thenAnswer((i) => Stream.value([ Member( userId: 'user-id', user: User(id: 'user-id'), ) ])); - when(channelState.members).thenReturn([ + when(() => channelState.members).thenReturn([ Member( userId: 'user-id', user: User(id: 'user-id'), ), ]); + when(() => client.wsConnectionStatusStream) + .thenAnswer((_) => Stream.value(ConnectionStatus.connecting)); + when(() => clientState.totalUnreadCountStream) + .thenAnswer((i) => Stream.value(1)); var backPressed = false; var imageTapped = false; diff --git a/packages/stream_chat_flutter/test/src/channel_image_test.dart b/packages/stream_chat_flutter/test/src/channel_image_test.dart index cea48d2f..1068bb52 100644 --- a/packages/stream_chat_flutter/test/src/channel_image_test.dart +++ b/packages/stream_chat_flutter/test/src/channel_image_test.dart @@ -1,7 +1,7 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:mockito/mockito.dart'; +import 'package:mocktail/mocktail.dart'; import 'package:stream_chat_flutter/src/group_image.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; @@ -16,15 +16,15 @@ void main() { final channel = MockChannel(); final channelState = MockChannelState(); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); - when(channel.state).thenReturn(channelState); - when(channel.client).thenReturn(client); - when(channel.extraDataStream).thenAnswer((i) => Stream.value({ + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => channel.state).thenReturn(channelState); + when(() => channel.client).thenReturn(client); + when(() => channel.extraDataStream).thenAnswer((i) => Stream.value({ 'name': 'test', 'image': 'imagetest', })); - when(channel.extraData).thenReturn({ + when(() => channel.extraData).thenReturn({ 'name': 'test', 'image': 'imagetest', }); @@ -55,17 +55,17 @@ void main() { final channel = MockChannel(); final channelState = MockChannelState(); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); - when(channel.state).thenReturn(channelState); - when(channel.client).thenReturn(client); - when(channel.extraDataStream).thenAnswer((i) => Stream.value({ + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => channel.state).thenReturn(channelState); + when(() => channel.client).thenReturn(client); + when(() => channel.extraDataStream).thenAnswer((i) => Stream.value({ 'name': 'test', })); - when(channel.extraData).thenReturn({ + when(() => channel.extraData).thenReturn({ 'name': 'test', }); - when(channelState.membersStream).thenAnswer((i) => Stream.value([ + when(() => channelState.membersStream).thenAnswer((i) => Stream.value([ Member( userId: 'user-id', user: User(id: 'user-id'), @@ -80,7 +80,7 @@ void main() { ), ) ])); - when(channelState.members).thenReturn([ + when(() => channelState.members).thenReturn([ Member( userId: 'user-id2', user: User( @@ -95,7 +95,7 @@ void main() { user: User(id: 'user-id'), ) ]); - when(clientState.usersStream).thenAnswer((i) => Stream.value({ + when(() => clientState.usersStream).thenAnswer((i) => Stream.value({ 'user-id2': User( id: 'user-id2', extraData: { @@ -103,7 +103,7 @@ void main() { }, ), })); - when(channel.extraData).thenReturn({ + when(() => channel.extraData).thenReturn({ 'name': 'test', }); @@ -133,17 +133,17 @@ void main() { final channel = MockChannel(); final channelState = MockChannelState(); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); - when(channel.state).thenReturn(channelState); - when(channel.client).thenReturn(client); - when(channel.extraDataStream).thenAnswer((i) => Stream.value({ + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => channel.state).thenReturn(channelState); + when(() => channel.client).thenReturn(client); + when(() => channel.extraDataStream).thenAnswer((i) => Stream.value({ 'name': 'test', })); - when(channel.extraData).thenReturn({ + when(() => channel.extraData).thenReturn({ 'name': 'test', }); - when(channelState.membersStream).thenAnswer((i) => Stream.value([ + when(() => channelState.membersStream).thenAnswer((i) => Stream.value([ Member( userId: 'user-id', user: User( @@ -172,7 +172,7 @@ void main() { ), ), ])); - when(channelState.members).thenReturn([ + when(() => channelState.members).thenReturn([ Member( userId: 'user-id', user: User( @@ -230,15 +230,15 @@ void main() { final channel = MockChannel(); final channelState = MockChannelState(); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); - when(channel.state).thenReturn(channelState); - when(channel.client).thenReturn(client); - when(channel.extraDataStream).thenAnswer((i) => Stream.value({ + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => channel.state).thenReturn(channelState); + when(() => channel.client).thenReturn(client); + when(() => channel.extraDataStream).thenAnswer((i) => Stream.value({ 'name': 'test', 'image': 'imagetest', })); - when(channel.extraData).thenReturn({ + when(() => channel.extraData).thenReturn({ 'name': 'test', 'image': 'imagetest', }); diff --git a/packages/stream_chat_flutter/test/src/channel_list_header_test.dart b/packages/stream_chat_flutter/test/src/channel_list_header_test.dart index 6fc4d428..f77ea04a 100644 --- a/packages/stream_chat_flutter/test/src/channel_list_header_test.dart +++ b/packages/stream_chat_flutter/test/src/channel_list_header_test.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:mockito/mockito.dart'; +import 'package:mocktail/mocktail.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'mocks.dart'; @@ -12,9 +12,9 @@ void main() { final client = MockClient(); final clientState = MockClientState(); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); - when(client.wsConnectionStatusStream) + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => client.wsConnectionStatusStream) .thenAnswer((_) => Stream.value(ConnectionStatus.connected)); await tester.pumpWidget( @@ -42,9 +42,9 @@ void main() { final client = MockClient(); final clientState = MockClientState(); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); - when(client.wsConnectionStatusStream) + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => client.wsConnectionStatusStream) .thenAnswer((_) => Stream.value(ConnectionStatus.disconnected)); await tester.pumpWidget( @@ -71,9 +71,9 @@ void main() { final client = MockClient(); final clientState = MockClientState(); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); - when(client.wsConnectionStatusStream) + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => client.wsConnectionStatusStream) .thenAnswer((_) => Stream.value(ConnectionStatus.connecting)); await tester.pumpWidget( @@ -100,9 +100,9 @@ void main() { final client = MockClient(); final clientState = MockClientState(); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); - when(client.wsConnectionStatusStream) + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => client.wsConnectionStatusStream) .thenAnswer((_) => Stream.value(ConnectionStatus.connecting)); await tester.pumpWidget( @@ -140,9 +140,9 @@ void main() { final client = MockClient(); final clientState = MockClientState(); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); - when(client.wsConnectionStatusStream) + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => client.wsConnectionStatusStream) .thenAnswer((_) => Stream.value(ConnectionStatus.connecting)); var tapped = false; @@ -174,9 +174,9 @@ void main() { final client = MockClient(); final clientState = MockClientState(); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); - when(client.wsConnectionStatusStream) + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => client.wsConnectionStatusStream) .thenAnswer((_) => Stream.value(ConnectionStatus.connecting)); var tapped = 0; diff --git a/packages/stream_chat_flutter/test/src/channel_name_test.dart b/packages/stream_chat_flutter/test/src/channel_name_test.dart index bf6f7e55..586a0776 100644 --- a/packages/stream_chat_flutter/test/src/channel_name_test.dart +++ b/packages/stream_chat_flutter/test/src/channel_name_test.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:mockito/mockito.dart'; +import 'package:mocktail/mocktail.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'mocks.dart'; @@ -15,40 +15,41 @@ void main() { final channelState = MockChannelState(); final lastMessageAt = DateTime.parse('2020-06-22 12:00:00'); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); - when(channel.lastMessageAt).thenReturn(lastMessageAt); - when(channel.state).thenReturn(channelState); - when(channel.client).thenReturn(client); - when(channel.isMuted).thenReturn(false); - when(channel.isMutedStream).thenAnswer((i) => Stream.value(false)); - when(channel.extraDataStream).thenAnswer((i) => Stream.value({ + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => channel.lastMessageAt).thenReturn(lastMessageAt); + when(() => channel.state).thenReturn(channelState); + when(() => channel.client).thenReturn(client); + when(() => channel.isMuted).thenReturn(false); + when(() => channel.isMutedStream).thenAnswer((i) => Stream.value(false)); + when(() => channel.extraDataStream).thenAnswer((i) => Stream.value({ 'name': 'test', })); - when(channel.extraData).thenReturn({ + when(() => channel.extraData).thenReturn({ 'name': 'test', }); - when(channelState.unreadCount).thenReturn(1); - when(channelState.unreadCountStream).thenAnswer((i) => Stream.value(1)); - when(channelState.membersStream).thenAnswer((i) => Stream.value([ + when(() => channelState.unreadCount).thenReturn(1); + when(() => channelState.unreadCountStream) + .thenAnswer((i) => Stream.value(1)); + when(() => channelState.membersStream).thenAnswer((i) => Stream.value([ Member( userId: 'user-id', user: User(id: 'user-id'), ) ])); - when(channelState.members).thenReturn([ + when(() => channelState.members).thenReturn([ Member( userId: 'user-id', user: User(id: 'user-id'), ), ]); - when(channelState.messages).thenReturn([ + when(() => channelState.messages).thenReturn([ Message( text: 'hello', user: User(id: 'other-user'), ) ]); - when(channelState.messagesStream).thenAnswer((i) => Stream.value([ + when(() => channelState.messagesStream).thenAnswer((i) => Stream.value([ Message( text: 'hello', user: User(id: 'other-user'), diff --git a/packages/stream_chat_flutter/test/src/channel_preview_test.dart b/packages/stream_chat_flutter/test/src/channel_preview_test.dart index d8ed9f72..b913489e 100644 --- a/packages/stream_chat_flutter/test/src/channel_preview_test.dart +++ b/packages/stream_chat_flutter/test/src/channel_preview_test.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:mockito/mockito.dart'; +import 'package:mocktail/mocktail.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'mocks.dart'; @@ -15,50 +15,55 @@ void main() { final channelState = MockChannelState(); final lastMessageAt = DateTime.parse('2020-06-22 12:00:00'); - when(channel.cid).thenReturn('cid'); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); - when(channel.lastMessageAt).thenReturn(lastMessageAt); - when(channel.state).thenReturn(channelState); - when(channel.client).thenReturn(client); - when(channel.isMuted).thenReturn(false); - when(channel.isMutedStream).thenAnswer((i) => Stream.value(false)); - when(channel.extraDataStream).thenAnswer((i) => Stream.value({ + when(() => channel.cid).thenReturn('cid'); + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => channel.lastMessageAt).thenReturn(lastMessageAt); + when(() => channel.state).thenReturn(channelState); + when(() => channel.client).thenReturn(client); + when(() => channel.isMuted).thenReturn(false); + when(() => channel.isMutedStream).thenAnswer((i) => Stream.value(false)); + when(() => channel.extraDataStream).thenAnswer((i) => Stream.value({ 'name': 'test name', })); - when(channel.extraData).thenReturn({ + when(() => channel.extraData).thenReturn({ 'name': 'test name', }); - when(clientState.channels).thenReturn({ - channel.cid: channel, + when(() => clientState.channels).thenReturn({ + channel.cid!: channel, }); - when(channelState.unreadCount).thenReturn(1); - when(channelState.unreadCountStream).thenAnswer((i) => Stream.value(1)); - when(channelState.membersStream).thenAnswer((i) => Stream.value([ + when(() => channelState.unreadCount).thenReturn(1); + when(() => channelState.unreadCountStream) + .thenAnswer((i) => Stream.value(1)); + when(() => channelState.membersStream).thenAnswer((i) => Stream.value([ Member( userId: 'user-id', user: User(id: 'user-id'), ) ])); - when(channelState.members).thenReturn([ + when(() => channelState.members).thenReturn([ Member( userId: 'user-id', user: User(id: 'user-id'), ), ]); - when(channelState.messages).thenReturn([ + when(() => channelState.messages).thenReturn([ Message( text: 'hello', user: User(id: 'other-user'), ) ]); - when(channelState.messagesStream).thenAnswer((i) => Stream.value([ + when(() => channelState.messagesStream).thenAnswer((i) => Stream.value([ Message( text: 'hello', user: User(id: 'other-user'), ) ])); + when(() => channelState.typingEvents).thenReturn([]); + when(() => channelState.typingEventsStream) + .thenAnswer((_) => Stream.value([])); + await tester.pumpWidget(MaterialApp( home: StreamChat( client: client, diff --git a/packages/stream_chat_flutter/test/src/date_divider_test.dart b/packages/stream_chat_flutter/test/src/date_divider_test.dart index 4856a0d3..eb5e9b70 100644 --- a/packages/stream_chat_flutter/test/src/date_divider_test.dart +++ b/packages/stream_chat_flutter/test/src/date_divider_test.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:mockito/mockito.dart'; +import 'package:mocktail/mocktail.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'mocks.dart'; @@ -12,8 +12,8 @@ void main() { final client = MockClient(); final clientState = MockClientState(); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); await tester.pumpWidget(MaterialApp( home: StreamChat( diff --git a/packages/stream_chat_flutter/test/src/deleted_message_test.dart b/packages/stream_chat_flutter/test/src/deleted_message_test.dart index bb93b28b..854c2492 100644 --- a/packages/stream_chat_flutter/test/src/deleted_message_test.dart +++ b/packages/stream_chat_flutter/test/src/deleted_message_test.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:golden_toolkit/golden_toolkit.dart'; -import 'package:mockito/mockito.dart'; +import 'package:mocktail/mocktail.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'mocks.dart'; @@ -13,8 +13,8 @@ void main() { final client = MockClient(); final clientState = MockClientState(); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); await tester.pumpWidget(MaterialApp( home: StreamChat( @@ -45,21 +45,20 @@ void main() { final channelState = MockChannelState(); final lastMessageAt = DateTime.parse('2020-06-22 12:00:00'); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); - when(channel.lastMessageAt).thenReturn(lastMessageAt); - when(channel.state).thenReturn(channelState); - when(channel.client).thenReturn(client); - when(channel.initialized).thenAnswer((_) => Future.value(true)); - when(channel.extraDataStream).thenAnswer((i) => Stream.value({ + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => channel.lastMessageAt).thenReturn(lastMessageAt); + when(() => channel.state).thenReturn(channelState); + when(() => channel.client).thenReturn(client); + when(() => channel.extraDataStream).thenAnswer((i) => Stream.value({ 'name': 'test', })); - when(channel.extraData).thenReturn({ + when(() => channel.extraData).thenReturn({ 'name': 'test', }); - when(clientState.totalUnreadCount).thenReturn(10); - when(clientState.totalUnreadCountStream) + when(() => clientState.totalUnreadCount).thenReturn(10); + when(() => clientState.totalUnreadCountStream) .thenAnswer((i) => Stream.value(10)); final materialTheme = ThemeData.light(); @@ -98,21 +97,20 @@ void main() { final channelState = MockChannelState(); final lastMessageAt = DateTime.parse('2020-06-22 12:00:00'); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); - when(channel.lastMessageAt).thenReturn(lastMessageAt); - when(channel.state).thenReturn(channelState); - when(channel.client).thenReturn(client); - when(channel.initialized).thenAnswer((_) => Future.value(true)); - when(channel.extraDataStream).thenAnswer((i) => Stream.value({ + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => channel.lastMessageAt).thenReturn(lastMessageAt); + when(() => channel.state).thenReturn(channelState); + when(() => channel.client).thenReturn(client); + when(() => channel.extraDataStream).thenAnswer((i) => Stream.value({ 'name': 'test', })); - when(channel.extraData).thenReturn({ + when(() => channel.extraData).thenReturn({ 'name': 'test', }); - when(clientState.totalUnreadCount).thenReturn(10); - when(clientState.totalUnreadCountStream) + when(() => clientState.totalUnreadCount).thenReturn(10); + when(() => clientState.totalUnreadCountStream) .thenAnswer((i) => Stream.value(10)); final materialTheme = ThemeData.dark(); @@ -151,21 +149,20 @@ void main() { final channelState = MockChannelState(); final lastMessageAt = DateTime.parse('2020-06-22 12:00:00'); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); - when(channel.lastMessageAt).thenReturn(lastMessageAt); - when(channel.state).thenReturn(channelState); - when(channel.client).thenReturn(client); - when(channel.initialized).thenAnswer((_) => Future.value(true)); - when(channel.extraDataStream).thenAnswer((i) => Stream.value({ + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => channel.lastMessageAt).thenReturn(lastMessageAt); + when(() => channel.state).thenReturn(channelState); + when(() => channel.client).thenReturn(client); + when(() => channel.extraDataStream).thenAnswer((i) => Stream.value({ 'name': 'test', })); - when(channel.extraData).thenReturn({ + when(() => channel.extraData).thenReturn({ 'name': 'test', }); - when(clientState.totalUnreadCount).thenReturn(10); - when(clientState.totalUnreadCountStream) + when(() => clientState.totalUnreadCount).thenReturn(10); + when(() => clientState.totalUnreadCountStream) .thenAnswer((i) => Stream.value(10)); final materialTheme = ThemeData.light(); diff --git a/packages/stream_chat_flutter/test/src/full_screen_media_test.dart b/packages/stream_chat_flutter/test/src/full_screen_media_test.dart index 55adf7d3..81c342eb 100644 --- a/packages/stream_chat_flutter/test/src/full_screen_media_test.dart +++ b/packages/stream_chat_flutter/test/src/full_screen_media_test.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:mockito/mockito.dart'; +import 'package:mocktail/mocktail.dart'; import 'package:photo_view/photo_view.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; @@ -16,51 +16,52 @@ void main() { final channelState = MockChannelState(); final lastMessageAt = DateTime.parse('2020-06-22 12:00:00'); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); - when(channel.lastMessageAt).thenReturn(lastMessageAt); - when(channel.state).thenReturn(channelState); - when(channel.client).thenReturn(client); - when(channel.isMuted).thenReturn(false); - when(channel.isMutedStream).thenAnswer((i) => Stream.value(false)); - when(channel.extraDataStream).thenAnswer((i) => Stream.value({ + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => channel.lastMessageAt).thenReturn(lastMessageAt); + when(() => channel.state).thenReturn(channelState); + when(() => channel.client).thenReturn(client); + when(() => channel.isMuted).thenReturn(false); + when(() => channel.isMutedStream).thenAnswer((i) => Stream.value(false)); + when(() => channel.extraDataStream).thenAnswer((i) => Stream.value({ 'name': 'test', })); - when(channel.extraData).thenReturn({ + when(() => channel.extraData).thenReturn({ 'name': 'test', }); - when(channelState.membersStream).thenAnswer((i) => Stream.value([ + when(() => channelState.membersStream).thenAnswer((i) => Stream.value([ Member( userId: 'user-id', user: User(id: 'user-id'), ) ])); - when(channelState.members).thenReturn([ + when(() => channelState.members).thenReturn([ Member( userId: 'user-id', user: User(id: 'user-id'), ), ]); - when(channelState.messages).thenReturn([ + when(() => channelState.messages).thenReturn([ Message( text: 'hello', user: User(id: 'other-user'), ) ]); - when(channelState.messagesStream).thenAnswer((i) => Stream.value([ + when(() => channelState.messagesStream).thenAnswer((i) => Stream.value([ Message( text: 'hello', user: User(id: 'other-user'), ) ])); - when(channelState.typingEvents).thenAnswer((i) => [ + when(() => channelState.typingEvents).thenAnswer((i) => [ User(id: 'other-user', extraData: {'name': 'demo'}) ]); - when(channelState.typingEventsStream).thenAnswer((i) => Stream.value([ - User(id: 'other-user', extraData: {'name': 'demo'}), - User(id: 'other-user', extraData: {'name': 'demo'}), - ])); + when(() => channelState.typingEventsStream) + .thenAnswer((i) => Stream.value([ + User(id: 'other-user', extraData: {'name': 'demo'}), + User(id: 'other-user', extraData: {'name': 'demo'}), + ])); await tester.pumpWidget(MaterialApp( home: StreamChat( @@ -78,7 +79,6 @@ void main() { message: Message( createdAt: DateTime.now(), ), - sentAt: DateTime.now(), ), ), ), diff --git a/packages/stream_chat_flutter/test/src/goldens/reaction_bubble_0.png b/packages/stream_chat_flutter/test/src/goldens/reaction_bubble_0.png index 12a654b9..18f898ad 100644 Binary files a/packages/stream_chat_flutter/test/src/goldens/reaction_bubble_0.png and b/packages/stream_chat_flutter/test/src/goldens/reaction_bubble_0.png differ diff --git a/packages/stream_chat_flutter/test/src/goldens/reaction_bubble_2.png b/packages/stream_chat_flutter/test/src/goldens/reaction_bubble_2.png index 48bb62da..ea03e3e6 100644 Binary files a/packages/stream_chat_flutter/test/src/goldens/reaction_bubble_2.png and b/packages/stream_chat_flutter/test/src/goldens/reaction_bubble_2.png differ diff --git a/packages/stream_chat_flutter/test/src/goldens/reaction_bubble_3_dark.png b/packages/stream_chat_flutter/test/src/goldens/reaction_bubble_3_dark.png index 2bb180b9..cb7e4894 100644 Binary files a/packages/stream_chat_flutter/test/src/goldens/reaction_bubble_3_dark.png and b/packages/stream_chat_flutter/test/src/goldens/reaction_bubble_3_dark.png differ diff --git a/packages/stream_chat_flutter/test/src/goldens/reaction_bubble_3_light.png b/packages/stream_chat_flutter/test/src/goldens/reaction_bubble_3_light.png index ade5d82b..8caefa4f 100644 Binary files a/packages/stream_chat_flutter/test/src/goldens/reaction_bubble_3_light.png and b/packages/stream_chat_flutter/test/src/goldens/reaction_bubble_3_light.png differ diff --git a/packages/stream_chat_flutter/test/src/goldens/reaction_bubble_like_dark.png b/packages/stream_chat_flutter/test/src/goldens/reaction_bubble_like_dark.png index 5a862031..84444215 100644 Binary files a/packages/stream_chat_flutter/test/src/goldens/reaction_bubble_like_dark.png and b/packages/stream_chat_flutter/test/src/goldens/reaction_bubble_like_dark.png differ diff --git a/packages/stream_chat_flutter/test/src/goldens/reaction_bubble_like_light.png b/packages/stream_chat_flutter/test/src/goldens/reaction_bubble_like_light.png index 47f78e30..b198aef5 100644 Binary files a/packages/stream_chat_flutter/test/src/goldens/reaction_bubble_like_light.png and b/packages/stream_chat_flutter/test/src/goldens/reaction_bubble_like_light.png differ diff --git a/packages/stream_chat_flutter/test/src/image_footer_test.dart b/packages/stream_chat_flutter/test/src/image_footer_test.dart index 2fe72aaa..e2809d11 100644 --- a/packages/stream_chat_flutter/test/src/image_footer_test.dart +++ b/packages/stream_chat_flutter/test/src/image_footer_test.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:mockito/mockito.dart'; +import 'package:mocktail/mocktail.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'mocks.dart'; @@ -15,17 +15,17 @@ void main() { final channelState = MockChannelState(); final lastMessageAt = DateTime.parse('2020-06-22 12:00:00'); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); - when(channel.lastMessageAt).thenReturn(lastMessageAt); - when(channel.state).thenReturn(channelState); - when(channel.client).thenReturn(client); - when(channel.isMuted).thenReturn(false); - when(channel.isMutedStream).thenAnswer((i) => Stream.value(false)); - when(channel.extraDataStream).thenAnswer((i) => Stream.value({ + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => channel.lastMessageAt).thenReturn(lastMessageAt); + when(() => channel.state).thenReturn(channelState); + when(() => channel.client).thenReturn(client); + when(() => channel.isMuted).thenReturn(false); + when(() => channel.isMutedStream).thenAnswer((i) => Stream.value(false)); + when(() => channel.extraDataStream).thenAnswer((i) => Stream.value({ 'name': 'test', })); - when(channel.extraData).thenReturn({ + when(() => channel.extraData).thenReturn({ 'name': 'test', }); @@ -37,7 +37,9 @@ void main() { child: WillPopScope( onWillPop: () async => false, child: Scaffold( - body: ImageFooter(), + body: ImageFooter( + message: Message(), + ), ), ), ), diff --git a/packages/stream_chat_flutter/test/src/info_tile_test.dart b/packages/stream_chat_flutter/test/src/info_tile_test.dart index d304d48e..091c64c7 100644 --- a/packages/stream_chat_flutter/test/src/info_tile_test.dart +++ b/packages/stream_chat_flutter/test/src/info_tile_test.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_portal/flutter_portal.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:mockito/mockito.dart'; +import 'package:mocktail/mocktail.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'mocks.dart'; @@ -13,8 +13,8 @@ void main() { final client = MockClient(); final clientState = MockClientState(); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); await tester.pumpWidget(MaterialApp( home: StreamChat( @@ -43,8 +43,8 @@ void main() { final client = MockClient(); final clientState = MockClientState(); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); await tester.pumpWidget(MaterialApp( home: StreamChat( diff --git a/packages/stream_chat_flutter/test/src/message_action_modal_test.dart b/packages/stream_chat_flutter/test/src/message_action_modal_test.dart index c744a6bb..0be509ae 100644 --- a/packages/stream_chat_flutter/test/src/message_action_modal_test.dart +++ b/packages/stream_chat_flutter/test/src/message_action_modal_test.dart @@ -1,23 +1,28 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:mockito/mockito.dart'; +import 'package:mocktail/mocktail.dart'; import 'package:stream_chat_flutter/src/message_actions_modal.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'mocks.dart'; void main() { + setUpAll(() { + registerFallbackValue(MaterialPageRoute(builder: (context) => SizedBox())); + registerFallbackValue(Message()); + }); + testWidgets( 'it should show the all actions', (WidgetTester tester) async { final client = MockClient(); final clientState = MockClientState(); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); final themeData = ThemeData(); - final streamTheme = StreamChatThemeData.getDefaultTheme(themeData); + final streamTheme = StreamChatThemeData.fromTheme(themeData); await tester.pumpWidget( MaterialApp( theme: themeData, @@ -55,11 +60,11 @@ void main() { final client = MockClient(); final clientState = MockClientState(); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); final themeData = ThemeData(); - final streamTheme = StreamChatThemeData.getDefaultTheme(themeData); + final streamTheme = StreamChatThemeData.fromTheme(themeData); await tester.pumpWidget( MaterialApp( theme: themeData, @@ -102,11 +107,11 @@ void main() { final client = MockClient(); final clientState = MockClientState(); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); final themeData = ThemeData(); - final streamTheme = StreamChatThemeData.getDefaultTheme(themeData); + final streamTheme = StreamChatThemeData.fromTheme(themeData); var tapped = false; await tester.pumpWidget( @@ -156,11 +161,11 @@ void main() { final client = MockClient(); final clientState = MockClientState(); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); final themeData = ThemeData(); - final streamTheme = StreamChatThemeData.getDefaultTheme(themeData); + final streamTheme = StreamChatThemeData.fromTheme(themeData); var tapped = false; @@ -173,7 +178,7 @@ void main() { child: Container( child: MessageActionsModal( onReplyTap: (m) { - return tapped = true; + tapped = true; }, message: Message( text: 'test', @@ -201,11 +206,11 @@ void main() { final client = MockClient(); final clientState = MockClientState(); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); final themeData = ThemeData(); - final streamTheme = StreamChatThemeData.getDefaultTheme(themeData); + final streamTheme = StreamChatThemeData.fromTheme(themeData); var tapped = false; @@ -218,7 +223,7 @@ void main() { child: Container( child: MessageActionsModal( onThreadReplyTap: (m) { - return tapped = true; + tapped = true; }, message: Message( text: 'test', @@ -247,12 +252,11 @@ void main() { final clientState = MockClientState(); final channel = MockChannel(); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); - when(channel.initialized).thenAnswer((_) => Future.value(true)); + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); final themeData = ThemeData(); - final streamTheme = StreamChatThemeData.getDefaultTheme(themeData); + final streamTheme = StreamChatThemeData.fromTheme(themeData); await tester.pumpWidget( MaterialApp( @@ -260,7 +264,7 @@ void main() { return StreamChat( client: client, streamChatThemeData: streamTheme, - child: child, + child: child!, ); }, theme: themeData, @@ -298,12 +302,11 @@ void main() { final clientState = MockClientState(); final channel = MockChannel(); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); - when(channel.initialized).thenAnswer((_) => Future.value(true)); + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); final themeData = ThemeData(); - final streamTheme = StreamChatThemeData.getDefaultTheme(themeData); + final streamTheme = StreamChatThemeData.fromTheme(themeData); await tester.pumpWidget( MaterialApp( @@ -311,7 +314,7 @@ void main() { return StreamChat( client: client, streamChatThemeData: streamTheme, - child: child, + child: child!, ); }, theme: themeData, @@ -352,12 +355,11 @@ void main() { final clientState = MockClientState(); final channel = MockChannel(); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); - when(channel.initialized).thenAnswer((_) => Future.value(true)); + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); final themeData = ThemeData(); - final streamTheme = StreamChatThemeData.getDefaultTheme(themeData); + final streamTheme = StreamChatThemeData.fromTheme(themeData); var tapped = false; @@ -367,7 +369,7 @@ void main() { return StreamChat( client: client, streamChatThemeData: streamTheme, - child: child, + child: child!, ); }, theme: themeData, @@ -404,12 +406,13 @@ void main() { final clientState = MockClientState(); final channel = MockChannel(); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); - when(channel.initialized).thenAnswer((_) => Future.value(true)); + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => channel.sendMessage(any())) + .thenAnswer((_) async => SendMessageResponse()); final themeData = ThemeData(); - final streamTheme = StreamChatThemeData.getDefaultTheme(themeData); + final streamTheme = StreamChatThemeData.fromTheme(themeData); await tester.pumpWidget( MaterialApp( @@ -417,7 +420,7 @@ void main() { return StreamChat( client: client, streamChatThemeData: streamTheme, - child: child, + child: child!, ); }, theme: themeData, @@ -443,7 +446,7 @@ void main() { await tester.tap(find.text('Resend')); - verify(channel.sendMessage(any)).called(1); + verify(() => channel.sendMessage(any())).called(1); }, ); @@ -454,12 +457,13 @@ void main() { final clientState = MockClientState(); final channel = MockChannel(); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); - when(channel.initialized).thenAnswer((_) => Future.value(true)); + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => channel.updateMessage(any())) + .thenAnswer((_) async => UpdateMessageResponse()); final themeData = ThemeData(); - final streamTheme = StreamChatThemeData.getDefaultTheme(themeData); + final streamTheme = StreamChatThemeData.fromTheme(themeData); await tester.pumpWidget( MaterialApp( @@ -467,7 +471,7 @@ void main() { return StreamChat( client: client, streamChatThemeData: streamTheme, - child: child, + child: child!, ); }, theme: themeData, @@ -493,7 +497,7 @@ void main() { await tester.tap(find.text('Resend Edited Message')); - verify(channel.updateMessage(any)).called(1); + verify(() => channel.updateMessage(any())).called(1); }, ); @@ -504,12 +508,11 @@ void main() { final clientState = MockClientState(); final channel = MockChannel(); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); - when(channel.initialized).thenAnswer((_) => Future.value(true)); + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); final themeData = ThemeData(); - final streamTheme = StreamChatThemeData.getDefaultTheme(themeData); + final streamTheme = StreamChatThemeData.fromTheme(themeData); await tester.pumpWidget( MaterialApp( @@ -517,7 +520,7 @@ void main() { return StreamChat( client: client, streamChatThemeData: streamTheme, - child: child, + child: child!, ); }, theme: themeData, @@ -549,7 +552,7 @@ void main() { await tester.tap(find.text('FLAG')); await tester.pumpAndSettle(); - verify(client.flagMessage('testid')).called(1); + verify(() => client.flagMessage('testid')).called(1); }, ); @@ -560,16 +563,15 @@ void main() { final clientState = MockClientState(); final channel = MockChannel(); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); - when(channel.initialized).thenAnswer((_) => Future.value(true)); - when(client.flagMessage(any)).thenThrow(ApiError( + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => client.flagMessage(any())).thenThrow(ApiError( '{}', 500, )); final themeData = ThemeData(); - final streamTheme = StreamChatThemeData.getDefaultTheme(themeData); + final streamTheme = StreamChatThemeData.fromTheme(themeData); await tester.pumpWidget( MaterialApp( @@ -577,7 +579,7 @@ void main() { return StreamChat( client: client, streamChatThemeData: streamTheme, - child: child, + child: child!, ); }, theme: themeData, @@ -620,16 +622,15 @@ void main() { final clientState = MockClientState(); final channel = MockChannel(); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); - when(channel.initialized).thenAnswer((_) => Future.value(true)); - when(client.flagMessage(any)).thenThrow(ApiError( + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => client.flagMessage(any())).thenThrow(ApiError( '{"code":4}', 400, )); final themeData = ThemeData(); - final streamTheme = StreamChatThemeData.getDefaultTheme(themeData); + final streamTheme = StreamChatThemeData.fromTheme(themeData); await tester.pumpWidget( MaterialApp( @@ -637,7 +638,7 @@ void main() { return StreamChat( client: client, streamChatThemeData: streamTheme, - child: child, + child: child!, ); }, theme: themeData, @@ -680,12 +681,11 @@ void main() { final clientState = MockClientState(); final channel = MockChannel(); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); - when(channel.initialized).thenAnswer((_) => Future.value(true)); + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); final themeData = ThemeData(); - final streamTheme = StreamChatThemeData.getDefaultTheme(themeData); + final streamTheme = StreamChatThemeData.fromTheme(themeData); await tester.pumpWidget( MaterialApp( @@ -693,7 +693,7 @@ void main() { return StreamChat( client: client, streamChatThemeData: streamTheme, - child: child, + child: child!, ); }, theme: themeData, @@ -725,7 +725,7 @@ void main() { await tester.tap(find.text('DELETE')); await tester.pumpAndSettle(); - verify(channel.deleteMessage(any)).called(1); + verify(() => channel.deleteMessage(any())).called(1); }, ); @@ -736,16 +736,15 @@ void main() { final clientState = MockClientState(); final channel = MockChannel(); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); - when(channel.initialized).thenAnswer((_) => Future.value(true)); - when(channel.deleteMessage(any)).thenThrow(ApiError( + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => channel.deleteMessage(any())).thenThrow(ApiError( '{}', 500, )); final themeData = ThemeData(); - final streamTheme = StreamChatThemeData.getDefaultTheme(themeData); + final streamTheme = StreamChatThemeData.fromTheme(themeData); await tester.pumpWidget( MaterialApp( @@ -753,7 +752,7 @@ void main() { return StreamChat( client: client, streamChatThemeData: streamTheme, - child: child, + child: child!, ); }, theme: themeData, diff --git a/packages/stream_chat_flutter/test/src/message_input_test.dart b/packages/stream_chat_flutter/test/src/message_input_test.dart index 38e80565..39e7ae3f 100644 --- a/packages/stream_chat_flutter/test/src/message_input_test.dart +++ b/packages/stream_chat_flutter/test/src/message_input_test.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:mockito/mockito.dart'; +import 'package:mocktail/mocktail.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'mocks.dart'; @@ -15,51 +15,52 @@ void main() { final channelState = MockChannelState(); final lastMessageAt = DateTime.parse('2020-06-22 12:00:00'); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); - when(channel.lastMessageAt).thenReturn(lastMessageAt); - when(channel.state).thenReturn(channelState); - when(channel.client).thenReturn(client); - when(channel.isMuted).thenReturn(false); - when(channel.isMutedStream).thenAnswer((i) => Stream.value(false)); - when(channel.extraDataStream).thenAnswer((i) => Stream.value({ + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => channel.lastMessageAt).thenReturn(lastMessageAt); + when(() => channel.state).thenReturn(channelState); + when(() => channel.client).thenReturn(client); + when(() => channel.isMuted).thenReturn(false); + when(() => channel.isMutedStream).thenAnswer((i) => Stream.value(false)); + when(() => channel.extraDataStream).thenAnswer((i) => Stream.value({ 'name': 'test', })); - when(channel.extraData).thenReturn({ + when(() => channel.extraData).thenReturn({ 'name': 'test', }); - when(channelState.membersStream).thenAnswer((i) => Stream.value([ + when(() => channelState.membersStream).thenAnswer((i) => Stream.value([ Member( userId: 'user-id', user: User(id: 'user-id'), ) ])); - when(channelState.members).thenReturn([ + when(() => channelState.members).thenReturn([ Member( userId: 'user-id', user: User(id: 'user-id'), ), ]); - when(channelState.messages).thenReturn([ + when(() => channelState.messages).thenReturn([ Message( text: 'hello', user: User(id: 'other-user'), ) ]); - when(channelState.messagesStream).thenAnswer((i) => Stream.value([ + when(() => channelState.messagesStream).thenAnswer((i) => Stream.value([ Message( text: 'hello', user: User(id: 'other-user'), ) ])); - when(channelState.typingEvents).thenAnswer((i) => [ + when(() => channelState.typingEvents).thenAnswer((i) => [ User(id: 'other-user', extraData: {'name': 'demo'}) ]); - when(channelState.typingEventsStream).thenAnswer((i) => Stream.value([ - User(id: 'other-user', extraData: {'name': 'demo'}), - User(id: 'other-user', extraData: {'name': 'demo'}), - ])); + when(() => channelState.typingEventsStream) + .thenAnswer((i) => Stream.value([ + User(id: 'other-user', extraData: {'name': 'demo'}), + User(id: 'other-user', extraData: {'name': 'demo'}), + ])); await tester.pumpWidget(MaterialApp( home: StreamChat( diff --git a/packages/stream_chat_flutter/test/src/message_reactions_modal_test.dart b/packages/stream_chat_flutter/test/src/message_reactions_modal_test.dart index 044eb693..e1136ac5 100644 --- a/packages/stream_chat_flutter/test/src/message_reactions_modal_test.dart +++ b/packages/stream_chat_flutter/test/src/message_reactions_modal_test.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:mockito/mockito.dart'; +import 'package:mocktail/mocktail.dart'; import 'package:stream_chat_flutter/src/message_reactions_modal.dart'; import 'package:stream_chat_flutter/src/reaction_bubble.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; @@ -15,10 +15,10 @@ void main() { final clientState = MockClientState(); final themeData = ThemeData(); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); - final streamTheme = StreamChatThemeData.getDefaultTheme(themeData); + final streamTheme = StreamChatThemeData.fromTheme(themeData); final message = Message( id: 'test', @@ -75,10 +75,10 @@ void main() { final clientState = MockClientState(); final themeData = ThemeData(); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); - final streamTheme = StreamChatThemeData.getDefaultTheme(themeData); + final streamTheme = StreamChatThemeData.fromTheme(themeData); final message = Message( id: 'test', @@ -90,6 +90,7 @@ void main() { Reaction( messageId: 'test', user: User(id: 'testid'), + type: 'test', ), ], ); diff --git a/packages/stream_chat_flutter/test/src/message_text_test.dart b/packages/stream_chat_flutter/test/src/message_text_test.dart index 1d2ac8be..a8aba86a 100644 --- a/packages/stream_chat_flutter/test/src/message_text_test.dart +++ b/packages/stream_chat_flutter/test/src/message_text_test.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_markdown/flutter_markdown.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:mockito/mockito.dart'; +import 'package:mocktail/mocktail.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'mocks.dart'; @@ -16,19 +16,19 @@ void main() { final channelState = MockChannelState(); final lastMessageAt = DateTime.parse('2020-06-22 12:00:00'); final themeData = ThemeData(); - final streamTheme = StreamChatThemeData.getDefaultTheme(themeData); + final streamTheme = StreamChatThemeData.fromTheme(themeData); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); - when(channel.lastMessageAt).thenReturn(lastMessageAt); - when(channel.state).thenReturn(channelState); - when(channel.client).thenReturn(client); - when(channel.isMuted).thenReturn(false); - when(channel.isMutedStream).thenAnswer((i) => Stream.value(false)); - when(channel.extraDataStream).thenAnswer((i) => Stream.value({ + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => channel.lastMessageAt).thenReturn(lastMessageAt); + when(() => channel.state).thenReturn(channelState); + when(() => channel.client).thenReturn(client); + when(() => channel.isMuted).thenReturn(false); + when(() => channel.isMutedStream).thenAnswer((i) => Stream.value(false)); + when(() => channel.extraDataStream).thenAnswer((i) => Stream.value({ 'name': 'test', })); - when(channel.extraData).thenReturn({ + when(() => channel.extraData).thenReturn({ 'name': 'test', }); diff --git a/packages/stream_chat_flutter/test/src/mocks.dart b/packages/stream_chat_flutter/test/src/mocks.dart index 50f7ee6a..842c1c2f 100644 --- a/packages/stream_chat_flutter/test/src/mocks.dart +++ b/packages/stream_chat_flutter/test/src/mocks.dart @@ -1,12 +1,20 @@ import 'package:flutter/material.dart'; -import 'package:mockito/mockito.dart'; +import 'package:mocktail/mocktail.dart'; import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; class MockClient extends Mock implements StreamChatClient {} class MockClientState extends Mock implements ClientState {} -class MockChannel extends Mock implements Channel {} +class MockChannel extends Mock implements Channel { + @override + Future get initialized async => true; + + @override + Future keyStroke([String? parentId]) async { + return; + } +} class MockChannelState extends Mock implements ChannelClientState {} diff --git a/packages/stream_chat_flutter/test/src/reaction_bubble_test.dart b/packages/stream_chat_flutter/test/src/reaction_bubble_test.dart index d4a3b6a6..fd5d31ce 100644 --- a/packages/stream_chat_flutter/test/src/reaction_bubble_test.dart +++ b/packages/stream_chat_flutter/test/src/reaction_bubble_test.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:golden_toolkit/golden_toolkit.dart'; -import 'package:mockito/mockito.dart'; +import 'package:mocktail/mocktail.dart'; import 'package:stream_chat_flutter/src/reaction_bubble.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; @@ -39,10 +39,10 @@ void main() { final clientState = MockClientState(); final themeData = ThemeData.light(); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); - final theme = StreamChatThemeData.getDefaultTheme(themeData); + final theme = StreamChatThemeData.fromTheme(themeData); await tester.pumpWidgetBuilder( StreamChat( client: client, @@ -55,9 +55,9 @@ void main() { user: User(id: 'test'), ), ], - borderColor: theme.ownMessageTheme.reactionsBorderColor, - backgroundColor: theme.ownMessageTheme.reactionsBackgroundColor, - maskColor: theme.ownMessageTheme.reactionsMaskColor, + borderColor: theme.ownMessageTheme.reactionsBorderColor!, + backgroundColor: theme.ownMessageTheme.reactionsBackgroundColor!, + maskColor: theme.ownMessageTheme.reactionsMaskColor!, ), ), ), @@ -73,15 +73,15 @@ void main() { final client = MockClient(); final clientState = MockClientState(); final themeData = ThemeData.dark(); - final theme = StreamChatThemeData.getDefaultTheme(themeData); + final theme = StreamChatThemeData.fromTheme(themeData); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); await tester.pumpWidgetBuilder( StreamChat( client: client, - streamChatThemeData: StreamChatThemeData.getDefaultTheme(themeData), + streamChatThemeData: StreamChatThemeData.fromTheme(themeData), child: Container( color: Colors.black, child: ReactionBubble( @@ -91,9 +91,9 @@ void main() { user: User(id: 'test'), ), ], - borderColor: theme.ownMessageTheme.reactionsBorderColor, - backgroundColor: theme.ownMessageTheme.reactionsBackgroundColor, - maskColor: theme.ownMessageTheme.reactionsMaskColor, + borderColor: theme.ownMessageTheme.reactionsBorderColor!, + backgroundColor: theme.ownMessageTheme.reactionsBackgroundColor!, + maskColor: theme.ownMessageTheme.reactionsMaskColor!, ), ), ), @@ -109,15 +109,15 @@ void main() { final client = MockClient(); final clientState = MockClientState(); final themeData = ThemeData.light(); - final theme = StreamChatThemeData.getDefaultTheme(themeData); + final theme = StreamChatThemeData.fromTheme(themeData); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); await tester.pumpWidgetBuilder( StreamChat( client: client, - streamChatThemeData: StreamChatThemeData.getDefaultTheme(themeData), + streamChatThemeData: StreamChatThemeData.fromTheme(themeData), child: Container( color: Colors.black, child: ReactionBubble( @@ -135,9 +135,9 @@ void main() { user: User(id: 'test'), ), ], - borderColor: theme.ownMessageTheme.reactionsBorderColor, - backgroundColor: theme.ownMessageTheme.reactionsBackgroundColor, - maskColor: theme.ownMessageTheme.reactionsMaskColor, + borderColor: theme.ownMessageTheme.reactionsBorderColor!, + backgroundColor: theme.ownMessageTheme.reactionsBackgroundColor!, + maskColor: theme.ownMessageTheme.reactionsMaskColor!, ), ), ), @@ -153,15 +153,15 @@ void main() { final client = MockClient(); final clientState = MockClientState(); final themeData = ThemeData.dark(); - final theme = StreamChatThemeData.getDefaultTheme(themeData); + final theme = StreamChatThemeData.fromTheme(themeData); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); await tester.pumpWidgetBuilder( StreamChat( client: client, - streamChatThemeData: StreamChatThemeData.getDefaultTheme(themeData), + streamChatThemeData: StreamChatThemeData.fromTheme(themeData), child: Container( color: Colors.black, child: ReactionBubble( @@ -179,9 +179,9 @@ void main() { user: User(id: 'test'), ), ], - borderColor: theme.ownMessageTheme.reactionsBorderColor, - backgroundColor: theme.ownMessageTheme.reactionsBackgroundColor, - maskColor: theme.ownMessageTheme.reactionsMaskColor, + borderColor: theme.ownMessageTheme.reactionsBorderColor!, + backgroundColor: theme.ownMessageTheme.reactionsBackgroundColor!, + maskColor: theme.ownMessageTheme.reactionsMaskColor!, ), ), ), @@ -198,13 +198,13 @@ void main() { final clientState = MockClientState(); final themeData = ThemeData(); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); await tester.pumpWidgetBuilder( StreamChat( client: client, - streamChatThemeData: StreamChatThemeData.getDefaultTheme(themeData), + streamChatThemeData: StreamChatThemeData.fromTheme(themeData), child: Container( child: ReactionBubble( reactions: [ diff --git a/packages/stream_chat_flutter/test/src/simple_frame.dart b/packages/stream_chat_flutter/test/src/simple_frame.dart index c472df85..18d7ae55 100644 --- a/packages/stream_chat_flutter/test/src/simple_frame.dart +++ b/packages/stream_chat_flutter/test/src/simple_frame.dart @@ -3,7 +3,7 @@ import 'package:flutter/material.dart'; class SimpleFrame extends StatelessWidget { final Widget child; - const SimpleFrame({Key key, @required this.child}) : super(key: key); + const SimpleFrame({Key? key, required this.child}) : super(key: key); @override Widget build(BuildContext context) { diff --git a/packages/stream_chat_flutter/test/src/system_message_test.dart b/packages/stream_chat_flutter/test/src/system_message_test.dart index 04967c39..53741864 100644 --- a/packages/stream_chat_flutter/test/src/system_message_test.dart +++ b/packages/stream_chat_flutter/test/src/system_message_test.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:golden_toolkit/golden_toolkit.dart'; -import 'package:mockito/mockito.dart'; +import 'package:mocktail/mocktail.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'mocks.dart'; @@ -16,20 +16,20 @@ void main() { final channelState = MockChannelState(); final lastMessageAt = DateTime.parse('2020-06-22 12:00:00'); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); - when(channel.lastMessageAt).thenReturn(lastMessageAt); - when(channel.state).thenReturn(channelState); - when(channel.client).thenReturn(client); - when(channel.extraDataStream).thenAnswer((i) => Stream.value({ + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => channel.lastMessageAt).thenReturn(lastMessageAt); + when(() => channel.state).thenReturn(channelState); + when(() => channel.client).thenReturn(client); + when(() => channel.extraDataStream).thenAnswer((i) => Stream.value({ 'name': 'test', })); - when(channel.extraData).thenReturn({ + when(() => channel.extraData).thenReturn({ 'name': 'test', }); - when(clientState.totalUnreadCount).thenReturn(10); - when(clientState.totalUnreadCountStream) + when(() => clientState.totalUnreadCount).thenReturn(10); + when(() => clientState.totalUnreadCountStream) .thenAnswer((i) => Stream.value(10)); var tapped = false; @@ -67,21 +67,20 @@ void main() { final channelState = MockChannelState(); final lastMessageAt = DateTime.parse('2020-06-22 12:00:00'); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); - when(channel.lastMessageAt).thenReturn(lastMessageAt); - when(channel.state).thenReturn(channelState); - when(channel.client).thenReturn(client); - when(channel.initialized).thenAnswer((_) => Future.value(true)); - when(channel.extraDataStream).thenAnswer((i) => Stream.value({ + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => channel.lastMessageAt).thenReturn(lastMessageAt); + when(() => channel.state).thenReturn(channelState); + when(() => channel.client).thenReturn(client); + when(() => channel.extraDataStream).thenAnswer((i) => Stream.value({ 'name': 'test', })); - when(channel.extraData).thenReturn({ + when(() => channel.extraData).thenReturn({ 'name': 'test', }); - when(clientState.totalUnreadCount).thenReturn(10); - when(clientState.totalUnreadCountStream) + when(() => clientState.totalUnreadCount).thenReturn(10); + when(() => clientState.totalUnreadCountStream) .thenAnswer((i) => Stream.value(10)); await tester.pumpWidgetBuilder( @@ -119,21 +118,20 @@ void main() { final channelState = MockChannelState(); final lastMessageAt = DateTime.parse('2020-06-22 12:00:00'); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); - when(channel.lastMessageAt).thenReturn(lastMessageAt); - when(channel.state).thenReturn(channelState); - when(channel.client).thenReturn(client); - when(channel.initialized).thenAnswer((_) => Future.value(true)); - when(channel.extraDataStream).thenAnswer((i) => Stream.value({ + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => channel.lastMessageAt).thenReturn(lastMessageAt); + when(() => channel.state).thenReturn(channelState); + when(() => channel.client).thenReturn(client); + when(() => channel.extraDataStream).thenAnswer((i) => Stream.value({ 'name': 'test', })); - when(channel.extraData).thenReturn({ + when(() => channel.extraData).thenReturn({ 'name': 'test', }); - when(clientState.totalUnreadCount).thenReturn(10); - when(clientState.totalUnreadCountStream) + when(() => clientState.totalUnreadCount).thenReturn(10); + when(() => clientState.totalUnreadCountStream) .thenAnswer((i) => Stream.value(10)); await tester.pumpWidgetBuilder( diff --git a/packages/stream_chat_flutter/test/src/thread_header_test.dart b/packages/stream_chat_flutter/test/src/thread_header_test.dart index 25987e31..9e64abb7 100644 --- a/packages/stream_chat_flutter/test/src/thread_header_test.dart +++ b/packages/stream_chat_flutter/test/src/thread_header_test.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:mockito/mockito.dart'; +import 'package:mocktail/mocktail.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'mocks.dart'; @@ -15,34 +15,38 @@ void main() { final channelState = MockChannelState(); final lastMessageAt = DateTime.parse('2020-06-22 12:00:00'); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); - when(channel.lastMessageAt).thenReturn(lastMessageAt); - when(channel.state).thenReturn(channelState); - when(channel.client).thenReturn(client); - when(channel.initialized).thenAnswer((_) => Future.value(true)); - when(channel.isMuted).thenReturn(false); - when(channel.isMutedStream).thenAnswer((i) => Stream.value(false)); - when(channel.extraDataStream).thenAnswer((i) => Stream.value({ + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => channel.lastMessageAt).thenReturn(lastMessageAt); + when(() => channel.state).thenReturn(channelState); + when(() => channel.client).thenReturn(client); + when(() => channel.isMuted).thenReturn(false); + when(() => channel.isMutedStream).thenAnswer((i) => Stream.value(false)); + when(() => channel.extraDataStream).thenAnswer((i) => Stream.value({ 'name': 'test', })); - when(channel.extraData).thenReturn({ + when(() => channel.extraData).thenReturn({ 'name': 'test', }); - when(channelState.unreadCount).thenReturn(1); - when(channelState.unreadCountStream).thenAnswer((i) => Stream.value(1)); - when(channelState.membersStream).thenAnswer((i) => Stream.value([ + when(() => channelState.unreadCount).thenReturn(1); + when(() => channelState.unreadCountStream) + .thenAnswer((i) => Stream.value(1)); + when(() => channelState.membersStream).thenAnswer((i) => Stream.value([ Member( userId: 'user-id', user: User(id: 'user-id'), ) ])); - when(channelState.members).thenReturn([ + when(() => channelState.members).thenReturn([ Member( userId: 'user-id', user: User(id: 'user-id'), ), ]); + when(() => client.wsConnectionStatusStream) + .thenAnswer((_) => Stream.value(ConnectionStatus.connecting)); + when(() => clientState.totalUnreadCountStream) + .thenAnswer((i) => Stream.value(1)); await tester.pumpWidget(MaterialApp( home: StreamChat( @@ -75,29 +79,29 @@ void main() { final channelState = MockChannelState(); final lastMessageAt = DateTime.parse('2020-06-22 12:00:00'); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); - when(channel.lastMessageAt).thenReturn(lastMessageAt); - when(channel.state).thenReturn(channelState); - when(channel.client).thenReturn(client); - when(channel.initialized).thenAnswer((_) => Future.value(true)); - when(channel.isMuted).thenReturn(false); - when(channel.isMutedStream).thenAnswer((i) => Stream.value(false)); - when(channel.extraDataStream).thenAnswer((i) => Stream.value({ + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => channel.lastMessageAt).thenReturn(lastMessageAt); + when(() => channel.state).thenReturn(channelState); + when(() => channel.client).thenReturn(client); + when(() => channel.isMuted).thenReturn(false); + when(() => channel.isMutedStream).thenAnswer((i) => Stream.value(false)); + when(() => channel.extraDataStream).thenAnswer((i) => Stream.value({ 'name': 'test', })); - when(channel.extraData).thenReturn({ + when(() => channel.extraData).thenReturn({ 'name': 'test', }); - when(channelState.unreadCount).thenReturn(1); - when(channelState.unreadCountStream).thenAnswer((i) => Stream.value(1)); - when(channelState.membersStream).thenAnswer((i) => Stream.value([ + when(() => channelState.unreadCount).thenReturn(1); + when(() => channelState.unreadCountStream) + .thenAnswer((i) => Stream.value(1)); + when(() => channelState.membersStream).thenAnswer((i) => Stream.value([ Member( userId: 'user-id', user: User(id: 'user-id'), ) ])); - when(channelState.members).thenReturn([ + when(() => channelState.members).thenReturn([ Member( userId: 'user-id', user: User(id: 'user-id'), diff --git a/packages/stream_chat_flutter/test/src/typing_indicator_test.dart b/packages/stream_chat_flutter/test/src/typing_indicator_test.dart index bbdbeb3e..8698de91 100644 --- a/packages/stream_chat_flutter/test/src/typing_indicator_test.dart +++ b/packages/stream_chat_flutter/test/src/typing_indicator_test.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:mockito/mockito.dart'; +import 'package:mocktail/mocktail.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'mocks.dart'; @@ -15,51 +15,52 @@ void main() { final channelState = MockChannelState(); final lastMessageAt = DateTime.parse('2020-06-22 12:00:00'); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); - when(channel.lastMessageAt).thenReturn(lastMessageAt); - when(channel.state).thenReturn(channelState); - when(channel.client).thenReturn(client); - when(channel.isMuted).thenReturn(false); - when(channel.isMutedStream).thenAnswer((i) => Stream.value(false)); - when(channel.extraDataStream).thenAnswer((i) => Stream.value({ + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => channel.lastMessageAt).thenReturn(lastMessageAt); + when(() => channel.state).thenReturn(channelState); + when(() => channel.client).thenReturn(client); + when(() => channel.isMuted).thenReturn(false); + when(() => channel.isMutedStream).thenAnswer((i) => Stream.value(false)); + when(() => channel.extraDataStream).thenAnswer((i) => Stream.value({ 'name': 'test', })); - when(channel.extraData).thenReturn({ + when(() => channel.extraData).thenReturn({ 'name': 'test', }); - when(channelState.membersStream).thenAnswer((i) => Stream.value([ + when(() => channelState.membersStream).thenAnswer((i) => Stream.value([ Member( userId: 'user-id', user: User(id: 'user-id'), ) ])); - when(channelState.members).thenReturn([ + when(() => channelState.members).thenReturn([ Member( userId: 'user-id', user: User(id: 'user-id'), ), ]); - when(channelState.messages).thenReturn([ + when(() => channelState.messages).thenReturn([ Message( text: 'hello', user: User(id: 'other-user'), ) ]); - when(channelState.messagesStream).thenAnswer((i) => Stream.value([ + when(() => channelState.messagesStream).thenAnswer((i) => Stream.value([ Message( text: 'hello', user: User(id: 'other-user'), ) ])); - when(channelState.typingEvents).thenAnswer((i) => [ + when(() => channelState.typingEvents).thenAnswer((i) => [ User(id: 'other-user', extraData: {'name': 'demo'}) ]); - when(channelState.typingEventsStream).thenAnswer((i) => Stream.value([ - User(id: 'other-user', extraData: {'name': 'demo'}), - User(id: 'other-user', extraData: {'name': 'demo'}), - ])); + when(() => channelState.typingEventsStream) + .thenAnswer((i) => Stream.value([ + User(id: 'other-user', extraData: {'name': 'demo'}), + User(id: 'other-user', extraData: {'name': 'demo'}), + ])); await tester.pumpWidget(MaterialApp( home: StreamChat( diff --git a/packages/stream_chat_flutter/test/src/unread_indicator_test.dart b/packages/stream_chat_flutter/test/src/unread_indicator_test.dart index 87018263..9b1b5e1e 100644 --- a/packages/stream_chat_flutter/test/src/unread_indicator_test.dart +++ b/packages/stream_chat_flutter/test/src/unread_indicator_test.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:mockito/mockito.dart'; +import 'package:mocktail/mocktail.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'mocks.dart'; @@ -15,20 +15,20 @@ void main() { final channelState = MockChannelState(); final lastMessageAt = DateTime.parse('2020-06-22 12:00:00'); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); - when(channel.lastMessageAt).thenReturn(lastMessageAt); - when(channel.state).thenReturn(channelState); - when(channel.client).thenReturn(client); - when(channel.extraDataStream).thenAnswer((i) => Stream.value({ + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => channel.lastMessageAt).thenReturn(lastMessageAt); + when(() => channel.state).thenReturn(channelState); + when(() => channel.client).thenReturn(client); + when(() => channel.extraDataStream).thenAnswer((i) => Stream.value({ 'name': 'test', })); - when(channel.extraData).thenReturn({ + when(() => channel.extraData).thenReturn({ 'name': 'test', }); - when(clientState.totalUnreadCount).thenReturn(10); - when(clientState.totalUnreadCountStream) + when(() => clientState.totalUnreadCount).thenReturn(10); + when(() => clientState.totalUnreadCountStream) .thenAnswer((i) => Stream.value(10)); await tester.pumpWidget(MaterialApp( @@ -54,19 +54,20 @@ void main() { final clientState = MockClientState(); final channel = MockChannel(); final channelState = MockChannelState(); - when(channel.cid).thenReturn('cid'); + when(() => channel.cid).thenReturn('cid'); final lastMessageAt = DateTime.parse('2020-06-22 12:00:00'); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); - when(clientState.channels).thenReturn({ - channel.cid: channel, + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => clientState.channels).thenReturn({ + channel.cid!: channel, }); - when(channel.lastMessageAt).thenReturn(lastMessageAt); - when(channel.state).thenReturn(channelState); - when(channel.client).thenReturn(client); - when(channelState.unreadCount).thenReturn(0); - when(channelState.unreadCountStream).thenAnswer((i) => Stream.value(0)); + when(() => channel.lastMessageAt).thenReturn(lastMessageAt); + when(() => channel.state).thenReturn(channelState); + when(() => channel.client).thenReturn(client); + when(() => channelState.unreadCount).thenReturn(0); + when(() => channelState.unreadCountStream) + .thenAnswer((i) => Stream.value(0)); await tester.pumpWidget(MaterialApp( home: StreamChat( @@ -92,20 +93,21 @@ void main() { final client = MockClient(); final clientState = MockClientState(); final channel = MockChannel(); - when(channel.cid).thenReturn('cid'); + when(() => channel.cid).thenReturn('cid'); final channelState = MockChannelState(); final lastMessageAt = DateTime.parse('2020-06-22 12:00:00'); - when(client.state).thenReturn(clientState); - when(clientState.user).thenReturn(OwnUser(id: 'user-id')); - when(clientState.channels).thenReturn({ - channel.cid: channel, + when(() => client.state).thenReturn(clientState); + when(() => clientState.user).thenReturn(OwnUser(id: 'user-id')); + when(() => clientState.channels).thenReturn({ + channel.cid!: channel, }); - when(channel.lastMessageAt).thenReturn(lastMessageAt); - when(channel.state).thenReturn(channelState); - when(channel.client).thenReturn(client); - when(channelState.unreadCount).thenReturn(100); - when(channelState.unreadCountStream).thenAnswer((i) => Stream.value(100)); + when(() => channel.lastMessageAt).thenReturn(lastMessageAt); + when(() => channel.state).thenReturn(channelState); + when(() => channel.client).thenReturn(client); + when(() => channelState.unreadCount).thenReturn(100); + when(() => channelState.unreadCountStream) + .thenAnswer((i) => Stream.value(100)); await tester.pumpWidget(MaterialApp( home: StreamChat( diff --git a/packages/stream_chat_flutter_core/CHANGELOG.md b/packages/stream_chat_flutter_core/CHANGELOG.md index 68d01d52..d23fd7d9 100644 --- a/packages/stream_chat_flutter_core/CHANGELOG.md +++ b/packages/stream_chat_flutter_core/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.0.0-nullsafety.0 + +* Migrate this package to null safety +* Update llc dependency + ## 1.5.2 * Update llc dependency diff --git a/packages/stream_chat_flutter_core/analysis_options.yaml b/packages/stream_chat_flutter_core/analysis_options.yaml index 545d5492..cf09a170 100644 --- a/packages/stream_chat_flutter_core/analysis_options.yaml +++ b/packages/stream_chat_flutter_core/analysis_options.yaml @@ -4,6 +4,7 @@ analyzer: - lib/**/*.freezed.dart - example/* - test/* + linter: rules: - always_use_package_imports diff --git a/packages/stream_chat_flutter_core/example/lib/main.dart b/packages/stream_chat_flutter_core/example/lib/main.dart index aa705db1..e7ef400a 100644 --- a/packages/stream_chat_flutter_core/example/lib/main.dart +++ b/packages/stream_chat_flutter_core/example/lib/main.dart @@ -38,8 +38,8 @@ class StreamExample extends StatelessWidget { /// If you'd prefer using pre-made UI widgets for your app, please see our other /// package, `stream_chat_flutter`. const StreamExample({ - Key key, - @required this.client, + Key? key, + required this.client, }) : super(key: key); /// Instance of Stream Client. @@ -55,7 +55,7 @@ class StreamExample extends StatelessWidget { home: HomeScreen(), builder: (context, child) => StreamChatCore( client: client, - child: child, + child: child!, ), ); } @@ -78,14 +78,12 @@ class HomeScreen extends StatelessWidget { body: ChannelsBloc( child: ChannelListCore( channelListController: channelListController, - filter: { - 'type': 'messaging', - 'members': { - r'$in': [ - StreamChatCore.of(context).user.id, - ] - } - }, + filter: Filter.and([ + Filter.equal('type', 'messaging'), + Filter.in_('members', [ + StreamChatCore.of(context).user!.id, + ]) + ]), emptyBuilder: (BuildContext context) { return Center( child: Text('Looks like you are not in any channels'), @@ -100,10 +98,13 @@ class HomeScreen extends StatelessWidget { ), ); }, - errorBuilder: (BuildContext context, dynamic error) { + errorBuilder: ( + BuildContext context, + dynamic error, + ) { return Center( child: Text( - 'Oh no, something went wrong. Please check your config.'), + 'Oh no, something went wrong. Please check your config. ${error}'), ); }, listBuilder: ( @@ -112,20 +113,20 @@ class HomeScreen extends StatelessWidget { ) => LazyLoadScrollView( onEndOfPage: () async { - channelListController.paginateData(); + channelListController.paginateData!(); }, child: ListView.builder( itemCount: channels.length, itemBuilder: (BuildContext context, int index) { final _item = channels[index]; return ListTile( - title: Text(_item.name), - subtitle: StreamBuilder( - stream: _item.state.lastMessageStream, - initialData: _item.state.lastMessage, + title: Text(_item.name!), + subtitle: StreamBuilder( + stream: _item.state!.lastMessageStream, + initialData: _item.state!.lastMessage, builder: (context, snapshot) { if (snapshot.hasData) { - return Text(snapshot.data.text); + return Text(snapshot.data!.text!); } return SizedBox(); @@ -169,8 +170,8 @@ class MessageScreen extends StatefulWidget { } class _MessageScreenState extends State { - TextEditingController _controller; - ScrollController _scrollController; + late final TextEditingController _controller; + late final ScrollController _scrollController; final messageListController = MessageListController(); @override @@ -203,11 +204,11 @@ class _MessageScreenState extends State { return Scaffold( appBar: AppBar( title: StreamBuilder>( - initialData: channel.state.typingEvents, - stream: channel.state.typingEventsStream, + initialData: channel.state?.typingEvents, + stream: channel.state?.typingEventsStream, builder: (context, snapshot) { - if (snapshot.hasData && snapshot.data.isNotEmpty) { - return Text('${snapshot.data.first.name} is typing...'); + if (snapshot.hasData && snapshot.data!.isNotEmpty) { + return Text('${snapshot.data!.first.name} is typing...'); } return SizedBox(); }, @@ -219,9 +220,10 @@ class _MessageScreenState extends State { Expanded( child: LazyLoadScrollView( onEndOfPage: () async { - messageListController.paginateData(); + messageListController.paginateData!(); }, child: MessageListCore( + messageListController: messageListController, emptyBuilder: (BuildContext context) { return Center( child: Text('Nothing here yet'), @@ -247,12 +249,12 @@ class _MessageScreenState extends State { itemBuilder: (BuildContext context, int index) { final item = messages[index]; final client = StreamChatCore.of(context).client; - if (item.user.id == client.uid) { + if (item.user!.id == client.uid) { return Align( alignment: Alignment.centerRight, child: Padding( padding: const EdgeInsets.all(8.0), - child: Text(item.text), + child: Text(item.text!), ), ); } else { @@ -260,7 +262,7 @@ class _MessageScreenState extends State { alignment: Alignment.centerLeft, child: Padding( padding: const EdgeInsets.all(8.0), - child: Text(item.text), + child: Text(item.text!), ), ); } @@ -268,7 +270,7 @@ class _MessageScreenState extends State { ); }, errorWidgetBuilder: (BuildContext context, error) { - print(error?.toString()); + print(error.toString()); return Center( child: SizedBox( height: 100.0, @@ -282,7 +284,7 @@ class _MessageScreenState extends State { ), ), Padding( - padding: const EdgeInsets.all(8.0), + padding: const EdgeInsets.all(8), child: Row( children: [ Expanded( @@ -303,12 +305,14 @@ class _MessageScreenState extends State { await channel.sendMessage( Message(text: _controller.value.text), ); - _controller.clear(); - _updateList(); + if (mounted) { + _controller.clear(); + _updateList(); + } } }, child: const Padding( - padding: EdgeInsets.all(8.0), + padding: EdgeInsets.all(8), child: Center( child: Icon( Icons.send, @@ -332,12 +336,12 @@ class _MessageScreenState extends State { /// below, we add two simple extensions to the [StreamChatClient] and [Channel]. extension on StreamChatClient { /// Fetches the current user id. - String get uid => state.user.id; + String get uid => state.user!.id; } extension on Channel { /// Fetches the name of the channel by accessing [extraData] or [cid]. - String get name { + String? get name { final _channelName = extraData['name']; if (_channelName != null) { return _channelName; diff --git a/packages/stream_chat_flutter_core/example/pubspec.yaml b/packages/stream_chat_flutter_core/example/pubspec.yaml index b0071176..889ad518 100644 --- a/packages/stream_chat_flutter_core/example/pubspec.yaml +++ b/packages/stream_chat_flutter_core/example/pubspec.yaml @@ -18,7 +18,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev version: 1.0.0+1 environment: - sdk: ">=2.7.0 <3.0.0" + sdk: '>=2.12.0 <3.0.0' dependencies: flutter: diff --git a/packages/stream_chat_flutter_core/lib/src/channel_list_core.dart b/packages/stream_chat_flutter_core/lib/src/channel_list_core.dart index 3adea312..8ead2637 100644 --- a/packages/stream_chat_flutter_core/lib/src/channel_list_core.dart +++ b/packages/stream_chat_flutter_core/lib/src/channel_list_core.dart @@ -57,11 +57,11 @@ import 'package:stream_chat_flutter_core/src/typedef.dart'; class ChannelListCore extends StatefulWidget { /// Instantiate a new ChannelListView const ChannelListCore({ - Key key, - @required this.errorBuilder, - @required this.emptyBuilder, - @required this.loadingBuilder, - @required this.listBuilder, + Key? key, + required this.errorBuilder, + required this.emptyBuilder, + required this.loadingBuilder, + required this.listBuilder, this.filter, this.options, this.sort, @@ -69,29 +69,13 @@ class ChannelListCore extends StatefulWidget { limit: 25, ), this.channelListController, - }) : assert( - errorBuilder != null, - 'Parameter errorBuilder should not be null', - ), - assert( - emptyBuilder != null, - 'Parameter emptyBuilder should not be null', - ), - assert( - loadingBuilder != null, - 'Parameter loadingBuilder should not be null', - ), - assert( - listBuilder != null, - 'Parameter listBuilder should not be null', - ), - super(key: key); + }) : super(key: key); /// A [ChannelListController] allows reloading and pagination. /// Use [ChannelListController.loadData] and /// [ChannelListController.paginateData] respectively for reloading and /// pagination. - final ChannelListController channelListController; + final ChannelListController? channelListController; /// The builder that will be used in case of error final ErrorBuilder errorBuilder; @@ -108,20 +92,20 @@ class ChannelListCore extends StatefulWidget { /// The query filters to use. /// You can query on any of the custom fields you've defined on the [Channel]. /// You can also filter other built-in channel fields. - final Map filter; + final Filter? filter; /// Query channels options. /// /// state: if true returns the Channel state /// watch: if true listen to changes to this Channel in real time. - final Map options; + final Map? options; /// The sorting used for the channels matching the filters. /// Sorting is based on field and direction, multiple sorting options can be /// provided. /// You can sort based on last_updated, last_message_at, updated_at, created /// _at or member_count. Direction can be ascending or descending. - final List> sort; + final List>? sort; /// Pagination parameters /// limit: the number of channels to return (max is 30) @@ -149,12 +133,12 @@ class ChannelListCoreState extends State { stream: channelsBlocState.channelsStream, builder: (context, snapshot) { if (snapshot.hasError) { - return widget.errorBuilder(context, snapshot.error); + return widget.errorBuilder(context, snapshot.error!); } if (!snapshot.hasData) { return widget.loadingBuilder(context); } - final channels = snapshot.data; + final channels = snapshot.data!; if (channels.isEmpty) { return widget.emptyBuilder(context); } @@ -186,7 +170,7 @@ class ChannelListCoreState extends State { ); } - StreamSubscription _subscription; + late StreamSubscription _subscription; @override void initState() { @@ -203,8 +187,8 @@ class ChannelListCoreState extends State { .listen((event) => loadData()); if (widget.channelListController != null) { - widget.channelListController.loadData = loadData; - widget.channelListController.paginateData = paginateData; + widget.channelListController!.loadData = loadData; + widget.channelListController!.paginateData = paginateData; } } @@ -215,8 +199,8 @@ class ChannelListCoreState extends State { if (widget.filter?.toString() != oldWidget.filter?.toString() || jsonEncode(widget.sort) != jsonEncode(oldWidget.sort) || widget.options?.toString() != oldWidget.options?.toString() || - widget.pagination?.toJson()?.toString() != - oldWidget.pagination?.toJson()?.toString()) { + widget.pagination.toJson().toString() != + oldWidget.pagination.toJson().toString()) { loadData(); } } @@ -233,10 +217,10 @@ class ChannelListCoreState extends State { class ChannelListController { /// This function calls Stream's servers to load a list of channels. /// If there is existing data, calling this function causes a reload. - AsyncCallback loadData; + AsyncCallback? loadData; /// This function is used to load another page of data. Note, [loadData] /// should be used to populate the initial page of data. Calling /// [paginateData] performs a query to load subsequent pages. - AsyncCallback paginateData; + AsyncCallback? paginateData; } diff --git a/packages/stream_chat_flutter_core/lib/src/channels_bloc.dart b/packages/stream_chat_flutter_core/lib/src/channels_bloc.dart index 2b4968eb..32e63bdc 100644 --- a/packages/stream_chat_flutter_core/lib/src/channels_bloc.dart +++ b/packages/stream_chat_flutter_core/lib/src/channels_bloc.dart @@ -20,13 +20,12 @@ class ChannelsBloc extends StatefulWidget { /// Creates a new [ChannelsBloc]. The parameter [child] must be supplied and /// not null. const ChannelsBloc({ - Key key, - @required this.child, + Key? key, + required this.child, this.lockChannelsOrder = false, this.channelsComparator, this.shouldAddChannel, - }) : assert(child != null, 'Parameter child should not be null.'), - super(key: key); + }) : super(key: key); /// The widget child final Widget child; @@ -36,18 +35,18 @@ class ChannelsBloc extends StatefulWidget { final bool lockChannelsOrder; /// Comparator used to sort the channels when a message.new event is received - final Comparator channelsComparator; + final Comparator? channelsComparator; /// Function used to evaluate if a channel should be added to the list when a /// message.new event is received - final bool Function(Event) shouldAddChannel; + final bool Function(Event)? shouldAddChannel; @override ChannelsBlocState createState() => ChannelsBlocState(); /// Use this method to get the current [ChannelsBlocState] instance static ChannelsBlocState of(BuildContext context) { - ChannelsBlocState streamChatState; + ChannelsBlocState? streamChatState; streamChatState = context.findAncestorStateOfType(); @@ -69,14 +68,15 @@ class ChannelsBlocState extends State } /// The current channel list - List get channels => _channelsController.value; + List? get channels => _channelsController.value; /// The current channel list as a stream Stream> get channelsStream => _channelsController.stream; final _queryChannelsLoadingController = BehaviorSubject.seeded(false); - final _channelsController = BehaviorSubject>(); + final BehaviorSubject> _channelsController = + BehaviorSubject>(); /// The stream notifying the state of queryChannel call Stream get queryChannelsLoading => @@ -88,10 +88,10 @@ class ChannelsBlocState extends State /// Calls [client.queryChannels] updating [queryChannelsLoading] stream Future queryChannels({ - Map filter, - List> sortOptions, - PaginationParams paginationParams, - Map options, + Filter? filter, + List>? sortOptions, + PaginationParams paginationParams = const PaginationParams(limit: 30), + Map? options, }) async { final client = StreamChatCore.of(context).client; @@ -104,9 +104,7 @@ class ChannelsBlocState extends State } try { - final clear = paginationParams == null || - paginationParams.offset == null || - paginationParams.offset == 0; + final clear = paginationParams.offset == 0; final oldChannels = List.from(channels ?? []); var newChannels = []; await for (final channels in client.queryChannels( @@ -123,7 +121,7 @@ class ChannelsBlocState extends State _channelsController.add(temp); } if (_channelsController.hasValue && - _queryChannelsLoadingController.value) { + _queryChannelsLoadingController.value!) { _queryChannelsLoadingController.sink.add(false); } } @@ -162,9 +160,8 @@ class ChannelsBlocState extends State newChannels.insert(0, _hiddenChannels[hiddenIndex]); _hiddenChannels.removeAt(hiddenIndex); } else { - if (client.state?.channels != null && - client.state?.channels[e.cid] != null) { - newChannels.insert(0, client.state.channels[e.cid]); + if (client.state.channels[e.cid] != null) { + newChannels.insert(0, client.state.channels[e.cid]!); } } } @@ -195,7 +192,7 @@ class ChannelsBlocState extends State // ignore: cascade_invocations final channel = e.channel; _channelsController.add(List.from( - (channels ?? [])..removeWhere((c) => c.cid == channel.cid))); + (channels ?? [])..removeWhere((c) => c.cid == channel?.cid))); })); } diff --git a/packages/stream_chat_flutter_core/lib/src/lazy_load_scroll_view.dart b/packages/stream_chat_flutter_core/lib/src/lazy_load_scroll_view.dart index 808c1dae..834d00b0 100644 --- a/packages/stream_chat_flutter_core/lib/src/lazy_load_scroll_view.dart +++ b/packages/stream_chat_flutter_core/lib/src/lazy_load_scroll_view.dart @@ -9,34 +9,33 @@ class LazyLoadScrollView extends StatefulWidget { /// Creates a new instance of [LazyLoadScrollView]. The parameter [child] /// must be supplied and not null. const LazyLoadScrollView({ - Key key, - @required this.child, + Key? key, + required this.child, this.onStartOfPage, this.onEndOfPage, this.onPageScrollStart, this.onPageScrollEnd, this.onInBetweenOfPage, this.scrollOffset = 100, - }) : assert(child != null, 'Parameter child should not be null'), - super(key: key); + }) : super(key: key); /// The [Widget] that this widget watches for changes on final Widget child; /// Called when the [child] reaches the start of the list - final AsyncCallback onStartOfPage; + final AsyncCallback? onStartOfPage; /// Called when the [child] reaches the end of the list - final AsyncCallback onEndOfPage; + final AsyncCallback? onEndOfPage; /// Called when the list scrolling starts - final VoidCallback onPageScrollStart; + final VoidCallback? onPageScrollStart; /// Called when the list scrolling ends - final VoidCallback onPageScrollEnd; + final VoidCallback? onPageScrollEnd; /// Called every time the [child] is in-between the list - final VoidCallback onInBetweenOfPage; + final VoidCallback? onInBetweenOfPage; /// The offset to take into account when triggering [onEndOfPage]/[onStartOfPage] in pixels final double scrollOffset; @@ -46,7 +45,7 @@ class LazyLoadScrollView extends StatefulWidget { } class _LazyLoadScrollViewState extends State { - _LoadingStatus _loadMoreStatus = _LoadingStatus.stable; + var _loadMoreStatus = _LoadingStatus.stable; double _scrollPosition = 0; @override @@ -59,13 +58,13 @@ class _LazyLoadScrollViewState extends State { bool _onNotification(ScrollNotification notification) { if (notification is ScrollStartNotification) { if (widget.onPageScrollStart != null) { - widget.onPageScrollStart(); + widget.onPageScrollStart!(); return true; } } if (notification is ScrollEndNotification) { if (widget.onPageScrollEnd != null) { - widget.onPageScrollEnd(); + widget.onPageScrollEnd!(); return true; } } @@ -73,12 +72,12 @@ class _LazyLoadScrollViewState extends State { final pixels = notification.metrics.pixels; final maxScrollExtent = notification.metrics.maxScrollExtent; final minScrollExtent = notification.metrics.minScrollExtent; - final scrollOffset = widget.scrollOffset ?? 0; + final scrollOffset = widget.scrollOffset; if (pixels > (minScrollExtent + scrollOffset) && pixels < (maxScrollExtent - scrollOffset)) { if (widget.onInBetweenOfPage != null) { - widget.onInBetweenOfPage(); + widget.onInBetweenOfPage!(); return true; } } @@ -114,10 +113,10 @@ class _LazyLoadScrollViewState extends State { } void _onEndOfPage() { - if (_loadMoreStatus != null && _loadMoreStatus == _LoadingStatus.stable) { + if (_loadMoreStatus == _LoadingStatus.stable) { if (widget.onEndOfPage != null) { _loadMoreStatus = _LoadingStatus.loading; - widget.onEndOfPage().whenComplete(() { + widget.onEndOfPage!().whenComplete(() { _loadMoreStatus = _LoadingStatus.stable; }); } @@ -125,10 +124,10 @@ class _LazyLoadScrollViewState extends State { } void _onStartOfPage() { - if (_loadMoreStatus != null && _loadMoreStatus == _LoadingStatus.stable) { + if (_loadMoreStatus == _LoadingStatus.stable) { if (widget.onStartOfPage != null) { _loadMoreStatus = _LoadingStatus.loading; - widget.onStartOfPage().whenComplete(() { + widget.onStartOfPage!().whenComplete(() { _loadMoreStatus = _LoadingStatus.stable; }); } diff --git a/packages/stream_chat_flutter_core/lib/src/message_list_core.dart b/packages/stream_chat_flutter_core/lib/src/message_list_core.dart index 53844447..e1fed859 100644 --- a/packages/stream_chat_flutter_core/lib/src/message_list_core.dart +++ b/packages/stream_chat_flutter_core/lib/src/message_list_core.dart @@ -61,30 +61,20 @@ import 'package:stream_chat_flutter_core/src/typedef.dart'; class MessageListCore extends StatefulWidget { /// Instantiate a new [MessageListView]. const MessageListCore({ - Key key, - @required this.loadingBuilder, - @required this.emptyBuilder, - @required this.messageListBuilder, - @required this.errorWidgetBuilder, + Key? key, + required this.loadingBuilder, + required this.emptyBuilder, + required this.messageListBuilder, + required this.errorWidgetBuilder, this.showScrollToBottom = true, this.parentMessage, this.messageListController, this.messageFilter, - }) : assert(loadingBuilder != null, 'loadingBuilder should not be null'), - assert(emptyBuilder != null, 'emptyBuilder should not be null'), - assert( - messageListBuilder != null, - 'messageListBuilder should not be null', - ), - assert( - errorWidgetBuilder != null, - 'errorWidgetBuilder should not be null', - ), - super(key: key); + }) : super(key: key); /// A [MessageListController] allows pagination. /// Use [ChannelListController.paginateData] pagination. - final MessageListController messageListController; + final MessageListController? messageListController; /// Function called when messages are fetched final Widget Function(BuildContext, List) messageListBuilder; @@ -108,10 +98,10 @@ class MessageListCore extends StatefulWidget { /// If the current message belongs to a `thread`, this property represents the /// first message or the parent of the conversation. - final Message parentMessage; + final Message? parentMessage; /// Predicate used to filter messages - final bool Function(Message) messageFilter; + final bool Function(Message)? messageFilter; @override MessageListCoreState createState() => MessageListCoreState(); @@ -119,41 +109,44 @@ class MessageListCore extends StatefulWidget { /// The current state of the [MessageListCore]. class MessageListCoreState extends State { - StreamChannelState _streamChannel; + late StreamChannelState _streamChannel; - bool get _upToDate => _streamChannel.channel.state.isUpToDate; + bool get _upToDate => _streamChannel.channel.state?.isUpToDate ?? true; bool get _isThreadConversation => widget.parentMessage != null; - OwnUser get _currentUser => _streamChannel.channel.client.state.user; + OwnUser? get _currentUser => _streamChannel.channel.client.state.user; var _messages = []; @override Widget build(BuildContext context) { final messagesStream = _isThreadConversation - ? _streamChannel.channel.state.threadsStream - .where((threads) => threads.containsKey(widget.parentMessage.id)) - .map((threads) => threads[widget.parentMessage.id]) + ? _streamChannel.channel.state?.threadsStream + .where((threads) => threads.containsKey(widget.parentMessage!.id)) + .map((threads) => threads[widget.parentMessage!.id]) : _streamChannel.channel.state?.messagesStream; bool defaultFilter(Message m) { - final isMyMessage = m.user.id == _currentUser.id; + final isMyMessage = m.user?.id == _currentUser?.id; final isDeletedOrShadowed = m.isDeleted == true || m.shadowed == true; if (isDeletedOrShadowed && !isMyMessage) return false; return true; } - return StreamBuilder>( + return StreamBuilder?>( stream: messagesStream?.map((messages) => - messages?.where(widget.messageFilter ?? defaultFilter)?.toList()), + messages?.where(widget.messageFilter ?? defaultFilter).toList( + growable: false, + )), builder: (context, snapshot) { if (snapshot.hasError) { - return widget.errorWidgetBuilder(context, snapshot.error); + return widget.errorWidgetBuilder(context, snapshot.error!); } else if (!snapshot.hasData) { return widget.loadingBuilder(context); } else { - final messageList = snapshot.data?.reversed?.toList() ?? []; + final messageList = + snapshot.data?.reversed.toList(growable: false) ?? []; if (messageList.isEmpty && !_isThreadConversation) { if (_upToDate) { return widget.emptyBuilder(context); @@ -169,13 +162,14 @@ class MessageListCoreState extends State { /// Fetches more messages with updated pagination and updates the widget. /// - /// Optionally pass the fetch direction, defaults to [QueryDirection.bottom] - Future paginateData( - {QueryDirection direction = QueryDirection.bottom}) { + /// Optionally pass the fetch direction, defaults to [QueryDirection.top] + Future paginateData({ + QueryDirection direction = QueryDirection.top, + }) { if (!_isThreadConversation) { return _streamChannel.queryMessages(direction: direction); } else { - return _streamChannel.getReplies(widget.parentMessage.id); + return _streamChannel.getReplies(widget.parentMessage!.id); } } @@ -184,11 +178,11 @@ class MessageListCoreState extends State { _streamChannel = StreamChannel.of(context); if (_isThreadConversation) { - _streamChannel.getReplies(widget.parentMessage.id); + _streamChannel.getReplies(widget.parentMessage!.id); } if (widget.messageListController != null) { - widget.messageListController.paginateData = paginateData; + widget.messageListController!.paginateData = paginateData; } super.initState(); @@ -206,5 +200,5 @@ class MessageListCoreState extends State { /// Controller used for paginating data in [ChannelListView] class MessageListController { /// Call this function to load further data - Future Function({QueryDirection direction}) paginateData; + Future Function({QueryDirection direction})? paginateData; } diff --git a/packages/stream_chat_flutter_core/lib/src/message_search_bloc.dart b/packages/stream_chat_flutter_core/lib/src/message_search_bloc.dart index 1c5c78de..6c9be4f0 100644 --- a/packages/stream_chat_flutter_core/lib/src/message_search_bloc.dart +++ b/packages/stream_chat_flutter_core/lib/src/message_search_bloc.dart @@ -9,14 +9,13 @@ import 'package:stream_chat_flutter_core/src/stream_chat_core.dart'; /// [MessageSearchBloc] can be access at anytime by using the static [of] method /// using Flutter's [BuildContext]. /// -// API docs: https://getstream.io/chat/docs/flutter-dart/send_message/ +/// API docs: https://getstream.io/chat/docs/flutter-dart/send_message/ class MessageSearchBloc extends StatefulWidget { /// Instantiate a new MessageSearchBloc const MessageSearchBloc({ - Key key, - @required this.child, - }) : assert(child != null, 'Parameter child should not be null.'), - super(key: key); + Key? key, + required this.child, + }) : super(key: key); /// The widget child final Widget child; @@ -26,7 +25,7 @@ class MessageSearchBloc extends StatefulWidget { /// Use this method to get the current [MessageSearchBlocState] instance static MessageSearchBlocState of(BuildContext context) { - MessageSearchBlocState state; + MessageSearchBlocState? state; state = context.findAncestorStateOfType(); @@ -42,7 +41,7 @@ class MessageSearchBloc extends StatefulWidget { class MessageSearchBlocState extends State with AutomaticKeepAliveClientMixin { /// The current messages list - List get messageResponses => _messageResponses.value; + List? get messageResponses => _messageResponses.value; /// The current messages list as a stream Stream> get messagesStream => @@ -59,11 +58,11 @@ class MessageSearchBlocState extends State /// Calls [StreamChatClient.search] updating /// [messagesStream] and [queryMessagesLoading] stream Future search({ - Map filter, - Map messageFilter, - List sort, - String query, - PaginationParams pagination, + required Filter filter, + Filter? messageFilter, + List? sort, + String? query, + PaginationParams? pagination, }) async { final client = StreamChatCore.of(context).client; @@ -73,9 +72,7 @@ class MessageSearchBlocState extends State _queryMessagesLoadingController.add(true); } try { - final clear = pagination == null || - pagination.offset == null || - pagination.offset == 0; + final clear = pagination == null || pagination.offset == 0; final oldMessages = List.from(messageResponses ?? []); @@ -93,7 +90,8 @@ class MessageSearchBlocState extends State final temp = oldMessages + messages.results; _messageResponses.add(temp); } - if (_messageResponses.hasValue && _queryMessagesLoadingController.value) { + if (_messageResponses.hasValue && + _queryMessagesLoadingController.value!) { _queryMessagesLoadingController.add(false); } } catch (e, stk) { diff --git a/packages/stream_chat_flutter_core/lib/src/message_search_list_core.dart b/packages/stream_chat_flutter_core/lib/src/message_search_list_core.dart index 72c5ea86..cb8c9387 100644 --- a/packages/stream_chat_flutter_core/lib/src/message_search_list_core.dart +++ b/packages/stream_chat_flutter_core/lib/src/message_search_list_core.dart @@ -42,54 +42,50 @@ class MessageSearchListCore extends StatefulWidget { /// * [loadingBuilder] /// * [childBuilder] const MessageSearchListCore({ - Key key, - @required this.emptyBuilder, - @required this.errorBuilder, - @required this.loadingBuilder, - @required this.childBuilder, + Key? key, + required this.emptyBuilder, + required this.errorBuilder, + required this.loadingBuilder, + required this.childBuilder, + required this.filters, this.messageQuery, - this.filters, this.sortOptions, this.paginationParams, this.messageFilters, this.messageSearchListController, - }) : assert(emptyBuilder != null, 'emptyBuilder should not be null'), - assert(errorBuilder != null, 'errorBuilder should not be null'), - assert(loadingBuilder != null, 'loadingBuilder should not be null'), - assert(childBuilder != null, 'childBuilder should not be null'), - super(key: key); + }) : super(key: key); /// A [MessageSearchListController] allows reloading and pagination. /// Use [MessageSearchListController.loadData] and /// [MessageSearchListController.paginateData] respectively for reloading and /// pagination. - final MessageSearchListController messageSearchListController; + final MessageSearchListController? messageSearchListController; /// Message String to search on - final String messageQuery; + final String? messageQuery; /// The query filters to use. /// You can query on any of the custom fields you've defined on the [Channel]. /// You can also filter other built-in channel fields. - final Map filters; + final Filter filters; /// The sorting used for the channels matching the filters. /// Sorting is based on field and direction, multiple sorting options can be /// provided. /// You can sort based on last_updated, last_message_at, updated_at, created_ /// at or member_count. Direction can be ascending or descending. - final List sortOptions; + final List? sortOptions; /// Pagination parameters /// limit: the number of users to return (max is 30) /// offset: the offset (max is 1000) /// message_limit: how many messages should be included to each channel - final PaginationParams paginationParams; + final PaginationParams? paginationParams; /// The message query filters to use. /// You can query on any of the custom fields you've defined on the [Channel]. /// You can also filter other built-in channel fields. - final Map messageFilters; + final Filter? messageFilters; /// The builder that is used when the search messages are fetched final Widget Function(List) childBuilder; @@ -114,8 +110,8 @@ class MessageSearchListCoreState extends State { super.didChangeDependencies(); loadData(); if (widget.messageSearchListController != null) { - widget.messageSearchListController.loadData = loadData; - widget.messageSearchListController.paginateData = paginateData; + widget.messageSearchListController!.loadData = loadData; + widget.messageSearchListController!.paginateData = paginateData; } } @@ -130,16 +126,16 @@ class MessageSearchListCoreState extends State { stream: messageSearchBloc.messagesStream, builder: (context, snapshot) { if (snapshot.hasError) { - return widget.errorBuilder(context, snapshot.error); + return widget.errorBuilder(context, snapshot.error!); } if (!snapshot.hasData) { return widget.loadingBuilder(context); } - final items = snapshot.data; + final items = snapshot.data!; if (items.isEmpty) { return widget.emptyBuilder(context); } - return widget.childBuilder(snapshot.data); + return widget.childBuilder(items); }, ); @@ -161,7 +157,7 @@ class MessageSearchListCoreState extends State { return messageSearchBloc.search( filter: widget.filters, sort: widget.sortOptions, - pagination: widget.paginationParams.copyWith( + pagination: widget.paginationParams!.copyWith( offset: messageSearchBloc.messageResponses?.length ?? 0, ), query: widget.messageQuery, @@ -172,13 +168,13 @@ class MessageSearchListCoreState extends State { @override void didUpdateWidget(MessageSearchListCore oldWidget) { super.didUpdateWidget(oldWidget); - if (widget.filters?.toString() != oldWidget.filters?.toString() || + if (widget.filters.toString() != oldWidget.filters.toString() || jsonEncode(widget.sortOptions) != jsonEncode(oldWidget.sortOptions) || widget.messageQuery?.toString() != oldWidget.messageQuery?.toString() || widget.messageFilters?.toString() != oldWidget.messageFilters?.toString() || - widget.paginationParams?.toJson()?.toString() != - oldWidget.paginationParams?.toJson()?.toString()) { + widget.paginationParams?.toJson().toString() != + oldWidget.paginationParams?.toJson().toString()) { loadData(); } } @@ -187,8 +183,8 @@ class MessageSearchListCoreState extends State { /// Controller used for paginating data in [ChannelListView] class MessageSearchListController { /// Call this function to reload data - AsyncCallback loadData; + AsyncCallback? loadData; /// Call this function to load further data - AsyncCallback paginateData; + AsyncCallback? paginateData; } diff --git a/packages/stream_chat_flutter_core/lib/src/stream_channel.dart b/packages/stream_chat_flutter_core/lib/src/stream_channel.dart index 8f86fca4..34f58faa 100644 --- a/packages/stream_chat_flutter_core/lib/src/stream_channel.dart +++ b/packages/stream_chat_flutter_core/lib/src/stream_channel.dart @@ -1,5 +1,6 @@ import 'dart:async'; +import 'package:collection/collection.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:rxdart/rxdart.dart'; @@ -21,16 +22,14 @@ class StreamChannel extends StatefulWidget { /// Creates a new instance of [StreamChannel]. Both [child] and [client] must /// be supplied and not null. const StreamChannel({ - Key key, - @required this.child, - @required this.channel, + Key? key, + required this.child, + required this.channel, this.showLoading = true, this.initialMessageId, - }) : assert(child != null, 'Child should not be null'), - assert(channel != null, 'Channel should not be null'), - super(key: key); + }) : super(key: key); - // ignore: public_member_api_docs + /// The child of the widget final Widget child; /// [channel] specifies the channel with which child should be wrapped @@ -40,11 +39,11 @@ class StreamChannel extends StatefulWidget { final bool showLoading; /// If passed the channel will load from this particular message. - final String initialMessageId; + final String? initialMessageId; /// Use this method to get the current [StreamChannelState] instance static StreamChannelState of(BuildContext context) { - StreamChannelState streamChannelState; + StreamChannelState? streamChannelState; streamChannelState = context.findAncestorStateOfType(); @@ -67,11 +66,11 @@ class StreamChannelState extends State { Channel get channel => widget.channel; /// InitialMessageId - String get initialMessageId => widget.initialMessageId; + String? get initialMessageId => widget.initialMessageId; /// Current channel state stream - Stream get channelStateStream => - widget.channel.state.channelStateStream; + Stream? get channelStateStream => + widget.channel.state?.channelStateStream; final _queryTopMessagesController = BehaviorSubject.seeded(false); final _queryBottomMessagesController = BehaviorSubject.seeded(false); @@ -89,16 +88,18 @@ class StreamChannelState extends State { int limit = 20, bool preferOffline = false, }) async { - if (_topPaginationEnded || _queryTopMessagesController?.value == true) { + if (_topPaginationEnded || + _queryTopMessagesController.value == true || + channel.state == null) { return; } _queryTopMessagesController.add(true); - if (channel.state.messages.isEmpty) { + if (channel.state!.messages.isEmpty) { return _queryTopMessagesController.add(false); } - final oldestMessage = channel.state.messages.first; + final oldestMessage = channel.state!.messages.first; try { final state = await queryBeforeMessage( @@ -120,15 +121,16 @@ class StreamChannelState extends State { bool preferOffline = false, }) async { if (_bottomPaginationEnded || - _queryBottomMessagesController?.value == true || - channel?.state?.isUpToDate == true) return; + _queryBottomMessagesController.value == true || + channel.state == null || + channel.state!.isUpToDate == true) return; _queryBottomMessagesController.add(true); - if (channel.state.messages.isEmpty) { + if (channel.state!.messages.isEmpty) { return _queryBottomMessagesController.add(false); } - final recentMessage = channel.state.messages.last; + final recentMessage = channel.state!.messages.last; try { final state = await queryAfterMessage( @@ -146,7 +148,7 @@ class StreamChannelState extends State { } /// Calls [channel.query] updating [queryMessage] stream - Future queryMessages({QueryDirection direction = QueryDirection.top}) { + Future queryMessages({QueryDirection? direction = QueryDirection.top}) { if (direction == QueryDirection.top) return _queryTopMessages(); return _queryBottomMessages(); } @@ -157,12 +159,14 @@ class StreamChannelState extends State { int limit = 50, bool preferOffline = false, }) async { - if (_topPaginationEnded || _queryTopMessagesController.value) return; + if (_topPaginationEnded || + _queryTopMessagesController.value! || + channel.state == null) return; _queryTopMessagesController.add(true); - Message message; - if (channel.state.threads.containsKey(parentId)) { - final thread = channel.state.threads[parentId]; + late Message message; + if (channel.state!.threads.containsKey(parentId)) { + final thread = channel.state!.threads[parentId]!; if (thread.isNotEmpty) { message = thread.first; } @@ -172,7 +176,7 @@ class StreamChannelState extends State { final response = await channel.getReplies( parentId, PaginationParams( - lessThan: message?.id, + lessThan: message.id, limit: limit, ), preferOffline: preferOffline, @@ -188,21 +192,26 @@ class StreamChannelState extends State { /// Query the channel members and watchers Future queryMembersAndWatchers() async { - await widget.channel.query( - membersPagination: PaginationParams( - offset: channel.state.members?.length, - limit: 100, - ), - watchersPagination: PaginationParams( - offset: channel.state.watchers?.length, - limit: 100, - ), - ); + final _members = channel.state?.members; + if (_members != null) { + await widget.channel.query( + membersPagination: PaginationParams( + offset: _members.length, + limit: 100, + ), + watchersPagination: PaginationParams( + offset: _members.length, + limit: 100, + ), + ); + } else { + return; + } } /// Loads channel at specific message Future loadChannelAtMessage( - String messageId, { + String? messageId, { int before = 20, int after = 20, bool preferOffline = false, @@ -214,15 +223,15 @@ class StreamChannelState extends State { preferOffline: preferOffline, ); - Future _queryAtMessage({ - String messageId, + Future> _queryAtMessage({ + String? messageId, int before = 20, int after = 20, bool preferOffline = false, }) async { - if (channel.state == null) return; - channel.state.isUpToDate = false; - channel.state.truncate(); + if (channel.state == null) return []; + channel.state!.isUpToDate = false; + channel.state!.truncate(); if (messageId == null) { await channel.query( @@ -231,8 +240,8 @@ class StreamChannelState extends State { ), preferOffline: preferOffline, ); - channel.state.isUpToDate = true; - return; + channel.state!.isUpToDate = true; + return []; } return Future.wait([ @@ -277,16 +286,15 @@ class StreamChannelState extends State { preferOffline: preferOffline, ); if (state.messages.isEmpty || state.messages.length < limit) { - channel.state.isUpToDate = true; + channel.state?.isUpToDate = true; } return state; } /// Future getMessage(String messageId) async { - var message = channel.state.messages.firstWhere( + var message = channel.state?.messages.firstWhereOrNull( (it) => it.id == messageId, - orElse: () => null, ); if (message == null) { final response = await channel.getMessagesById([messageId]); @@ -298,7 +306,7 @@ class StreamChannelState extends State { /// Reloads the channel with latest message Future reloadChannel() => _queryAtMessage(before: 30); - List> _futures; + late List> _futures; Future get _loadChannelAtMessage async { try { @@ -350,7 +358,7 @@ class StreamChannelState extends State { var message = snapshot.error.toString(); if (snapshot.error is DioError) { final dioError = snapshot.error as DioError; - if (dioError.type == DioErrorType.RESPONSE) { + if (dioError.type == DioErrorType.response) { message = dioError.message; } else { message = 'Check your connection and retry'; @@ -358,9 +366,9 @@ class StreamChannelState extends State { } return Center(child: Text(message)); } - final initialized = snapshot.data[0]; + final initialized = snapshot.data![0]; // ignore: avoid_bool_literals_in_conditional_expressions - final dataLoaded = initialMessageId == null ? true : snapshot.data[1]; + final dataLoaded = initialMessageId == null ? true : snapshot.data![1]; if (widget.showLoading && (!initialized || !dataLoaded)) { return const Center( child: CircularProgressIndicator(), diff --git a/packages/stream_chat_flutter_core/lib/src/stream_chat_core.dart b/packages/stream_chat_flutter_core/lib/src/stream_chat_core.dart index 15c2ddf3..a72425ed 100644 --- a/packages/stream_chat_flutter_core/lib/src/stream_chat_core.dart +++ b/packages/stream_chat_flutter_core/lib/src/stream_chat_core.dart @@ -38,14 +38,12 @@ class StreamChatCore extends StatefulWidget { /// [StreamChatCore] is a stateful widget which reacts to system events and /// updates Stream's connection status accordingly. const StreamChatCore({ - Key key, - @required this.client, - @required this.child, + Key? key, + required this.client, + required this.child, this.onBackgroundEventReceived, this.backgroundKeepAlive = const Duration(minutes: 1), - }) : assert(client != null, 'Stream Chat Client should not be null'), - assert(child != null, 'Child should not be null'), - super(key: key); + }) : super(key: key); /// Instance of Stream Chat Client containing information about the current /// application. @@ -61,14 +59,14 @@ class StreamChatCore extends StatefulWidget { /// Handler called whenever the [client] receives a new [Event] while the app /// is in background. Can be used to display various notifications depending /// upon the [Event.type] - final EventHandler onBackgroundEventReceived; + final EventHandler? onBackgroundEventReceived; @override StreamChatCoreState createState() => StreamChatCoreState(); /// Use this method to get the current [StreamChatCoreState] instance static StreamChatCoreState of(BuildContext context) { - StreamChatCoreState streamChatState; + StreamChatCoreState? streamChatState; streamChatState = context.findAncestorStateOfType(); @@ -87,24 +85,24 @@ class StreamChatCoreState extends State /// Initialized client used throughout the application. StreamChatClient get client => widget.client; - Timer _disconnectTimer; + Timer? _disconnectTimer; @override Widget build(BuildContext context) => widget.child; /// The current user - User get user => client.state?.user; + User? get user => client.state.user; /// The current user as a stream - Stream get userStream => client.state?.userStream; + Stream get userStream => client.state.userStream; @override void initState() { super.initState(); - WidgetsBinding.instance.addObserver(this); + WidgetsBinding.instance?.addObserver(this); } - StreamSubscription _eventSubscription; + StreamSubscription? _eventSubscription; @override void didChangeAppLifecycleState(AppLifecycleState state) { @@ -119,15 +117,15 @@ class StreamChatCoreState extends State ); void onTimerComplete() { - _eventSubscription.cancel(); + _eventSubscription?.cancel(); client.disconnect(); } _disconnectTimer = Timer(widget.backgroundKeepAlive, onTimerComplete); } else if (state == AppLifecycleState.resumed) { if (_disconnectTimer?.isActive == true) { - _eventSubscription.cancel(); - _disconnectTimer.cancel(); + _eventSubscription?.cancel(); + _disconnectTimer?.cancel(); } else { if (client.wsConnectionStatus == ConnectionStatus.disconnected) { client.connect(); @@ -139,7 +137,7 @@ class StreamChatCoreState extends State @override void dispose() { - WidgetsBinding.instance.removeObserver(this); + WidgetsBinding.instance?.removeObserver(this); _eventSubscription?.cancel(); _disconnectTimer?.cancel(); super.dispose(); diff --git a/packages/stream_chat_flutter_core/lib/src/user_list_core.dart b/packages/stream_chat_flutter_core/lib/src/user_list_core.dart index f1bd68ee..694c52bc 100644 --- a/packages/stream_chat_flutter_core/lib/src/user_list_core.dart +++ b/packages/stream_chat_flutter_core/lib/src/user_list_core.dart @@ -57,27 +57,23 @@ import 'package:stream_chat_flutter_core/src/users_bloc.dart'; class UserListCore extends StatefulWidget { /// Instantiate a new [UserListCore] const UserListCore({ - @required this.errorBuilder, - @required this.emptyBuilder, - @required this.loadingBuilder, - @required this.listBuilder, - Key key, + required this.errorBuilder, + required this.emptyBuilder, + required this.loadingBuilder, + required this.listBuilder, + Key? key, this.filter, this.options, this.sort, this.pagination, this.groupAlphabetically = false, this.userListController, - }) : assert(errorBuilder != null, ''), - assert(emptyBuilder != null, ''), - assert(loadingBuilder != null, ''), - assert(listBuilder != null, ''), - super(key: key); + }) : super(key: key); /// A [UserListController] allows reloading and pagination. /// Use [UserListController.loadData] and [UserListController.paginateData] /// respectively for reloading and pagination. - final UserListController userListController; + final UserListController? userListController; /// The builder that will be used in case of error final Widget Function(Object error) errorBuilder; @@ -94,25 +90,25 @@ class UserListCore extends StatefulWidget { /// The query filters to use. /// You can query on any of the custom fields you've defined on the [Channel]. /// You can also filter other built-in channel fields. - final Map filter; + final Filter? filter; /// Query channels options. /// /// state: if true returns the Channel state /// watch: if true listen to changes to this Channel in real time. - final Map options; + final Map? options; /// The sorting used for the channels matching the filters. /// Sorting is based on field and direction, multiple sorting options can be /// provided. You can sort based on last_updated, last_message_at, updated_at, /// created_at or member_count. Direction can be ascending or descending. - final List sort; + final List? sort; /// Pagination parameters /// limit: the number of users to return (max is 30) /// offset: the offset (max is 1000) /// message_limit: how many messages should be included to each channel - final PaginationParams pagination; + final PaginationParams? pagination; /// Set it to true to group users by their first character /// @@ -131,8 +127,8 @@ class UserListCoreState extends State super.didChangeDependencies(); loadData(); if (widget.userListController != null) { - widget.userListController.loadData = loadData; - widget.userListController.paginateData = paginateData; + widget.userListController!.loadData = loadData; + widget.userListController!.paginateData = paginateData; } } @@ -158,14 +154,14 @@ class UserListCoreState extends State } final groupedUsers = >{}; for (final e in temp) { - final alphabet = e.name[0]?.toUpperCase(); + final alphabet = e.name[0].toUpperCase(); groupedUsers[alphabet] = [...groupedUsers[alphabet] ?? [], e]; } final items = []; for (final key in groupedUsers.keys) { items ..add(ListHeaderItem(key)) - ..addAll(groupedUsers[key].map((e) => ListUserItem(e))); + ..addAll(groupedUsers[key]!.map((e) => ListUserItem(e))); } return items; } @@ -180,12 +176,12 @@ class UserListCoreState extends State stream: _buildUserStream(usersBlocState), builder: (context, snapshot) { if (snapshot.hasError) { - return widget.errorBuilder(snapshot.error); + return widget.errorBuilder(snapshot.error!); } if (!snapshot.hasData) { return widget.loadingBuilder(context); } - final items = snapshot.data; + final items = snapshot.data!; if (items.isEmpty) { return widget.emptyBuilder(context); } @@ -210,7 +206,7 @@ class UserListCoreState extends State return _usersBloc.queryUsers( filter: widget.filter, sort: widget.sort, - pagination: widget.pagination.copyWith( + pagination: widget.pagination!.copyWith( offset: _usersBloc.users?.length ?? 0, ), options: widget.options, @@ -223,8 +219,8 @@ class UserListCoreState extends State if (widget.filter?.toString() != oldWidget.filter?.toString() || jsonEncode(widget.sort) != jsonEncode(oldWidget.sort) || widget.options?.toString() != oldWidget.options?.toString() || - widget.pagination?.toJson()?.toString() != - oldWidget.pagination?.toJson()?.toString()) { + widget.pagination?.toJson().toString() != + oldWidget.pagination?.toJson().toString()) { loadData(); } } @@ -235,7 +231,7 @@ class UserListCoreState extends State /// with `USER`. abstract class ListItem { /// Unique key per list item - String get key { + String? get key { if (this is ListHeaderItem) { final header = (this as ListHeaderItem).heading; return 'HEADER-${header.toLowerCase()}'; @@ -250,8 +246,8 @@ abstract class ListItem { /// Helper function to build widget based on ListItem type // ignore: missing_return Widget when({ - @required Widget Function(String heading) headerItem, - @required Widget Function(User user) userItem, + required Widget Function(String heading) headerItem, + required Widget Function(User user) userItem, }) { if (this is ListHeaderItem) { return headerItem((this as ListHeaderItem).heading); @@ -259,6 +255,7 @@ abstract class ListItem { if (this is ListUserItem) { return userItem((this as ListUserItem).user); } + return Container(); } } @@ -283,8 +280,8 @@ class ListUserItem extends ListItem { /// Controller used for paginating data in [ChannelListView] class UserListController { /// Call this function to reload data - AsyncCallback loadData; + AsyncCallback? loadData; /// Call this function to load further data - AsyncCallback paginateData; + AsyncCallback? paginateData; } diff --git a/packages/stream_chat_flutter_core/lib/src/users_bloc.dart b/packages/stream_chat_flutter_core/lib/src/users_bloc.dart index 92881619..9b33f75c 100644 --- a/packages/stream_chat_flutter_core/lib/src/users_bloc.dart +++ b/packages/stream_chat_flutter_core/lib/src/users_bloc.dart @@ -14,13 +14,9 @@ class UsersBloc extends StatefulWidget { /// Instantiate a new [UsersBloc]. The parameter [child] must be supplied and /// not null. const UsersBloc({ - @required this.child, - Key key, - }) : assert( - child != null, - 'When constructing a UsersBloc, the parameter ' - 'child should not be null.'), - super(key: key); + required this.child, + Key? key, + }) : super(key: key); /// The widget child final Widget child; @@ -30,7 +26,7 @@ class UsersBloc extends StatefulWidget { /// Use this method to get the current [UsersBlocState] instance static UsersBlocState of(BuildContext context) { - UsersBlocState state; + UsersBlocState? state; state = context.findAncestorStateOfType(); @@ -46,7 +42,7 @@ class UsersBloc extends StatefulWidget { class UsersBlocState extends State with AutomaticKeepAliveClientMixin { /// The current users list - List get users => _usersController.value; + List? get users => _usersController.value; /// The current users list as a stream Stream> get usersStream => _usersController.stream; @@ -62,10 +58,10 @@ class UsersBlocState extends State /// online/offline. /// [API Reference](https://getstream.io/chat/docs/flutter-dart/query_users/?language=dart) Future queryUsers({ - Map filter, - List sort, - Map options, - PaginationParams pagination, + Filter? filter, + List? sort, + Map? options, + PaginationParams? pagination, }) async { final client = StreamChatCore.of(context).client; @@ -76,9 +72,7 @@ class UsersBlocState extends State } try { - final clear = pagination == null || - pagination.offset == null || - pagination.offset == 0; + final clear = pagination == null || pagination.offset == 0; final oldUsers = List.from(users ?? []); @@ -95,7 +89,7 @@ class UsersBlocState extends State final temp = oldUsers + usersResponse.users; _usersController.add(temp); } - if (_usersController.hasValue && _queryUsersLoadingController.value) { + if (_usersController.hasValue && _queryUsersLoadingController.value!) { _queryUsersLoadingController.add(false); } } catch (e, stk) { diff --git a/packages/stream_chat_flutter_core/pubspec.yaml b/packages/stream_chat_flutter_core/pubspec.yaml index 1d035bac..031d8711 100644 --- a/packages/stream_chat_flutter_core/pubspec.yaml +++ b/packages/stream_chat_flutter_core/pubspec.yaml @@ -1,26 +1,25 @@ name: stream_chat_flutter_core homepage: https://github.com/GetStream/stream-chat-flutter description: Stream Chat official Flutter SDK Core. Build your own chat experience using Dart and Flutter. -version: 1.5.2 +version: 2.0.0-nullsafety.0 repository: https://github.com/GetStream/stream-chat-flutter issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues environment: - sdk: ">=2.7.0 <3.0.0" + sdk: '>=2.12.0 <3.0.0' flutter: ">=1.17.0" dependencies: + collection: ^1.15.0-nullsafety.4 flutter: sdk: flutter - meta: ^1.2.4 - rxdart: ^0.25.0 + meta: ^1.3.0 + rxdart: ^0.26.0 stream_chat: ^1.5.3 -dependency_overrides: - stream_chat: - path: ../stream_chat - dev_dependencies: + fake_async: ^1.2.0 flutter_test: sdk: flutter - mockito: ^4.1.3 \ No newline at end of file + mocktail: ^0.1.1 + diff --git a/packages/stream_chat_flutter_core/test/channel_list_core_test.dart b/packages/stream_chat_flutter_core/test/channel_list_core_test.dart index b0fb9ded..53303231 100644 --- a/packages/stream_chat_flutter_core/test/channel_list_core_test.dart +++ b/packages/stream_chat_flutter_core/test/channel_list_core_test.dart @@ -1,8 +1,8 @@ import 'dart:async'; -import 'package:flutter_test/flutter_test.dart'; import 'package:flutter/widgets.dart'; -import 'package:mockito/mockito.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mocktail/mocktail.dart'; import 'package:stream_chat_flutter_core/src/channel_list_core.dart'; import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; @@ -24,64 +24,12 @@ void main() { client, 'testType$index', 'testId$index', - {'extra_data_key': 'extra_data_value_$index'}, + extraData: {'extra_data_key': 'extra_data_value_$index'}, ); }, ); } - test( - 'should throw assertion error in case listBuilder is null', - () { - final channelListCore = () => ChannelListCore( - listBuilder: null, - loadingBuilder: (BuildContext context) => Offstage(), - emptyBuilder: (BuildContext context) => Offstage(), - errorBuilder: (BuildContext context, Object error) => Offstage(), - ); - expect(channelListCore, throwsA(isA())); - }, - ); - - test( - 'should throw assertion error in case loadingBuilder is null', - () { - final channelListCore = () => ChannelListCore( - listBuilder: (_, __) => Offstage(), - loadingBuilder: null, - emptyBuilder: (BuildContext context) => Offstage(), - errorBuilder: (BuildContext context, Object error) => Offstage(), - ); - expect(channelListCore, throwsA(isA())); - }, - ); - - test( - 'should throw assertion error in case emptyBuilder is null', - () { - final channelListCore = () => ChannelListCore( - listBuilder: (_, __) => Offstage(), - loadingBuilder: (BuildContext context) => Offstage(), - emptyBuilder: null, - errorBuilder: (BuildContext context, Object error) => Offstage(), - ); - expect(channelListCore, throwsA(isA())); - }, - ); - - test( - 'should throw assertion error in case errorBuilder is null', - () { - final channelListCore = () => ChannelListCore( - listBuilder: (_, __) => Offstage(), - loadingBuilder: (BuildContext context) => Offstage(), - emptyBuilder: (BuildContext context) => Offstage(), - errorBuilder: null, - ); - expect(channelListCore, throwsA(isA())); - }, - ); - testWidgets( 'should throw if ChannelListCore is used where ChannelsBloc is not present ' 'in the widget tree', @@ -116,7 +64,8 @@ void main() { final mockClient = MockClient(); - when(mockClient.on(any, any, any, any)).thenAnswer((_) => Stream.empty()); + when(() => mockClient.on(any(), any(), any(), any())) + .thenAnswer((_) => Stream.empty()); await tester.pumpWidget( StreamChatCore( @@ -151,7 +100,8 @@ void main() { final mockClient = MockClient(); - when(mockClient.on(any, any, any, any)).thenAnswer((_) => Stream.empty()); + when(() => mockClient.on(any(), any(), any(), any())) + .thenAnswer((_) => Stream.empty()); await tester.pumpWidget( StreamChatCore( @@ -185,15 +135,16 @@ void main() { final mockClient = MockClient(); - when(mockClient.on(any, any, any, any)).thenAnswer((_) => Stream.empty()); + when(() => mockClient.on(any(), any(), any(), any())) + .thenAnswer((_) => Stream.empty()); const error = 'Error! Error! Error!'; - when(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: pagination, - )).thenThrow(error); + when(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: pagination, + )).thenThrow(error); await tester.pumpWidget( StreamChatCore( @@ -208,12 +159,12 @@ void main() { expect(find.byKey(errorWidgetKey), findsOneWidget); - verify(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: pagination, - )).called(1); + verify(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: pagination, + )).called(1); }, ); @@ -233,15 +184,16 @@ void main() { final mockClient = MockClient(); - when(mockClient.on(any, any, any, any)).thenAnswer((_) => Stream.empty()); + when(() => mockClient.on(any(), any(), any(), any())) + .thenAnswer((_) => Stream.empty()); const channels = []; - when(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: pagination, - )).thenAnswer((_) => Stream.value(channels)); + when(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: pagination, + )).thenAnswer((_) => Stream.value(channels)); await tester.pumpWidget( StreamChatCore( @@ -256,12 +208,12 @@ void main() { expect(find.byKey(emptyWidgetKey), findsOneWidget); - verify(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: pagination, - )).called(1); + verify(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: pagination, + )).called(1); }, ); @@ -281,15 +233,16 @@ void main() { final mockClient = MockClient(); - when(mockClient.on(any, any, any, any)).thenAnswer((_) => Stream.empty()); + when(() => mockClient.on(any(), any(), any(), any())) + .thenAnswer((_) => Stream.empty()); final channels = _generateChannels(mockClient); - when(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: pagination, - )).thenAnswer((_) => Stream.value(channels)); + when(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: pagination, + )).thenAnswer((_) => Stream.value(channels)); await tester.pumpWidget( StreamChatCore( @@ -304,12 +257,12 @@ void main() { expect(find.byKey(listWidgetKey), findsOneWidget); - verify(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: pagination, - )).called(1); + verify(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: pagination, + )).called(1); }, ); @@ -337,15 +290,16 @@ void main() { final mockClient = MockClient(); - when(mockClient.on(any, any, any, any)).thenAnswer((_) => Stream.empty()); + when(() => mockClient.on(any(), any(), any(), any())) + .thenAnswer((_) => Stream.empty()); final channels = _generateChannels(mockClient); - when(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: pagination, - )).thenAnswer((_) => Stream.value(channels)); + when(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: pagination, + )).thenAnswer((_) => Stream.value(channels)); await tester.pumpWidget( Directionality( @@ -364,12 +318,12 @@ void main() { expect(find.byKey(listWidgetKey), findsOneWidget); expect(find.text(channels.map((e) => e.cid).join(',')), findsOneWidget); - verify(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: pagination, - )).called(1); + verify(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: pagination, + )).called(1); final channelListCoreState = tester.state( find.byKey(channelListCoreKey), @@ -378,12 +332,12 @@ void main() { final offset = channels.length; final paginatedChannels = _generateChannels(mockClient, offset: offset); final updatedPagination = pagination.copyWith(offset: offset); - when(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: updatedPagination, - )).thenAnswer((_) => Stream.value(paginatedChannels)); + when(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: updatedPagination, + )).thenAnswer((_) => Stream.value(paginatedChannels)); await channelListCoreState.paginateData(); @@ -398,12 +352,12 @@ void main() { findsOneWidget, ); - verify(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: updatedPagination, - )).called(1); + verify(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: updatedPagination, + )).called(1); }, ); @@ -411,8 +365,8 @@ void main() { 'should rebuild ChannelListCore with updated widget data ' 'on calling setState()', (tester) async { - StateSetter _stateSetter; - int limit = pagination.limit; + StateSetter? _stateSetter; + var limit = pagination.limit; const channelListCoreKey = Key('channelListCore'); const listWidgetKey = Key('listWidget'); @@ -435,15 +389,16 @@ void main() { final mockClient = MockClient(); - when(mockClient.on(any, any, any, any)).thenAnswer((_) => Stream.empty()); + when(() => mockClient.on(any(), any(), any(), any())) + .thenAnswer((_) => Stream.empty()); final channels = _generateChannels(mockClient); - when(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: pagination, - )).thenAnswer((_) => Stream.value(channels)); + when(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: pagination, + )).thenAnswer((_) => Stream.value(channels)); await tester.pumpWidget( Directionality( @@ -466,24 +421,24 @@ void main() { expect(find.byKey(listWidgetKey), findsOneWidget); expect(find.text(channels.map((e) => e.cid).join(',')), findsOneWidget); - verify(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: pagination, - )).called(1); + verify(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: pagination, + )).called(1); // Rebuilding ChannelListCore with new pagination limit - _stateSetter(() => limit = 6); + _stateSetter?.call(() => limit = 6); final updatedChannels = _generateChannels(mockClient, count: limit); final updatedPagination = pagination.copyWith(limit: limit); - when(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: updatedPagination, - )).thenAnswer((_) => Stream.value(updatedChannels)); + when(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: updatedPagination, + )).thenAnswer((_) => Stream.value(updatedChannels)); await tester.pumpAndSettle(); @@ -493,12 +448,12 @@ void main() { findsOneWidget, ); - verify(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: updatedPagination, - )).called(1); + verify(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: updatedPagination, + )).called(1); }, ); } diff --git a/packages/stream_chat_flutter_core/test/channels_bloc_test.dart b/packages/stream_chat_flutter_core/test/channels_bloc_test.dart index e021e9b7..19d327bd 100644 --- a/packages/stream_chat_flutter_core/test/channels_bloc_test.dart +++ b/packages/stream_chat_flutter_core/test/channels_bloc_test.dart @@ -2,13 +2,17 @@ import 'dart:async'; import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:mockito/mockito.dart'; +import 'package:mocktail/mocktail.dart'; import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; import 'matchers/channel_matcher.dart'; import 'mocks.dart'; void main() { + setUpAll(() { + registerFallbackValue(const PaginationParams()); + }); + List _generateChannels( StreamChatClient client, { int count = 3, @@ -22,24 +26,12 @@ void main() { client, 'testType$index', 'testId$index', - {'extra_data_key': 'extra_data_value_$index'}, + extraData: {'extra_data_key': 'extra_data_value_$index'}, ); }, ); } - test( - 'should throw assertion error if child is null', - () async { - const channelsBlocKey = Key('channelsBloc'); - final channelsBloc = () => ChannelsBloc( - key: channelsBlocKey, - child: null, - ); - expect(channelsBloc, throwsA(isA())); - }, - ); - testWidgets( 'should throw if ChannelsBloc is used where StreamChat is not present in the widget tree', (tester) async { @@ -70,7 +62,8 @@ void main() { final mockClient = MockClient(); - when(mockClient.on(any, any, any, any)).thenAnswer((_) => Stream.empty()); + when(() => mockClient.on(any(), any(), any(), any())) + .thenAnswer((_) => Stream.empty()); await tester.pumpWidget( StreamChatCore( @@ -101,7 +94,8 @@ void main() { final mockClient = MockClient(); - when(mockClient.on(any, any, any, any)).thenAnswer((_) => Stream.empty()); + when(() => mockClient.on(any(), any(), any(), any())) + .thenAnswer((_) => Stream.empty()); await tester.pumpWidget( StreamChatCore( @@ -117,12 +111,12 @@ void main() { final offlineChannels = _generateChannels(mockClient); final onlineChannels = _generateChannels(mockClient, offset: 3); - when(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: anyNamed('paginationParams'), - )).thenAnswer( + when(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: any(named: 'paginationParams'), + )).thenAnswer( (_) => Stream.fromIterable([offlineChannels, onlineChannels]), ); @@ -136,12 +130,12 @@ void main() { ]), ); - verify(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: anyNamed('paginationParams'), - )).called(1); + verify(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: any(named: 'paginationParams'), + )).called(1); }, ); @@ -163,7 +157,8 @@ void main() { final mockClient = MockClient(); - when(mockClient.on(any, any, any, any)).thenAnswer((_) => Stream.empty()); + when(() => mockClient.on(any(), any(), any(), any())) + .thenAnswer((_) => Stream.empty()); await tester.pumpWidget( StreamChatCore( @@ -176,14 +171,14 @@ void main() { find.byKey(channelsBlocKey), ); - final error = 'Error! Error! Error!'; + const error = 'Error! Error! Error!'; - when(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: anyNamed('paginationParams'), - )).thenThrow(error); + when(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: any(named: 'paginationParams'), + )).thenThrow(error); channelsBlocState.queryChannels(); @@ -192,12 +187,12 @@ void main() { emitsError(error), ); - verify(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: anyNamed('paginationParams'), - )).called(1); + verify(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: any(named: 'paginationParams'), + )).called(1); }, ); @@ -214,7 +209,8 @@ void main() { final mockClient = MockClient(); - when(mockClient.on(any, any, any, any)).thenAnswer((_) => Stream.empty()); + when(() => mockClient.on(any(), any(), any(), any())) + .thenAnswer((_) => Stream.empty()); await tester.pumpWidget( StreamChatCore( @@ -229,38 +225,41 @@ void main() { final channels = _generateChannels(mockClient); - when(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: anyNamed('paginationParams'), - )).thenAnswer((_) => Stream.value(channels)); + when(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: any(named: 'paginationParams'), + )).thenAnswer((_) => Stream.value(channels)); - channelsBlocState.queryChannels(); + const pagination = PaginationParams(limit: 3); + channelsBlocState.queryChannels( + paginationParams: pagination, + ); await expectLater( channelsBlocState.channelsStream, emits(isSameChannelListAs(channels)), ); - verify(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: anyNamed('paginationParams'), - )).called(1); + verify(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: any(named: 'paginationParams'), + )).called(1); final offset = channels.length; - final paginationParams = PaginationParams(offset: offset); + final paginationParams = pagination.copyWith(offset: offset); final newChannels = _generateChannels(mockClient, offset: offset); - when(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: paginationParams, - )).thenAnswer( + when(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: paginationParams, + )).thenAnswer( (_) => Stream.value(newChannels), ); @@ -277,12 +276,12 @@ void main() { ), ]); - verify(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: paginationParams, - )).called(1); + verify(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: paginationParams, + )).called(1); }, ); @@ -299,7 +298,8 @@ void main() { final mockClient = MockClient(); - when(mockClient.on(any, any, any, any)).thenAnswer((_) => Stream.empty()); + when(() => mockClient.on(any(), any(), any(), any())) + .thenAnswer((_) => Stream.empty()); await tester.pumpWidget( StreamChatCore( @@ -313,39 +313,41 @@ void main() { ); final channels = _generateChannels(mockClient); + final paginationParams = const PaginationParams( + limit: 3, + ); - when(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: anyNamed('paginationParams'), - )).thenAnswer((_) => Stream.value(channels)); + when(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: paginationParams, + )).thenAnswer((_) => Stream.value(channels)); - channelsBlocState.queryChannels(); + channelsBlocState.queryChannels( + paginationParams: paginationParams, + ); await expectLater( channelsBlocState.channelsStream, emits(isSameChannelListAs(channels)), ); - verify(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: anyNamed('paginationParams'), - )).called(1); - - final offset = channels.length; - final paginationParams = PaginationParams(offset: offset); + verify(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: paginationParams, + )).called(1); final error = 'Error! Error! Error!'; - when(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: paginationParams, - )).thenThrow(error); + when(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: paginationParams, + )).thenThrow(error); channelsBlocState.queryChannels(paginationParams: paginationParams); @@ -354,17 +356,17 @@ void main() { emitsError(error), ); - verify(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: paginationParams, - )).called(1); + verify(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: paginationParams, + )).called(1); }, ); group('event controller test', () { - StreamController eventController; + late StreamController eventController; setUp(() { eventController = StreamController.broadcast(); }); @@ -379,12 +381,12 @@ void main() { child: Offstage(), ); - when(mockClient.on(any, any, any, any)) + when(() => mockClient.on(any(), any(), any(), any())) .thenAnswer((_) => Stream.empty()); - when(mockClient.on( - EventType.channelHidden, - )).thenAnswer((_) => eventController.stream); + when(() => mockClient.on( + EventType.channelHidden, + )).thenAnswer((_) => eventController.stream); await tester.pumpWidget( StreamChatCore( @@ -399,23 +401,23 @@ void main() { final channels = _generateChannels(mockClient); - when(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: anyNamed('paginationParams'), - )).thenAnswer( + when(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: any(named: 'paginationParams'), + )).thenAnswer( (_) => Stream.value(channels), ); await channelsBlocState.queryChannels(); - verify(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: anyNamed('paginationParams'), - )).called(1); + verify(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: any(named: 'paginationParams'), + )).called(1); final channelHiddenEvent = Event( type: EventType.channelHidden, @@ -435,7 +437,7 @@ void main() { ]), ); - verify(mockClient.on(EventType.channelHidden)).called(1); + verify(() => mockClient.on(EventType.channelHidden)).called(1); }, ); @@ -450,13 +452,13 @@ void main() { child: Offstage(), ); - when(mockClient.on(any, any, any, any)) + when(() => mockClient.on(any(), any(), any(), any())) .thenAnswer((_) => Stream.empty()); - when(mockClient.on( - EventType.channelDeleted, - EventType.notificationRemovedFromChannel, - )).thenAnswer((_) => eventController.stream); + when(() => mockClient.on( + EventType.channelDeleted, + EventType.notificationRemovedFromChannel, + )).thenAnswer((_) => eventController.stream); await tester.pumpWidget( StreamChatCore( @@ -471,31 +473,38 @@ void main() { final channels = _generateChannels(mockClient); - when(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: anyNamed('paginationParams'), - )).thenAnswer( + when(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: any(named: 'paginationParams'), + )).thenAnswer( (_) => Stream.value(channels), ); await channelsBlocState.queryChannels(); - verify(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: anyNamed('paginationParams'), - )).called(1); + verify(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: any(named: 'paginationParams'), + )).called(1); final channelDeletedOrNotificationRemovedEvent = Event( - channel: EventChannel(cid: channels.first.cid), + channel: EventChannel( + cid: channels.first.cid!, + updatedAt: DateTime.now(), + config: ChannelConfig(), + createdAt: DateTime.now(), + memberCount: 1, + ), ); eventController.add(channelDeletedOrNotificationRemovedEvent); - final channelCid = channelDeletedOrNotificationRemovedEvent.channel.cid; + final channelCid = + channelDeletedOrNotificationRemovedEvent.channel?.cid; final newChannels = [...channels] ..removeWhere((it) => it.cid == channelCid); @@ -507,10 +516,10 @@ void main() { ]), ); - verify(mockClient.on( - EventType.channelDeleted, - EventType.notificationRemovedFromChannel, - )).called(1); + verify(() => mockClient.on( + EventType.channelDeleted, + EventType.notificationRemovedFromChannel, + )).called(1); }, ); @@ -525,12 +534,12 @@ void main() { child: Offstage(), ); - when(mockClient.on(any, any, any, any)) + when(() => mockClient.on(any(), any(), any(), any())) .thenAnswer((_) => Stream.empty()); - when(mockClient.on( - EventType.messageNew, - )).thenAnswer((_) => eventController.stream); + when(() => mockClient.on( + EventType.messageNew, + )).thenAnswer((_) => eventController.stream); await tester.pumpWidget( StreamChatCore( @@ -545,23 +554,23 @@ void main() { final channels = _generateChannels(mockClient); - when(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: anyNamed('paginationParams'), - )).thenAnswer( + when(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: any(named: 'paginationParams'), + )).thenAnswer( (_) => Stream.value(channels), ); await channelsBlocState.queryChannels(); - verify(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: anyNamed('paginationParams'), - )).called(1); + verify(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: any(named: 'paginationParams'), + )).called(1); final messageNewEvent = Event( type: EventType.messageNew, @@ -585,7 +594,7 @@ void main() { ]), ); - verify(mockClient.on(EventType.messageNew)).called(1); + verify(() => mockClient.on(EventType.messageNew)).called(1); }, ); @@ -609,16 +618,16 @@ void main() { shouldAddChannel: (e) => channels.map((it) => it.cid).contains(e.cid), ); - when(mockClient.on(any, any, any, any)) + when(() => mockClient.on(any(), any(), any(), any())) .thenAnswer((_) => Stream.empty()); - when(mockClient.on( - EventType.channelHidden, - )).thenAnswer((_) => hiddenChannelEventController.stream); + when(() => mockClient.on( + EventType.channelHidden, + )).thenAnswer((_) => hiddenChannelEventController.stream); - when(mockClient.on( - EventType.messageNew, - )).thenAnswer((_) => eventController.stream); + when(() => mockClient.on( + EventType.messageNew, + )).thenAnswer((_) => eventController.stream); final messageNewEvent = Event( type: EventType.messageNew, @@ -636,23 +645,23 @@ void main() { find.byKey(channelsBlocKey), ); - when(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: anyNamed('paginationParams'), - )).thenAnswer( + when(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: any(named: 'paginationParams'), + )).thenAnswer( (_) => Stream.value(channels), ); await channelsBlocState.queryChannels(); - verify(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: anyNamed('paginationParams'), - )).called(1); + verify(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: any(named: 'paginationParams'), + )).called(1); final channelHiddenEvent = Event( type: EventType.channelHidden, @@ -681,8 +690,8 @@ void main() { ]), ); - verify(mockClient.on(EventType.channelHidden)).called(1); - verify(mockClient.on(EventType.messageNew)).called(1); + verify(() => mockClient.on(EventType.channelHidden)).called(1); + verify(() => mockClient.on(EventType.messageNew)).called(1); }, ); @@ -694,7 +703,7 @@ void main() { final mockClient = MockClient(); final channels = _generateChannels(mockClient); final stateChannels = { - for (var c in _generateChannels(mockClient, offset: 5)) c.cid: c + for (var c in _generateChannels(mockClient, offset: 5)) c.cid!: c }; const channelsBlocKey = Key('channelsBloc'); final channelsBloc = ChannelsBloc( @@ -703,14 +712,14 @@ void main() { shouldAddChannel: (_) => true, ); - when(mockClient.state.channels).thenReturn(stateChannels); + when(() => mockClient.state.channels).thenReturn(stateChannels); - when(mockClient.on(any, any, any, any)) + when(() => mockClient.on(any(), any(), any(), any())) .thenAnswer((_) => Stream.empty()); - when(mockClient.on( - EventType.messageNew, - )).thenAnswer((_) => eventController.stream); + when(() => mockClient.on( + EventType.messageNew, + )).thenAnswer((_) => eventController.stream); await tester.pumpWidget( StreamChatCore( @@ -723,23 +732,23 @@ void main() { find.byKey(channelsBlocKey), ); - when(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: anyNamed('paginationParams'), - )).thenAnswer( + when(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: any(named: 'paginationParams'), + )).thenAnswer( (_) => Stream.value(channels), ); await channelsBlocState.queryChannels(); - verify(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: anyNamed('paginationParams'), - )).called(1); + verify(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: any(named: 'paginationParams'), + )).called(1); final messageNewEvent = Event( type: EventType.messageNew, @@ -749,7 +758,7 @@ void main() { eventController.add(messageNewEvent); final newChannels = [...channels] - ..insert(0, stateChannels[stateChannels.keys.first]); + ..insert(0, stateChannels[stateChannels.keys.first]!); await expectLater( channelsBlocState.channelsStream, @@ -759,7 +768,7 @@ void main() { ]), ); - verify(mockClient.on(EventType.messageNew)).called(1); + verify(() => mockClient.on(EventType.messageNew)).called(1); }, ); @@ -783,12 +792,12 @@ void main() { channelsComparator: channelComparator, ); - when(mockClient.on(any, any, any, any)) + when(() => mockClient.on(any(), any(), any(), any())) .thenAnswer((_) => Stream.empty()); - when(mockClient.on( - EventType.messageNew, - )).thenAnswer((_) => eventController.stream); + when(() => mockClient.on( + EventType.messageNew, + )).thenAnswer((_) => eventController.stream); await tester.pumpWidget( StreamChatCore( @@ -801,23 +810,23 @@ void main() { find.byKey(channelsBlocKey), ); - when(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: anyNamed('paginationParams'), - )).thenAnswer( + when(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: any(named: 'paginationParams'), + )).thenAnswer( (_) => Stream.value(channels), ); await channelsBlocState.queryChannels(); - verify(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: anyNamed('paginationParams'), - )).called(1); + verify(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: any(named: 'paginationParams'), + )).called(1); final messageNewEvent = Event( type: EventType.messageNew, @@ -836,7 +845,7 @@ void main() { ]), ); - verify(mockClient.on(EventType.messageNew)).called(1); + verify(() => mockClient.on(EventType.messageNew)).called(1); }, ); diff --git a/packages/stream_chat_flutter_core/test/lazy_load_scroll_view_test.dart b/packages/stream_chat_flutter_core/test/lazy_load_scroll_view_test.dart index 19c1f2a2..af59088f 100644 --- a/packages/stream_chat_flutter_core/test/lazy_load_scroll_view_test.dart +++ b/packages/stream_chat_flutter_core/test/lazy_load_scroll_view_test.dart @@ -3,18 +3,6 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:stream_chat_flutter_core/src/lazy_load_scroll_view.dart'; void main() { - test( - 'should throw assertion error if child is null', - () async { - const lazyLoadScrollViewKey = Key('lazyLoadScrollView'); - final lazyLoadScrollView = () => LazyLoadScrollView( - key: lazyLoadScrollViewKey, - child: null, - ); - expect(lazyLoadScrollView, throwsA(isA())); - }, - ); - testWidgets( 'should render LazyLoadScrollView if child is provided', (tester) async { diff --git a/packages/stream_chat_flutter_core/test/matchers/channel_matcher.dart b/packages/stream_chat_flutter_core/test/matchers/channel_matcher.dart index f371acff..f6d7fb50 100644 --- a/packages/stream_chat_flutter_core/test/matchers/channel_matcher.dart +++ b/packages/stream_chat_flutter_core/test/matchers/channel_matcher.dart @@ -7,7 +7,7 @@ Matcher isSameChannelAs(Channel targetChannel) => class _IsSameChannelAs extends Matcher { const _IsSameChannelAs({ - @required this.targetChannel, + required this.targetChannel, }) : assert(targetChannel != null, ''); final Channel targetChannel; @@ -26,7 +26,7 @@ Matcher isSameChannelListAs(List targetChannelList) => class _IsSameChannelListAs extends Matcher { const _IsSameChannelListAs({ - @required this.targetChannelList, + required this.targetChannelList, }) : assert(targetChannelList != null, ''); final List targetChannelList; diff --git a/packages/stream_chat_flutter_core/test/matchers/get_message_response_matcher.dart b/packages/stream_chat_flutter_core/test/matchers/get_message_response_matcher.dart index bd89420b..c3a6629d 100644 --- a/packages/stream_chat_flutter_core/test/matchers/get_message_response_matcher.dart +++ b/packages/stream_chat_flutter_core/test/matchers/get_message_response_matcher.dart @@ -1,4 +1,3 @@ -import 'package:meta/meta.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; @@ -7,15 +6,15 @@ Matcher isSameMessageResponseAs(GetMessageResponse targetResponse) => class _IsSameMessageResponseAs extends Matcher { const _IsSameMessageResponseAs({ - @required this.targetResponse, - }) : assert(targetResponse != null, ''); + required this.targetResponse, + }); final GetMessageResponse targetResponse; @override bool matches(covariant GetMessageResponse response, Map matchState) => response.message.id == targetResponse.message.id && - response.channel.cid == targetResponse.channel.cid; + response.channel?.cid == targetResponse.channel?.cid; @override Description describe(Description description) => @@ -28,15 +27,15 @@ Matcher isSameMessageResponseListAs( class _IsSameMessageResponseListAs extends Matcher { const _IsSameMessageResponseListAs({ - @required this.targetResponseList, - }) : assert(targetResponseList != null, ''); + required this.targetResponseList, + }); final List targetResponseList; @override bool matches( covariant List responseList, Map matchState) { - bool matches = true; + var matches = true; for (var i = 0; i < responseList.length; i++) { final response = responseList[i]; final targetResponse = targetResponseList[i]; diff --git a/packages/stream_chat_flutter_core/test/matchers/message_matcher.dart b/packages/stream_chat_flutter_core/test/matchers/message_matcher.dart index 12a3e19d..69a24f49 100644 --- a/packages/stream_chat_flutter_core/test/matchers/message_matcher.dart +++ b/packages/stream_chat_flutter_core/test/matchers/message_matcher.dart @@ -7,7 +7,7 @@ Matcher isSameMessageAs(Message targetMessage) => class _IsSameMessageAs extends Matcher { const _IsSameMessageAs({ - @required this.targetMessage, + required this.targetMessage, }) : assert(targetMessage != null, ''); final Message targetMessage; @@ -26,7 +26,7 @@ Matcher isSameMessageListAs(List targetMessageList) => class _IsSameMessageListAs extends Matcher { const _IsSameMessageListAs({ - @required this.targetMessageList, + required this.targetMessageList, }) : assert(targetMessageList != null, ''); final List targetMessageList; diff --git a/packages/stream_chat_flutter_core/test/matchers/users_matcher.dart b/packages/stream_chat_flutter_core/test/matchers/users_matcher.dart index 3816fa24..38e25a64 100644 --- a/packages/stream_chat_flutter_core/test/matchers/users_matcher.dart +++ b/packages/stream_chat_flutter_core/test/matchers/users_matcher.dart @@ -6,7 +6,7 @@ Matcher isSameUserAs(User targetUser) => _IsSameUserAs(targetUser: targetUser); class _IsSameUserAs extends Matcher { const _IsSameUserAs({ - @required this.targetUser, + required this.targetUser, }) : assert(targetUser != null, ''); final User targetUser; @@ -24,7 +24,7 @@ Matcher isSameUserListAs(List targetUserList) => class _IsSameUserListAs extends Matcher { const _IsSameUserListAs({ - @required this.targetUserList, + required this.targetUserList, }) : assert(targetUserList != null, ''); final List targetUserList; diff --git a/packages/stream_chat_flutter_core/test/message_list_core_test.dart b/packages/stream_chat_flutter_core/test/message_list_core_test.dart index 95446f95..9aea9489 100644 --- a/packages/stream_chat_flutter_core/test/message_list_core_test.dart +++ b/packages/stream_chat_flutter_core/test/message_list_core_test.dart @@ -2,7 +2,7 @@ import 'dart:async'; import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:mockito/mockito.dart'; +import 'package:mocktail/mocktail.dart'; import 'package:stream_chat_flutter_core/src/message_list_core.dart'; import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; @@ -62,61 +62,6 @@ void main() { return threads ? threadMessages : messages; } - test( - 'should throw assertion error in case messageListBuilder is null', - () { - final messageListCore = () => MessageListCore( - messageListBuilder: null, - loadingBuilder: (BuildContext context) => Offstage(), - emptyBuilder: (BuildContext context) => Offstage(), - errorWidgetBuilder: (BuildContext context, Object error) => - Offstage(), - ); - expect(messageListCore, throwsA(isA())); - }, - ); - - test( - 'should throw assertion error in case loadingBuilder is null', - () { - final messageListCore = () => MessageListCore( - messageListBuilder: (_, __) => Offstage(), - loadingBuilder: null, - emptyBuilder: (BuildContext context) => Offstage(), - errorWidgetBuilder: (BuildContext context, Object error) => - Offstage(), - ); - expect(messageListCore, throwsA(isA())); - }, - ); - - test( - 'should throw assertion error in case emptyBuilder is null', - () { - final messageListCore = () => MessageListCore( - messageListBuilder: (_, __) => Offstage(), - loadingBuilder: (BuildContext context) => Offstage(), - emptyBuilder: null, - errorWidgetBuilder: (BuildContext context, Object error) => - Offstage(), - ); - expect(messageListCore, throwsA(isA())); - }, - ); - - test( - 'should throw assertion error in case errorWidgetBuilder is null', - () { - final messageListCore = () => MessageListCore( - messageListBuilder: (_, __) => Offstage(), - loadingBuilder: (BuildContext context) => Offstage(), - emptyBuilder: (BuildContext context) => Offstage(), - errorWidgetBuilder: null, - ); - expect(messageListCore, throwsA(isA())); - }, - ); - testWidgets( 'should throw if MessageListCore is used where StreamChannel is not present ' 'in the widget tree', @@ -150,8 +95,11 @@ void main() { ); final mockChannel = MockChannel(); + when(() => mockChannel.initialized).thenAnswer((_) => Future.value(true)); - when(mockChannel.state.isUpToDate).thenReturn(true); + when(() => mockChannel.state.isUpToDate).thenReturn(true); + when(() => mockChannel.state.messagesStream) + .thenAnswer((_) => Stream.value([])); await tester.pumpWidget( StreamChannel( @@ -182,7 +130,10 @@ void main() { final mockChannel = MockChannel(); - when(mockChannel.state.isUpToDate).thenReturn(true); + when(() => mockChannel.state.isUpToDate).thenReturn(true); + when(() => mockChannel.state.messagesStream) + .thenAnswer((_) => Stream.value([])); + when(() => mockChannel.initialized).thenAnswer((_) => Future.value(true)); await tester.pumpWidget( StreamChannel( @@ -213,11 +164,11 @@ void main() { final mockChannel = MockChannel(); - when(mockChannel.state.isUpToDate).thenReturn(true); - when(mockChannel.initialized).thenAnswer((_) async => true); + when(() => mockChannel.state.isUpToDate).thenReturn(true); + when(() => mockChannel.initialized).thenAnswer((_) async => true); const error = 'Error! Error! Error!'; - when(mockChannel.state.messagesStream) + when(() => mockChannel.state.messagesStream) .thenAnswer((_) => Stream.error(error)); await tester.pumpWidget( @@ -252,11 +203,11 @@ void main() { final mockChannel = MockChannel(); - when(mockChannel.state.isUpToDate).thenReturn(true); - when(mockChannel.initialized).thenAnswer((_) async => true); + when(() => mockChannel.state.isUpToDate).thenReturn(true); + when(() => mockChannel.initialized).thenAnswer((_) async => true); const messages = []; - when(mockChannel.state.messagesStream) + when(() => mockChannel.state.messagesStream) .thenAnswer((_) => Stream.value(messages)); await tester.pumpWidget( @@ -291,11 +242,18 @@ void main() { final mockChannel = MockChannel(); - when(mockChannel.state.isUpToDate).thenReturn(false); - when(mockChannel.initialized).thenAnswer((_) async => true); + when(() => mockChannel.state.isUpToDate).thenReturn(false); + when(() => mockChannel.initialized).thenAnswer((_) async => true); + when(() => mockChannel.query( + options: any(named: 'options'), + membersPagination: any(named: 'membersPagination'), + messagesPagination: any(named: 'messagesPagination'), + preferOffline: any(named: 'preferOffline'), + watchersPagination: any(named: 'watchersPagination'), + )).thenAnswer((_) async => ChannelState()); const messages = []; - when(mockChannel.state.messagesStream) + when(() => mockChannel.state.messagesStream) .thenAnswer((_) => Stream.value(messages)); await tester.pumpWidget( @@ -335,11 +293,11 @@ void main() { final mockChannel = MockChannel(); - when(mockChannel.state.isUpToDate).thenReturn(true); - when(mockChannel.initialized).thenAnswer((_) async => true); + when(() => mockChannel.state.isUpToDate).thenReturn(true); + when(() => mockChannel.initialized).thenAnswer((_) async => true); final messages = _generateMessages(); - when(mockChannel.state.messagesStream) + when(() => mockChannel.state.messagesStream) .thenAnswer((_) => Stream.value(messages)); await tester.pumpWidget( @@ -382,13 +340,13 @@ void main() { final mockChannel = MockChannel(); - when(mockChannel.state.isUpToDate).thenReturn(true); - when(mockChannel.initialized).thenAnswer((_) async => true); + when(() => mockChannel.state.isUpToDate).thenReturn(true); + when(() => mockChannel.initialized).thenAnswer((_) async => true); final threads = {parentMessage.id: messages}; - when(mockChannel.state.threads).thenReturn(threads); - when(mockChannel.state.threadsStream) + when(() => mockChannel.state.threads).thenReturn(threads); + when(() => mockChannel.state.threadsStream) .thenAnswer((_) => Stream.value(threads)); await tester.pumpWidget( diff --git a/packages/stream_chat_flutter_core/test/message_search_bloc_test.dart b/packages/stream_chat_flutter_core/test/message_search_bloc_test.dart index e639c03b..91c07435 100644 --- a/packages/stream_chat_flutter_core/test/message_search_bloc_test.dart +++ b/packages/stream_chat_flutter_core/test/message_search_bloc_test.dart @@ -1,6 +1,6 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:mockito/mockito.dart'; +import 'package:mocktail/mocktail.dart'; import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat_flutter_core/src/message_search_bloc.dart'; import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; @@ -8,6 +8,8 @@ import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; import 'matchers/get_message_response_matcher.dart'; import 'mocks.dart'; +final testFilter = Filter.custom(operator: '\$test', value: 'testValue'); + void main() { List _generateMessages({ int count = 3, @@ -23,24 +25,12 @@ void main() { text: 'testTextData$index', ) ..channel = ChannelModel( - cid: 'testCid', + cid: 'testCid:id', ); }, ); } - test( - 'should throw assertion error if child is null', - () async { - const messageSearchBlocKey = Key('messageSearchBloc'); - final messageSearchBloc = () => MessageSearchBloc( - key: messageSearchBlocKey, - child: null, - ); - expect(messageSearchBloc, throwsA(isA())); - }, - ); - testWidgets( 'messageSearchBlocState.search() should throw if used where ' 'StreamChat is not present in the widget tree', @@ -62,7 +52,7 @@ void main() { ); try { - await usersBlocState.search(); + await usersBlocState.search(filter: testFilter); } catch (e) { expect(e, isInstanceOf()); } @@ -93,30 +83,30 @@ void main() { final messageResponseList = _generateMessages(); - when(mockClient.search( - any, - query: anyNamed('query'), - sort: anyNamed('sort'), - messageFilters: anyNamed('messageFilters'), - paginationParams: anyNamed('paginationParams'), - )).thenAnswer( + when(() => mockClient.search( + testFilter, + query: any(named: 'query'), + sort: any(named: 'sort'), + messageFilters: any(named: 'messageFilters'), + paginationParams: any(named: 'paginationParams'), + )).thenAnswer( (_) async => SearchMessagesResponse()..results = messageResponseList, ); - messageSearchBlocState.search(); + messageSearchBlocState.search(filter: testFilter); await expectLater( messageSearchBlocState.messagesStream, emits(isSameMessageResponseListAs(messageResponseList)), ); - verify(mockClient.search( - any, - query: anyNamed('query'), - sort: anyNamed('sort'), - messageFilters: anyNamed('messageFilters'), - paginationParams: anyNamed('paginationParams'), - )).called(1); + verify(() => mockClient.search( + testFilter, + query: any(named: 'query'), + sort: any(named: 'sort'), + messageFilters: any(named: 'messageFilters'), + paginationParams: any(named: 'paginationParams'), + )).called(1); }, ); @@ -144,28 +134,28 @@ void main() { ); const error = 'Error! Error! Error!'; - when(mockClient.search( - any, - query: anyNamed('query'), - sort: anyNamed('sort'), - messageFilters: anyNamed('messageFilters'), - paginationParams: anyNamed('paginationParams'), - )).thenThrow(error); + when(() => mockClient.search( + testFilter, + query: any(named: 'query'), + sort: any(named: 'sort'), + messageFilters: any(named: 'messageFilters'), + paginationParams: any(named: 'paginationParams'), + )).thenThrow(error); - messageSearchBlocState.search(); + messageSearchBlocState.search(filter: testFilter); await expectLater( messageSearchBlocState.messagesStream, emitsError(error), ); - verify(mockClient.search( - any, - query: anyNamed('query'), - sort: anyNamed('sort'), - messageFilters: anyNamed('messageFilters'), - paginationParams: anyNamed('paginationParams'), - )).called(1); + verify(() => mockClient.search( + testFilter, + query: any(named: 'query'), + sort: any(named: 'sort'), + messageFilters: any(named: 'messageFilters'), + paginationParams: any(named: 'paginationParams'), + )).called(1); }, ); @@ -196,47 +186,47 @@ void main() { final messageResponseList = _generateMessages(); - when(mockClient.search( - any, - query: anyNamed('query'), - sort: anyNamed('sort'), - messageFilters: anyNamed('messageFilters'), - paginationParams: anyNamed('paginationParams'), - )).thenAnswer( + when(() => mockClient.search( + testFilter, + query: any(named: 'query'), + sort: any(named: 'sort'), + messageFilters: any(named: 'messageFilters'), + paginationParams: any(named: 'paginationParams'), + )).thenAnswer( (_) async => SearchMessagesResponse()..results = messageResponseList, ); - messageSearchBlocState.search(); + messageSearchBlocState.search(filter: testFilter); await expectLater( messageSearchBlocState.messagesStream, emits(isSameMessageResponseListAs(messageResponseList)), ); - verify(mockClient.search( - any, - query: anyNamed('query'), - sort: anyNamed('sort'), - messageFilters: anyNamed('messageFilters'), - paginationParams: anyNamed('paginationParams'), - )).called(1); + verify(() => mockClient.search( + testFilter, + query: any(named: 'query'), + sort: any(named: 'sort'), + messageFilters: any(named: 'messageFilters'), + paginationParams: any(named: 'paginationParams'), + )).called(1); final offset = messageResponseList.length; final paginatedMessageResponseList = _generateMessages(offset: offset); final pagination = PaginationParams(offset: offset); - when(mockClient.search( - any, - query: anyNamed('query'), - sort: anyNamed('sort'), - messageFilters: anyNamed('messageFilters'), - paginationParams: pagination, - )).thenAnswer( + when(() => mockClient.search( + testFilter, + query: any(named: 'query'), + sort: any(named: 'sort'), + messageFilters: any(named: 'messageFilters'), + paginationParams: pagination, + )).thenAnswer( (_) async => SearchMessagesResponse()..results = paginatedMessageResponseList, ); - messageSearchBlocState.search(pagination: pagination); + messageSearchBlocState.search(pagination: pagination, filter: testFilter); await Future.wait([ expectLater( @@ -251,13 +241,13 @@ void main() { ), ]); - verify(mockClient.search( - any, - query: anyNamed('query'), - sort: anyNamed('sort'), - messageFilters: anyNamed('messageFilters'), - paginationParams: pagination, - )).called(1); + verify(() => mockClient.search( + testFilter, + query: any(named: 'query'), + sort: any(named: 'sort'), + messageFilters: any(named: 'messageFilters'), + paginationParams: pagination, + )).called(1); }, ); @@ -288,57 +278,57 @@ void main() { final messageResponseList = _generateMessages(); - when(mockClient.search( - any, - query: anyNamed('query'), - sort: anyNamed('sort'), - messageFilters: anyNamed('messageFilters'), - paginationParams: anyNamed('paginationParams'), - )).thenAnswer( + when(() => mockClient.search( + testFilter, + query: any(named: 'query'), + sort: any(named: 'sort'), + messageFilters: any(named: 'messageFilters'), + paginationParams: any(named: 'paginationParams'), + )).thenAnswer( (_) async => SearchMessagesResponse()..results = messageResponseList, ); - messageSearchBlocState.search(); + messageSearchBlocState.search(filter: testFilter); await expectLater( messageSearchBlocState.messagesStream, emits(isSameMessageResponseListAs(messageResponseList)), ); - verify(mockClient.search( - any, - query: anyNamed('query'), - sort: anyNamed('sort'), - messageFilters: anyNamed('messageFilters'), - paginationParams: anyNamed('paginationParams'), - )).called(1); + verify(() => mockClient.search( + testFilter, + query: any(named: 'query'), + sort: any(named: 'sort'), + messageFilters: any(named: 'messageFilters'), + paginationParams: any(named: 'paginationParams'), + )).called(1); final offset = messageResponseList.length; final pagination = PaginationParams(offset: offset); const error = 'Error! Error! Error!'; - when(mockClient.search( - any, - query: anyNamed('query'), - sort: anyNamed('sort'), - messageFilters: anyNamed('messageFilters'), - paginationParams: pagination, - )).thenThrow(error); + when(() => mockClient.search( + testFilter, + query: any(named: 'query'), + sort: any(named: 'sort'), + messageFilters: any(named: 'messageFilters'), + paginationParams: pagination, + )).thenThrow(error); - messageSearchBlocState.search(pagination: pagination); + messageSearchBlocState.search(pagination: pagination, filter: testFilter); await expectLater( messageSearchBlocState.queryMessagesLoading, emitsError(error), ); - verify(mockClient.search( - any, - query: anyNamed('query'), - sort: anyNamed('sort'), - messageFilters: anyNamed('messageFilters'), - paginationParams: pagination, - )).called(1); + verify(() => mockClient.search( + testFilter, + query: any(named: 'query'), + sort: any(named: 'sort'), + messageFilters: any(named: 'messageFilters'), + paginationParams: pagination, + )).called(1); }, ); } diff --git a/packages/stream_chat_flutter_core/test/message_search_list_core_test.dart b/packages/stream_chat_flutter_core/test/message_search_list_core_test.dart index aa4a47f9..1f7414d0 100644 --- a/packages/stream_chat_flutter_core/test/message_search_list_core_test.dart +++ b/packages/stream_chat_flutter_core/test/message_search_list_core_test.dart @@ -1,83 +1,32 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:mockito/mockito.dart'; -import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; +import 'package:mocktail/mocktail.dart'; import 'package:stream_chat_flutter_core/src/message_search_list_core.dart'; +import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; import 'mocks.dart'; +final testFilter = Filter.custom(operator: '\$test', value: 'testValue'); + void main() { List _generateMessages({ int count = 3, int offset = 0, - }) { - return List.generate( - count, - (index) { - index = index + offset; - return GetMessageResponse() - ..message = Message( - id: 'testId$index', - text: 'testTextData$index', - ) - ..channel = ChannelModel( - cid: 'testCid', - ); - }, - ); - } - - test( - 'should throw assertion error in case childBuilder is null', - () { - final messageSearchListCore = () => MessageSearchListCore( - childBuilder: null, - loadingBuilder: (BuildContext context) => Offstage(), - emptyBuilder: (BuildContext context) => Offstage(), - errorBuilder: (BuildContext context, Object error) => Offstage(), - ); - expect(messageSearchListCore, throwsA(isA())); - }, - ); - - test( - 'should throw assertion error in case loadingBuilder is null', - () { - final messageSearchListCore = () => MessageSearchListCore( - childBuilder: (List messages) => Offstage(), - loadingBuilder: null, - emptyBuilder: (BuildContext context) => Offstage(), - errorBuilder: (BuildContext context, Object error) => Offstage(), - ); - expect(messageSearchListCore, throwsA(isA())); - }, - ); - - test( - 'should throw assertion error in case emptyBuilder is null', - () { - final messageSearchListCore = () => MessageSearchListCore( - childBuilder: (List messages) => Offstage(), - loadingBuilder: (BuildContext context) => Offstage(), - emptyBuilder: null, - errorBuilder: (BuildContext context, Object error) => Offstage(), - ); - expect(messageSearchListCore, throwsA(isA())); - }, - ); - - test( - 'should throw assertion error in case errorBuilder is null', - () { - final messageSearchListCore = () => MessageSearchListCore( - childBuilder: (List messages) => Offstage(), - loadingBuilder: (BuildContext context) => Offstage(), - emptyBuilder: (BuildContext context) => Offstage(), - errorBuilder: null, - ); - expect(messageSearchListCore, throwsA(isA())); - }, - ); + }) => + List.generate( + count, + (index) { + index = index + offset; + return GetMessageResponse() + ..message = Message( + id: 'testId$index', + text: 'testTextData$index', + ) + ..channel = ChannelModel( + cid: 'test:Cid', + ); + }, + ); testWidgets( 'should throw if MessageSearchListCore is used where MessageSearchBloc ' @@ -86,10 +35,11 @@ void main() { const messageSearchListCoreKey = Key('messageSearchListCore'); final messageSearchListCore = MessageSearchListCore( key: messageSearchListCoreKey, - childBuilder: (List messages) => Offstage(), - loadingBuilder: (BuildContext context) => Offstage(), - emptyBuilder: (BuildContext context) => Offstage(), - errorBuilder: (BuildContext context, Object error) => Offstage(), + childBuilder: (List? messages) => const Offstage(), + loadingBuilder: (BuildContext context) => const Offstage(), + emptyBuilder: (BuildContext context) => const Offstage(), + errorBuilder: (BuildContext context, Object? error) => const Offstage(), + filters: testFilter, ); await tester.pumpWidget(messageSearchListCore); @@ -109,7 +59,8 @@ void main() { childBuilder: (List messages) => Offstage(), loadingBuilder: (BuildContext context) => Offstage(), emptyBuilder: (BuildContext context) => Offstage(), - errorBuilder: (BuildContext context, Object error) => Offstage(), + errorBuilder: (BuildContext context, Object? error) => Offstage(), + filters: testFilter, ); final mockClient = MockClient(); @@ -140,6 +91,7 @@ void main() { emptyBuilder: (BuildContext context) => Offstage(), errorBuilder: (BuildContext context, Object error) => Offstage(), messageSearchListController: controller, + filters: testFilter, ); expect(controller.loadData, isNull); @@ -175,18 +127,19 @@ void main() { errorBuilder: (BuildContext context, Object error) => Offstage( key: errorWidgetKey, ), + filters: testFilter, ); final mockClient = MockClient(); const error = 'Error! Error! Error!'; - when(mockClient.search( - any, - query: anyNamed('query'), - sort: anyNamed('sort'), - messageFilters: anyNamed('messageFilters'), - paginationParams: anyNamed('paginationParams'), - )).thenThrow(error); + when(() => mockClient.search( + testFilter, + query: any(named: 'query'), + sort: any(named: 'sort'), + messageFilters: any(named: 'messageFilters'), + paginationParams: any(named: 'paginationParams'), + )).thenThrow(error); await tester.pumpWidget( StreamChatCore( @@ -201,13 +154,13 @@ void main() { expect(find.byKey(errorWidgetKey), findsOneWidget); - verify(mockClient.search( - any, - query: anyNamed('query'), - sort: anyNamed('sort'), - messageFilters: anyNamed('messageFilters'), - paginationParams: anyNamed('paginationParams'), - )).called(1); + verify(() => mockClient.search( + testFilter, + query: any(named: 'query'), + sort: any(named: 'sort'), + messageFilters: any(named: 'messageFilters'), + paginationParams: any(named: 'paginationParams'), + )).called(1); }, ); @@ -223,18 +176,19 @@ void main() { loadingBuilder: (BuildContext context) => Offstage(), emptyBuilder: (BuildContext context) => Offstage(key: emptyWidgetKey), errorBuilder: (BuildContext context, Object error) => Offstage(), + filters: testFilter, ); final mockClient = MockClient(); final messageResponseList = []; - when(mockClient.search( - any, - query: anyNamed('query'), - sort: anyNamed('sort'), - messageFilters: anyNamed('messageFilters'), - paginationParams: anyNamed('paginationParams'), - )).thenAnswer( + when(() => mockClient.search( + testFilter, + query: any(named: 'query'), + sort: any(named: 'sort'), + messageFilters: any(named: 'messageFilters'), + paginationParams: any(named: 'paginationParams'), + )).thenAnswer( (_) async => SearchMessagesResponse()..results = messageResponseList, ); @@ -251,13 +205,13 @@ void main() { expect(find.byKey(emptyWidgetKey), findsOneWidget); - verify(mockClient.search( - any, - query: anyNamed('query'), - sort: anyNamed('sort'), - messageFilters: anyNamed('messageFilters'), - paginationParams: anyNamed('paginationParams'), - )).called(1); + verify(() => mockClient.search( + testFilter, + query: any(named: 'query'), + sort: any(named: 'sort'), + messageFilters: any(named: 'messageFilters'), + paginationParams: any(named: 'paginationParams'), + )).called(1); }, ); @@ -274,18 +228,19 @@ void main() { loadingBuilder: (BuildContext context) => Offstage(), emptyBuilder: (BuildContext context) => Offstage(), errorBuilder: (BuildContext context, Object error) => Offstage(), + filters: testFilter, ); final mockClient = MockClient(); final messageResponseList = _generateMessages(); - when(mockClient.search( - any, - query: anyNamed('query'), - sort: anyNamed('sort'), - messageFilters: anyNamed('messageFilters'), - paginationParams: anyNamed('paginationParams'), - )).thenAnswer( + when(() => mockClient.search( + testFilter, + query: any(named: 'query'), + sort: any(named: 'sort'), + messageFilters: any(named: 'messageFilters'), + paginationParams: any(named: 'paginationParams'), + )).thenAnswer( (_) async => SearchMessagesResponse()..results = messageResponseList, ); @@ -302,13 +257,13 @@ void main() { expect(find.byKey(childWidgetKey), findsOneWidget); - verify(mockClient.search( - any, - query: anyNamed('query'), - sort: anyNamed('sort'), - messageFilters: anyNamed('messageFilters'), - paginationParams: anyNamed('paginationParams'), - )).called(1); + verify(() => mockClient.search( + testFilter, + query: any(named: 'query'), + sort: any(named: 'sort'), + messageFilters: any(named: 'messageFilters'), + paginationParams: any(named: 'paginationParams'), + )).called(1); }, ); @@ -324,25 +279,26 @@ void main() { childBuilder: (List messages) => Container( key: childWidgetKey, child: Text( - messages.map((e) => '${e.channel.cid}-${e.message.id}').join(','), + messages.map((e) => '${e.channel?.cid}-${e.message.id}').join(','), ), ), loadingBuilder: (BuildContext context) => Offstage(), emptyBuilder: (BuildContext context) => Offstage(), errorBuilder: (BuildContext context, Object error) => Offstage(), paginationParams: pagination, + filters: testFilter, ); final mockClient = MockClient(); final messageResponseList = _generateMessages(); - when(mockClient.search( - any, - query: anyNamed('query'), - sort: anyNamed('sort'), - messageFilters: anyNamed('messageFilters'), - paginationParams: pagination, - )).thenAnswer( + when(() => mockClient.search( + testFilter, + query: any(named: 'query'), + sort: any(named: 'sort'), + messageFilters: any(named: 'messageFilters'), + paginationParams: pagination, + )).thenAnswer( (_) async => SearchMessagesResponse()..results = messageResponseList, ); @@ -364,19 +320,19 @@ void main() { expect( find.text( messageResponseList - .map((e) => '${e.channel.cid}-${e.message.id}') + .map((e) => '${e.channel?.cid}-${e.message.id}') .join(','), ), findsOneWidget, ); - verify(mockClient.search( - any, - query: anyNamed('query'), - sort: anyNamed('sort'), - messageFilters: anyNamed('messageFilters'), - paginationParams: pagination, - )).called(1); + verify(() => mockClient.search( + testFilter, + query: any(named: 'query'), + sort: any(named: 'sort'), + messageFilters: any(named: 'messageFilters'), + paginationParams: pagination, + )).called(1); final messageSearchListCoreState = tester.state( @@ -386,13 +342,13 @@ void main() { final offset = messageResponseList.length; final paginatedMessageResponseList = _generateMessages(offset: offset); final updatedPagination = pagination.copyWith(offset: offset); - when(mockClient.search( - any, - query: anyNamed('query'), - sort: anyNamed('sort'), - messageFilters: anyNamed('messageFilters'), - paginationParams: updatedPagination, - )).thenAnswer( + when(() => mockClient.search( + testFilter, + query: any(named: 'query'), + sort: any(named: 'sort'), + messageFilters: any(named: 'messageFilters'), + paginationParams: updatedPagination, + )).thenAnswer( (_) async => SearchMessagesResponse()..results = paginatedMessageResponseList, ); @@ -406,17 +362,17 @@ void main() { find.text([ ...messageResponseList, ...paginatedMessageResponseList, - ].map((e) => '${e.channel.cid}-${e.message.id}').join(',')), + ].map((e) => '${e.channel?.cid}-${e.message.id}').join(',')), findsOneWidget, ); - verify(mockClient.search( - any, - query: anyNamed('query'), - sort: anyNamed('sort'), - messageFilters: anyNamed('messageFilters'), - paginationParams: updatedPagination, - )).called(1); + verify(() => mockClient.search( + testFilter, + query: any(named: 'query'), + sort: any(named: 'sort'), + messageFilters: any(named: 'messageFilters'), + paginationParams: updatedPagination, + )).called(1); }, ); @@ -426,8 +382,8 @@ void main() { (tester) async { const pagination = PaginationParams(); - StateSetter _stateSetter; - int limit = pagination.limit; + StateSetter? _stateSetter; + var limit = pagination.limit; const messageSearchListCoreKey = Key('messageSearchListCore'); const childWidgetKey = Key('childWidget'); @@ -438,7 +394,7 @@ void main() { key: childWidgetKey, child: Text( messages - .map((e) => '${e.channel.cid}-${e.message.id}') + .map((e) => '${e.channel?.cid}-${e.message.id}') .join(','), ), ), @@ -446,18 +402,19 @@ void main() { emptyBuilder: (BuildContext context) => Offstage(), errorBuilder: (BuildContext context, Object error) => Offstage(), paginationParams: pagination.copyWith(limit: limit), + filters: testFilter, ); final mockClient = MockClient(); final messageResponseList = _generateMessages(); - when(mockClient.search( - any, - query: anyNamed('query'), - sort: anyNamed('sort'), - messageFilters: anyNamed('messageFilters'), - paginationParams: pagination, - )).thenAnswer( + when(() => mockClient.search( + testFilter, + query: any(named: 'query'), + sort: any(named: 'sort'), + messageFilters: any(named: 'messageFilters'), + paginationParams: pagination, + )).thenAnswer( (_) async => SearchMessagesResponse()..results = messageResponseList, ); @@ -483,32 +440,32 @@ void main() { expect( find.text( messageResponseList - .map((e) => '${e.channel.cid}-${e.message.id}') + .map((e) => '${e.channel?.cid}-${e.message.id}') .join(','), ), findsOneWidget, ); - verify(mockClient.search( - any, - query: anyNamed('query'), - sort: anyNamed('sort'), - messageFilters: anyNamed('messageFilters'), - paginationParams: pagination, - )).called(1); + verify(() => mockClient.search( + testFilter, + query: any(named: 'query'), + sort: any(named: 'sort'), + messageFilters: any(named: 'messageFilters'), + paginationParams: pagination, + )).called(1); // Rebuilding MessageSearchListCore with new pagination limit - _stateSetter(() => limit = 6); + _stateSetter?.call(() => limit = 6); final updatedMessageResponseList = _generateMessages(count: limit); final updatedPagination = pagination.copyWith(limit: limit); - when(mockClient.search( - any, - query: anyNamed('query'), - sort: anyNamed('sort'), - messageFilters: anyNamed('messageFilters'), - paginationParams: updatedPagination, - )).thenAnswer( + when(() => mockClient.search( + testFilter, + query: any(named: 'query'), + sort: any(named: 'sort'), + messageFilters: any(named: 'messageFilters'), + paginationParams: updatedPagination, + )).thenAnswer( (_) async => SearchMessagesResponse()..results = updatedMessageResponseList, ); @@ -518,18 +475,18 @@ void main() { expect(find.byKey(childWidgetKey), findsOneWidget); expect( find.text(updatedMessageResponseList - .map((e) => '${e.channel.cid}-${e.message.id}') + .map((e) => '${e.channel?.cid}-${e.message.id}') .join(',')), findsOneWidget, ); - verify(mockClient.search( - any, - query: anyNamed('query'), - sort: anyNamed('sort'), - messageFilters: anyNamed('messageFilters'), - paginationParams: updatedPagination, - )).called(1); + verify(() => mockClient.search( + testFilter, + query: any(named: 'query'), + sort: any(named: 'sort'), + messageFilters: any(named: 'messageFilters'), + paginationParams: updatedPagination, + )).called(1); }, ); } diff --git a/packages/stream_chat_flutter_core/test/mocks.dart b/packages/stream_chat_flutter_core/test/mocks.dart index a46b7b5c..6ca24363 100644 --- a/packages/stream_chat_flutter_core/test/mocks.dart +++ b/packages/stream_chat_flutter_core/test/mocks.dart @@ -1,19 +1,20 @@ -import 'package:mockito/mockito.dart'; +import 'package:mocktail/mocktail.dart'; import 'package:stream_chat/stream_chat.dart'; class MockLogger extends Mock implements Logger {} class MockClient extends Mock implements StreamChatClient { + @override final Logger logger = MockLogger(); - ClientState _state; + ClientState? _state; @override ClientState get state => _state ??= MockClientState(); } class MockClientState extends Mock implements ClientState { - OwnUser _user; + OwnUser? _user; @override OwnUser get user => _user ??= OwnUser( @@ -25,12 +26,12 @@ class MockClientState extends Mock implements ClientState { } class MockChannel extends Mock implements Channel { - ChannelClientState _state; + ChannelClientState? _state; @override ChannelClientState get state => _state ??= MockChannelState(); - StreamChatClient _client; + StreamChatClient? _client; @override StreamChatClient get client => _client ??= MockClient(); diff --git a/packages/stream_chat_flutter_core/test/stream_channel_test.dart b/packages/stream_chat_flutter_core/test/stream_channel_test.dart index 4387a918..b48c02b1 100644 --- a/packages/stream_chat_flutter_core/test/stream_channel_test.dart +++ b/packages/stream_chat_flutter_core/test/stream_channel_test.dart @@ -3,7 +3,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:mockito/mockito.dart'; +import 'package:mocktail/mocktail.dart'; import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; import 'mocks.dart'; @@ -62,39 +62,13 @@ void main() { return threads ? threadMessages : messages; } - test( - 'should throw assertion error if child is null', - () async { - final mockChannel = MockChannel(); - const streamChannelKey = Key('streamChannel'); - final streamChannel = () => StreamChannel( - key: streamChannelKey, - channel: mockChannel, - child: null, - ); - expect(streamChannel, throwsA(isA())); - }, - ); - - test( - 'should throw assertion error if channel is null', - () async { - const streamChannelKey = Key('streamChannel'); - final streamChannel = () => StreamChannel( - key: streamChannelKey, - child: Offstage(), - channel: null, - ); - expect(streamChannel, throwsA(isA())); - }, - ); - testWidgets( 'should render StreamChannel if both channel and child is provided', (tester) async { final mockChannel = MockChannel(); const streamChannelKey = Key('streamChannel'); const childKey = Key('childKey'); + when(() => mockChannel.initialized).thenAnswer((_) => Future.value(true)); final streamChannel = StreamChannel( key: streamChannelKey, channel: mockChannel, @@ -121,8 +95,13 @@ void main() { ); final errorMessage = 'Error! Error! Error!'; - final error = DioError(type: DioErrorType.RESPONSE, error: errorMessage); - when(mockChannel.initialized).thenAnswer((_) => Future.error(error)); + final error = DioError( + type: DioErrorType.response, + error: errorMessage, + requestOptions: RequestOptions(path: ''), + ); + when(() => mockChannel.initialized) + .thenAnswer((_) => Future.error(error)); await tester.pumpWidget( Directionality( @@ -135,7 +114,7 @@ void main() { expect(find.text(errorMessage), findsOneWidget); - verify(mockChannel.initialized).called(1); + verify(() => mockChannel.initialized).called(1); }, ); @@ -153,7 +132,7 @@ void main() { showLoading: true, ); - when(mockChannel.initialized).thenAnswer((_) async => false); + when(() => mockChannel.initialized).thenAnswer((_) async => false); await tester.pumpWidget( Directionality( @@ -166,7 +145,7 @@ void main() { expect(find.byType(CircularProgressIndicator), findsOneWidget); - verify(mockChannel.initialized).called(1); + verify(() => mockChannel.initialized).called(1); }, ); @@ -183,15 +162,15 @@ void main() { initialMessageId: 'testInitialMessageId', ); - when(mockChannel.initialized).thenAnswer((_) async => true); + when(() => mockChannel.initialized).thenAnswer((_) async => true); final messages = _generateMessages(); - when(mockChannel.query( - options: anyNamed('options'), - messagesPagination: anyNamed('messagesPagination'), - membersPagination: anyNamed('membersPagination'), - watchersPagination: anyNamed('watchersPagination'), - preferOffline: anyNamed('preferOffline'), - )).thenAnswer((_) async => ChannelState(messages: messages)); + when(() => mockChannel.query( + options: any(named: 'options'), + messagesPagination: any(named: 'messagesPagination'), + membersPagination: any(named: 'membersPagination'), + watchersPagination: any(named: 'watchersPagination'), + preferOffline: any(named: 'preferOffline'), + )).thenAnswer((_) async => ChannelState(messages: messages)); await tester.pumpWidget( Directionality( @@ -202,14 +181,14 @@ void main() { await tester.pumpAndSettle(); - verify(mockChannel.initialized).called(1); - verify(mockChannel.query( - options: anyNamed('options'), - messagesPagination: anyNamed('messagesPagination'), - membersPagination: anyNamed('membersPagination'), - watchersPagination: anyNamed('watchersPagination'), - preferOffline: anyNamed('preferOffline'), - )).called( + verify(() => mockChannel.initialized).called(1); + verify(() => mockChannel.query( + options: any(named: 'options'), + messagesPagination: any(named: 'messagesPagination'), + membersPagination: any(named: 'membersPagination'), + watchersPagination: any(named: 'watchersPagination'), + preferOffline: any(named: 'preferOffline'), + )).called( 2, // Fetching After messages + Fetching Before messages, ); }, @@ -219,7 +198,7 @@ void main() { 'should rebuild StreamChannel with updated widget data ' 'on calling setState()', (tester) async { - StateSetter _stateSetter; + StateSetter? _stateSetter; var initialMessageId = 'testInitialMessageId'; @@ -244,25 +223,25 @@ void main() { limit: 20, ); - when(mockChannel.initialized).thenAnswer((_) async => true); + when(() => mockChannel.initialized).thenAnswer((_) async => true); final messages = _generateMessages(); - when(mockChannel.query( - options: anyNamed('options'), - messagesPagination: beforePagination, - membersPagination: anyNamed('membersPagination'), - watchersPagination: anyNamed('watchersPagination'), - preferOffline: anyNamed('preferOffline'), - )).thenAnswer((_) async => ChannelState(messages: messages)); + when(() => mockChannel.query( + options: any(named: 'options'), + messagesPagination: beforePagination, + membersPagination: any(named: 'membersPagination'), + watchersPagination: any(named: 'watchersPagination'), + preferOffline: any(named: 'preferOffline'), + )).thenAnswer((_) async => ChannelState(messages: messages)); - when(mockChannel.query( - options: anyNamed('options'), - messagesPagination: afterPagination, - membersPagination: anyNamed('membersPagination'), - watchersPagination: anyNamed('watchersPagination'), - preferOffline: anyNamed('preferOffline'), - )).thenAnswer((_) async => ChannelState(messages: messages)); + when(() => mockChannel.query( + options: any(named: 'options'), + messagesPagination: afterPagination, + membersPagination: any(named: 'membersPagination'), + watchersPagination: any(named: 'watchersPagination'), + preferOffline: any(named: 'preferOffline'), + )).thenAnswer((_) async => ChannelState(messages: messages)); await tester.pumpWidget( Directionality( @@ -279,23 +258,23 @@ void main() { await tester.pumpAndSettle(); - verify(mockChannel.query( - options: anyNamed('options'), - messagesPagination: beforePagination, - membersPagination: anyNamed('membersPagination'), - watchersPagination: anyNamed('watchersPagination'), - preferOffline: anyNamed('preferOffline'), - )).called(1); + verify(() => mockChannel.query( + options: any(named: 'options'), + messagesPagination: beforePagination, + membersPagination: any(named: 'membersPagination'), + watchersPagination: any(named: 'watchersPagination'), + preferOffline: any(named: 'preferOffline'), + )).called(1); - verify(mockChannel.query( - options: anyNamed('options'), - messagesPagination: afterPagination, - membersPagination: anyNamed('membersPagination'), - watchersPagination: anyNamed('watchersPagination'), - preferOffline: anyNamed('preferOffline'), - )).called(1); + verify(() => mockChannel.query( + options: any(named: 'options'), + messagesPagination: afterPagination, + membersPagination: any(named: 'membersPagination'), + watchersPagination: any(named: 'watchersPagination'), + preferOffline: any(named: 'preferOffline'), + )).called(1); - _stateSetter(() => initialMessageId = 'testInitialMessageId2'); + _stateSetter?.call(() => initialMessageId = 'testInitialMessageId2'); final updatedBeforePagination = beforePagination.copyWith( lessThan: initialMessageId, @@ -305,39 +284,39 @@ void main() { greaterThanOrEqual: initialMessageId, ); - when(mockChannel.query( - options: anyNamed('options'), - messagesPagination: updatedBeforePagination, - membersPagination: anyNamed('membersPagination'), - watchersPagination: anyNamed('watchersPagination'), - preferOffline: anyNamed('preferOffline'), - )).thenAnswer((_) async => ChannelState(messages: messages)); + when(() => mockChannel.query( + options: any(named: 'options'), + messagesPagination: updatedBeforePagination, + membersPagination: any(named: 'membersPagination'), + watchersPagination: any(named: 'watchersPagination'), + preferOffline: any(named: 'preferOffline'), + )).thenAnswer((_) async => ChannelState(messages: messages)); - when(mockChannel.query( - options: anyNamed('options'), - messagesPagination: updatedAfterPagination, - membersPagination: anyNamed('membersPagination'), - watchersPagination: anyNamed('watchersPagination'), - preferOffline: anyNamed('preferOffline'), - )).thenAnswer((_) async => ChannelState(messages: messages)); + when(() => mockChannel.query( + options: any(named: 'options'), + messagesPagination: updatedAfterPagination, + membersPagination: any(named: 'membersPagination'), + watchersPagination: any(named: 'watchersPagination'), + preferOffline: any(named: 'preferOffline'), + )).thenAnswer((_) async => ChannelState(messages: messages)); await tester.pumpAndSettle(); - verify(mockChannel.query( - options: anyNamed('options'), - messagesPagination: updatedBeforePagination, - membersPagination: anyNamed('membersPagination'), - watchersPagination: anyNamed('watchersPagination'), - preferOffline: anyNamed('preferOffline'), - )).called(1); + verify(() => mockChannel.query( + options: any(named: 'options'), + messagesPagination: updatedBeforePagination, + membersPagination: any(named: 'membersPagination'), + watchersPagination: any(named: 'watchersPagination'), + preferOffline: any(named: 'preferOffline'), + )).called(1); - verify(mockChannel.query( - options: anyNamed('options'), - messagesPagination: updatedAfterPagination, - membersPagination: anyNamed('membersPagination'), - watchersPagination: anyNamed('watchersPagination'), - preferOffline: anyNamed('preferOffline'), - )).called(1); + verify(() => mockChannel.query( + options: any(named: 'options'), + messagesPagination: updatedAfterPagination, + membersPagination: any(named: 'membersPagination'), + watchersPagination: any(named: 'watchersPagination'), + preferOffline: any(named: 'preferOffline'), + )).called(1); }, ); } diff --git a/packages/stream_chat_flutter_core/test/stream_chat_core_test.dart b/packages/stream_chat_flutter_core/test/stream_chat_core_test.dart index 4adc0062..bc5324b8 100644 --- a/packages/stream_chat_flutter_core/test/stream_chat_core_test.dart +++ b/packages/stream_chat_flutter_core/test/stream_chat_core_test.dart @@ -2,7 +2,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:mockito/mockito.dart'; +import 'package:mocktail/mocktail.dart'; import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; import 'mocks.dart'; @@ -12,29 +12,6 @@ class MockOnBackgroundEventReceived extends Mock { } void main() { - test( - 'should throw assertion error in case client is null', - () { - final streamChatCore = () => StreamChatCore( - client: null, - child: Offstage(), - ); - expect(streamChatCore, throwsA(isA())); - }, - ); - - test( - 'should throw assertion error in case child is null', - () { - final mockClient = MockClient(); - final streamChatCore = () => StreamChatCore( - client: mockClient, - child: null, - ); - expect(streamChatCore, throwsA(isA())); - }, - ); - testWidgets( 'should render StreamChatCore if both client and child is provided', (tester) async { @@ -92,7 +69,7 @@ void main() { expect(find.byKey(streamChatCoreKey), findsOneWidget); expect(find.byKey(childKey), findsOneWidget); - when(mockClient.disconnect()).thenAnswer((_) async { + when(() => mockClient.disconnect()).thenAnswer((_) async { return; }); @@ -102,7 +79,7 @@ void main() { streamChatCoreState.didChangeAppLifecycleState(AppLifecycleState.paused); - verify(mockClient.disconnect()).called(1); + verify(() => mockClient.disconnect()).called(1); }, ); @@ -131,8 +108,8 @@ void main() { expect(find.byKey(childKey), findsOneWidget); final event = Event(); - when(mockClient.on()).thenAnswer((_) => Stream.value(event)); - when(mockClient.disconnect()).thenAnswer((_) async { + when(() => mockClient.on()).thenAnswer((_) => Stream.value(event)); + when(() => mockClient.disconnect()).thenAnswer((_) async { return; }); @@ -143,14 +120,14 @@ void main() { streamChatCoreState .didChangeAppLifecycleState(AppLifecycleState.paused); - await untilCalled(mockOnBackgroundEventReceived.call(event)); + await untilCalled(() => mockOnBackgroundEventReceived.call(event)); - verify(mockOnBackgroundEventReceived.call(event)).called(1); + verify(() => mockOnBackgroundEventReceived.call(event)).called(1); await Future.delayed(backgroundKeepAlive); - verify(mockClient.disconnect()).called(1); - verifyNever(mockOnBackgroundEventReceived.call(event)); + verify(() => mockClient.disconnect()).called(1); + verifyNever(() => mockOnBackgroundEventReceived.call(event)); }); }, ); @@ -180,7 +157,7 @@ void main() { expect(find.byKey(childKey), findsOneWidget); final event = Event(); - when(mockClient.on()).thenAnswer((_) => Stream.value(event)); + when(() => mockClient.on()).thenAnswer((_) => Stream.value(event)); final streamChatCoreState = tester.state( find.byKey(streamChatCoreKey), @@ -189,14 +166,14 @@ void main() { streamChatCoreState .didChangeAppLifecycleState(AppLifecycleState.paused); - await untilCalled(mockOnBackgroundEventReceived.call(event)); + await untilCalled(() => mockOnBackgroundEventReceived.call(event)); - verify(mockOnBackgroundEventReceived.call(event)).called(1); + verify(() => mockOnBackgroundEventReceived.call(event)).called(1); streamChatCoreState .didChangeAppLifecycleState(AppLifecycleState.resumed); - verifyNever(mockOnBackgroundEventReceived.call(event)); + verifyNever(() => mockOnBackgroundEventReceived.call(event)); }); }, ); @@ -222,9 +199,10 @@ void main() { expect(find.byKey(childKey), findsOneWidget); final event = Event(); - when(mockClient.on()).thenAnswer((_) => Stream.value(event)); - when(mockClient.connect()).thenAnswer((_) async => event); - when(mockClient.wsConnectionStatus) + when(() => mockClient.on()).thenAnswer((_) => Stream.value(event)); + when(() => mockClient.connect()).thenAnswer((_) async => event); + when(mockClient.disconnect).thenAnswer((_) async => null); + when(() => mockClient.wsConnectionStatus) .thenReturn(ConnectionStatus.disconnected); final streamChatCoreState = tester.state( @@ -239,7 +217,7 @@ void main() { streamChatCoreState .didChangeAppLifecycleState(AppLifecycleState.resumed); - verify(mockClient.connect()).called(1); + verify(() => mockClient.connect()).called(1); }); }, ); @@ -265,7 +243,7 @@ void main() { expect(find.byKey(streamChatCoreKey), findsOneWidget); expect(find.byKey(childKey), findsOneWidget); - when(mockClient.state.userStream) + when(() => mockClient.state.userStream) .thenAnswer((_) => userController.stream); final streamChatCoreState = tester.state( diff --git a/packages/stream_chat_flutter_core/test/user_list_core_test.dart b/packages/stream_chat_flutter_core/test/user_list_core_test.dart index d1c7e83a..a0f8bd51 100644 --- a/packages/stream_chat_flutter_core/test/user_list_core_test.dart +++ b/packages/stream_chat_flutter_core/test/user_list_core_test.dart @@ -1,6 +1,6 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:mockito/mockito.dart'; +import 'package:mocktail/mocktail.dart'; import 'package:stream_chat_flutter_core/src/user_list_core.dart'; import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; @@ -33,58 +33,6 @@ void main() { ); } - test( - 'should throw assertion error in case listBuilder is null', - () { - final userListCore = () => UserListCore( - listBuilder: null, - loadingBuilder: (BuildContext context) => Offstage(), - emptyBuilder: (BuildContext context) => Offstage(), - errorBuilder: (Object error) => Offstage(), - ); - expect(userListCore, throwsA(isA())); - }, - ); - - test( - 'should throw assertion error in case loadingBuilder is null', - () { - final userListCore = () => UserListCore( - listBuilder: (_, __) => Offstage(), - loadingBuilder: null, - emptyBuilder: (BuildContext context) => Offstage(), - errorBuilder: (Object error) => Offstage(), - ); - expect(userListCore, throwsA(isA())); - }, - ); - - test( - 'should throw assertion error in case emptyBuilder is null', - () { - final userListCore = () => UserListCore( - listBuilder: (_, __) => Offstage(), - loadingBuilder: (BuildContext context) => Offstage(), - emptyBuilder: null, - errorBuilder: (Object error) => Offstage(), - ); - expect(userListCore, throwsA(isA())); - }, - ); - - test( - 'should throw assertion error in case errorBuilder is null', - () { - final userListCore = () => UserListCore( - listBuilder: (_, __) => Offstage(), - loadingBuilder: (BuildContext context) => Offstage(), - emptyBuilder: (BuildContext context) => Offstage(), - errorBuilder: null, - ); - expect(userListCore, throwsA(isA())); - }, - ); - testWidgets( 'should throw if UserListCore is used where UsersBloc is not present ' 'in the widget tree', @@ -183,12 +131,12 @@ void main() { final mockClient = MockClient(); const error = 'Error! Error! Error!'; - when(mockClient.queryUsers( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - pagination: anyNamed('pagination'), - )).thenThrow(error); + when(() => mockClient.queryUsers( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + pagination: any(named: 'pagination'), + )).thenThrow(error); await tester.pumpWidget( StreamChatCore( @@ -203,12 +151,12 @@ void main() { expect(find.byKey(errorWidgetKey), findsOneWidget); - verify(mockClient.queryUsers( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - pagination: anyNamed('pagination'), - )).called(1); + verify(() => mockClient.queryUsers( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + pagination: any(named: 'pagination'), + )).called(1); }, ); @@ -228,12 +176,12 @@ void main() { final mockClient = MockClient(); const users = []; - when(mockClient.queryUsers( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - pagination: anyNamed('pagination'), - )).thenAnswer((_) async => QueryUsersResponse()..users = users); + when(() => mockClient.queryUsers( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + pagination: any(named: 'pagination'), + )).thenAnswer((_) async => QueryUsersResponse()..users = users); await tester.pumpWidget( StreamChatCore( @@ -248,12 +196,12 @@ void main() { expect(find.byKey(emptyWidgetKey), findsOneWidget); - verify(mockClient.queryUsers( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - pagination: anyNamed('pagination'), - )).called(1); + verify(() => mockClient.queryUsers( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + pagination: any(named: 'pagination'), + )).called(1); }, ); @@ -273,12 +221,12 @@ void main() { final mockClient = MockClient(); final users = _generateUsers(); - when(mockClient.queryUsers( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - pagination: anyNamed('pagination'), - )).thenAnswer((_) async => QueryUsersResponse()..users = users); + when(() => mockClient.queryUsers( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + pagination: any(named: 'pagination'), + )).thenAnswer((_) async => QueryUsersResponse()..users = users); await tester.pumpWidget( StreamChatCore( @@ -293,12 +241,12 @@ void main() { expect(find.byKey(listWidgetKey), findsOneWidget); - verify(mockClient.queryUsers( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - pagination: anyNamed('pagination'), - )).called(1); + verify(() => mockClient.queryUsers( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + pagination: any(named: 'pagination'), + )).called(1); }, ); @@ -314,7 +262,7 @@ void main() { child: ListView( children: items.map((e) { return Container( - key: Key(e.key), + key: Key(e.key ?? ''), child: e.when( headerItem: (heading) => Text(heading), userItem: (user) => Text(user.id), @@ -332,12 +280,12 @@ void main() { final mockClient = MockClient(); final users = _generateUsers(); - when(mockClient.queryUsers( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - pagination: anyNamed('pagination'), - )).thenAnswer((_) async => QueryUsersResponse()..users = users); + when(() => mockClient.queryUsers( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + pagination: any(named: 'pagination'), + )).thenAnswer((_) async => QueryUsersResponse()..users = users); await tester.pumpWidget( Directionality( @@ -359,12 +307,12 @@ void main() { expect(find.byKey(Key('USER-${user.id}')), findsOneWidget); } - verify(mockClient.queryUsers( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - pagination: anyNamed('pagination'), - )).called(1); + verify(() => mockClient.queryUsers( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + pagination: any(named: 'pagination'), + )).called(1); }, ); @@ -382,7 +330,7 @@ void main() { child: ListView( children: items.map((e) { return Container( - key: Key(e.key), + key: Key(e.key ?? ''), child: e.when( headerItem: (heading) => Text(heading), userItem: (user) => Text(user.id), @@ -401,12 +349,12 @@ void main() { final mockClient = MockClient(); final users = _generateUsers(); - when(mockClient.queryUsers( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - pagination: anyNamed('pagination'), - )).thenAnswer((_) async => QueryUsersResponse()..users = users); + when(() => mockClient.queryUsers( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + pagination: any(named: 'pagination'), + )).thenAnswer((_) async => QueryUsersResponse()..users = users); await tester.pumpWidget( Directionality( @@ -428,12 +376,12 @@ void main() { expect(find.byKey(Key('USER-${user.id}')), findsOneWidget); } - verify(mockClient.queryUsers( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - pagination: anyNamed('pagination'), - )).called(1); + verify(() => mockClient.queryUsers( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + pagination: any(named: 'pagination'), + )).called(1); final userListCoreState = tester.state( find.byKey(userListCoreKey), @@ -442,12 +390,14 @@ void main() { final offset = users.length; final paginatedUsers = _generateUsers(offset: offset); final updatedPagination = pagination.copyWith(offset: offset); - when(mockClient.queryUsers( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - pagination: updatedPagination, - )).thenAnswer((_) async => QueryUsersResponse()..users = paginatedUsers); + when(() => mockClient.queryUsers( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + pagination: updatedPagination, + )) + .thenAnswer( + (_) async => QueryUsersResponse()..users = paginatedUsers); await userListCoreState.paginateData(); @@ -458,12 +408,12 @@ void main() { expect(find.byKey(Key('USER-${user.id}')), findsOneWidget); } - verify(mockClient.queryUsers( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - pagination: updatedPagination, - )).called(1); + verify(() => mockClient.queryUsers( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + pagination: updatedPagination, + )).called(1); }, ); @@ -473,7 +423,7 @@ void main() { (tester) async { const pagination = PaginationParams(); - StateSetter _stateSetter; + StateSetter? _stateSetter; int limit = pagination.limit; const userListCoreKey = Key('userListCore'); @@ -485,7 +435,7 @@ void main() { child: ListView( children: items.map((e) { return Container( - key: Key(e.key), + key: Key(e.key ?? ''), child: e.when( headerItem: (heading) => Text(heading), userItem: (user) => Text(user.id), @@ -504,12 +454,12 @@ void main() { final mockClient = MockClient(); final users = _generateUsers(); - when(mockClient.queryUsers( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - pagination: anyNamed('pagination'), - )).thenAnswer((_) async => QueryUsersResponse()..users = users); + when(() => mockClient.queryUsers( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + pagination: any(named: 'pagination'), + )).thenAnswer((_) async => QueryUsersResponse()..users = users); await tester.pumpWidget( Directionality( @@ -535,24 +485,25 @@ void main() { expect(find.byKey(Key('USER-${user.id}')), findsOneWidget); } - verify(mockClient.queryUsers( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - pagination: anyNamed('pagination'), - )).called(1); + verify(() => mockClient.queryUsers( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + pagination: any(named: 'pagination'), + )).called(1); // Rebuilding UserListCore with new pagination limit - _stateSetter(() => limit = 6); + _stateSetter?.call(() => limit = 6); final updatedUsers = _generateUsers(count: limit); final updatedPagination = pagination.copyWith(limit: limit); - when(mockClient.queryUsers( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - pagination: updatedPagination, - )).thenAnswer((_) async => QueryUsersResponse()..users = updatedUsers); + when(() => mockClient.queryUsers( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + pagination: updatedPagination, + )) + .thenAnswer((_) async => QueryUsersResponse()..users = updatedUsers); await tester.pumpAndSettle(); @@ -561,12 +512,12 @@ void main() { expect(find.byKey(Key('USER-${user.id}')), findsOneWidget); } - verify(mockClient.queryUsers( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - pagination: updatedPagination, - )).called(1); + verify(() => mockClient.queryUsers( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + pagination: updatedPagination, + )).called(1); }, ); } diff --git a/packages/stream_chat_flutter_core/test/users_bloc_test.dart b/packages/stream_chat_flutter_core/test/users_bloc_test.dart index e9ccd2e7..8c348211 100644 --- a/packages/stream_chat_flutter_core/test/users_bloc_test.dart +++ b/packages/stream_chat_flutter_core/test/users_bloc_test.dart @@ -1,9 +1,9 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:mocktail/mocktail.dart'; import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat_flutter_core/src/stream_chat_core.dart'; import 'package:stream_chat_flutter_core/src/users_bloc.dart'; -import 'package:mockito/mockito.dart'; import 'matchers/users_matcher.dart'; import 'mocks.dart'; @@ -31,18 +31,6 @@ void main() { ); } - test( - 'should throw assertion error if child is null', - () async { - const usersBlocKey = Key('usersBloc'); - final usersBloc = () => UsersBloc( - key: usersBlocKey, - child: null, - ); - expect(usersBloc, throwsA(isA())); - }, - ); - testWidgets( 'usersBlocState.queryUsers() should throw if used where ' 'StreamChat is not present in the widget tree', @@ -96,12 +84,12 @@ void main() { final users = _generateUsers(); - when(mockClient.queryUsers( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - pagination: anyNamed('pagination'), - )).thenAnswer((_) async => QueryUsersResponse()..users = users); + when(() => mockClient.queryUsers( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + pagination: any(named: 'pagination'), + )).thenAnswer((_) async => QueryUsersResponse()..users = users); usersBlocState.queryUsers(); @@ -110,12 +98,12 @@ void main() { emits(isSameUserListAs(users)), ); - verify(mockClient.queryUsers( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - pagination: anyNamed('pagination'), - )).called(1); + verify(() => mockClient.queryUsers( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + pagination: any(named: 'pagination'), + )).called(1); }, ); @@ -145,12 +133,12 @@ void main() { final error = 'Error! Error! Error!'; - when(mockClient.queryUsers( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - pagination: anyNamed('pagination'), - )).thenThrow(error); + when(() => mockClient.queryUsers( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + pagination: any(named: 'pagination'), + )).thenThrow(error); usersBlocState.queryUsers(); @@ -159,12 +147,12 @@ void main() { emitsError(error), ); - verify(mockClient.queryUsers( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - pagination: anyNamed('pagination'), - )).called(1); + verify(() => mockClient.queryUsers( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + pagination: any(named: 'pagination'), + )).called(1); }, ); @@ -195,12 +183,12 @@ void main() { final users = _generateUsers(); - when(mockClient.queryUsers( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - pagination: anyNamed('pagination'), - )).thenAnswer((_) async => QueryUsersResponse()..users = users); + when(() => mockClient.queryUsers( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + pagination: any(named: 'pagination'), + )).thenAnswer((_) async => QueryUsersResponse()..users = users); usersBlocState.queryUsers(); @@ -209,23 +197,25 @@ void main() { emits(isSameUserListAs(users)), ); - verify(mockClient.queryUsers( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - pagination: anyNamed('pagination'), - )).called(1); + verify(() => mockClient.queryUsers( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + pagination: any(named: 'pagination'), + )).called(1); final offset = users.length; final paginatedUsers = _generateUsers(offset: offset); final pagination = PaginationParams(offset: offset); - when(mockClient.queryUsers( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - pagination: pagination, - )).thenAnswer((_) async => QueryUsersResponse()..users = paginatedUsers); + when(() => mockClient.queryUsers( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + pagination: pagination, + )) + .thenAnswer( + (_) async => QueryUsersResponse()..users = paginatedUsers); usersBlocState.queryUsers(pagination: pagination); @@ -240,12 +230,12 @@ void main() { ), ]); - verify(mockClient.queryUsers( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - pagination: pagination, - )).called(1); + verify(() => mockClient.queryUsers( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + pagination: pagination, + )).called(1); }, ); @@ -276,12 +266,12 @@ void main() { final users = _generateUsers(); - when(mockClient.queryUsers( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - pagination: anyNamed('pagination'), - )).thenAnswer((_) async => QueryUsersResponse()..users = users); + when(() => mockClient.queryUsers( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + pagination: any(named: 'pagination'), + )).thenAnswer((_) async => QueryUsersResponse()..users = users); usersBlocState.queryUsers(); @@ -290,24 +280,24 @@ void main() { emits(isSameUserListAs(users)), ); - verify(mockClient.queryUsers( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - pagination: anyNamed('pagination'), - )).called(1); + verify(() => mockClient.queryUsers( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + pagination: any(named: 'pagination'), + )).called(1); final offset = users.length; final pagination = PaginationParams(offset: offset); final error = 'Error! Error! Error!'; - when(mockClient.queryUsers( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - pagination: pagination, - )).thenThrow(error); + when(() => mockClient.queryUsers( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + pagination: pagination, + )).thenThrow(error); usersBlocState.queryUsers(pagination: pagination); @@ -316,12 +306,12 @@ void main() { emitsError(error), ); - verify(mockClient.queryUsers( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - pagination: pagination, - )).called(1); + verify(() => mockClient.queryUsers( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + pagination: pagination, + )).called(1); }, ); } diff --git a/packages/stream_chat_persistence/CHANGELOG.md b/packages/stream_chat_persistence/CHANGELOG.md index e4971975..1b5c1dc2 100644 --- a/packages/stream_chat_persistence/CHANGELOG.md +++ b/packages/stream_chat_persistence/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.0.0-nullsafety.0 + +* Migrate this package to null safety +* Update llc dependency + ## 1.5.2 * Fix sorting by last_updated diff --git a/packages/stream_chat_persistence/analysis_options.yaml b/packages/stream_chat_persistence/analysis_options.yaml index 26001160..ff0fc87c 100644 --- a/packages/stream_chat_persistence/analysis_options.yaml +++ b/packages/stream_chat_persistence/analysis_options.yaml @@ -1,11 +1,10 @@ analyzer: - exclude: + exclude: - lib/**/*.g.dart - lib/**/*.freezed.dart - example/* - - test/* -linter: - rules: +linter: + rules: - always_use_package_imports - avoid_empty_else - avoid_relative_lib_imports diff --git a/packages/stream_chat_persistence/example/lib/main.dart b/packages/stream_chat_persistence/example/lib/main.dart index b356d96b..f61bfed0 100644 --- a/packages/stream_chat_persistence/example/lib/main.dart +++ b/packages/stream_chat_persistence/example/lib/main.dart @@ -50,15 +50,16 @@ Future main() async { /// Example using Stream's Low Level Dart client. class StreamExample extends StatelessWidget { - /// To initialize this example, an instance of [client] and [channel] is required. + /// To initialize this example, an instance of + /// [client] and [channel] is required. const StreamExample({ - Key key, - @required this.client, - @required this.channel, + Key? key, + required this.client, + required this.channel, }) : super(key: key); - /// Instance of [StreamChatClient] we created earlier. This contains information about - /// our application and connection state. + /// Instance of [StreamChatClient] we created earlier. + /// This contains information about our application and connection state. final StreamChatClient client; /// The channel we'd like to observe and participate. @@ -77,28 +78,31 @@ class StreamExample extends StatelessWidget { /// containing the channel name and a [MessageView] displaying recent messages. class HomeScreen extends StatelessWidget { /// [HomeScreen] is constructed using the [Channel] we defined earlier. - const HomeScreen({Key key, @required this.channel}) : super(key: key); + const HomeScreen({ + Key? key, + required this.channel, + }) : super(key: key); /// Channel object containing the [Channel.id] we'd like to observe. final Channel channel; @override Widget build(BuildContext context) { - final messages = channel.state.channelStateStream; + final messages = channel.state!.channelStateStream; return Scaffold( appBar: AppBar( title: Text('Channel: ${channel.id}'), ), body: SafeArea( - child: StreamBuilder( + child: StreamBuilder( stream: messages, builder: ( BuildContext context, - AsyncSnapshot snapshot, + AsyncSnapshot snapshot, ) { if (snapshot.hasData && snapshot.data != null) { return MessageView( - messages: snapshot.data.messages.reversed.toList(), + messages: snapshot.data!.messages.reversed.toList(), channel: channel, ); } else if (snapshot.hasError) { @@ -110,8 +114,8 @@ class HomeScreen extends StatelessWidget { } return const Center( child: SizedBox( - width: 100.0, - height: 100.0, + width: 100, + height: 100, child: CircularProgressIndicator(), ), ); @@ -127,9 +131,9 @@ class HomeScreen extends StatelessWidget { class MessageView extends StatefulWidget { /// Message takes the latest list of messages and the current channel. const MessageView({ - Key key, - @required this.messages, - @required this.channel, + Key? key, + required this.messages, + required this.channel, }) : super(key: key); /// List of messages sent in the given channel. @@ -143,8 +147,8 @@ class MessageView extends StatefulWidget { } class _MessageViewState extends State { - TextEditingController _controller; - ScrollController _scrollController; + late final TextEditingController _controller; + late final ScrollController _scrollController; List get _messages => widget.messages; @@ -182,20 +186,20 @@ class _MessageViewState extends State { reverse: true, itemBuilder: (BuildContext context, int index) { final item = _messages[index]; - if (item.user.id == widget.channel.client.uid) { + if (item.user?.id == widget.channel.client.uid) { return Align( alignment: Alignment.centerRight, child: Padding( - padding: const EdgeInsets.all(8.0), - child: Text(item.text), + padding: const EdgeInsets.all(8), + child: Text(item.text ?? ''), ), ); } else { return Align( alignment: Alignment.centerLeft, child: Padding( - padding: const EdgeInsets.all(8.0), - child: Text(item.text), + padding: const EdgeInsets.all(8), + child: Text(item.text ?? ''), ), ); } @@ -203,7 +207,7 @@ class _MessageViewState extends State { ), ), Padding( - padding: const EdgeInsets.all(8.0), + padding: const EdgeInsets.all(8), child: Row( children: [ Expanded( @@ -251,7 +255,8 @@ class _MessageViewState extends State { } } -/// Helper extension for quickly retrieving the current user id from a [StreamChatClient]. +/// Helper extension for quickly retrieving +/// the current user id from a [StreamChatClient]. extension on StreamChatClient { - String get uid => state.user.id; + String get uid => state.user!.id; } diff --git a/packages/stream_chat_persistence/example/pubspec.yaml b/packages/stream_chat_persistence/example/pubspec.yaml index faa4fcd4..e6c30c94 100644 --- a/packages/stream_chat_persistence/example/pubspec.yaml +++ b/packages/stream_chat_persistence/example/pubspec.yaml @@ -5,18 +5,22 @@ publish_to: 'none' version: 1.0.0+1 environment: - sdk: ">=2.7.0 <3.0.0" + sdk: ">=2.12.0 <3.0.0" dependencies: flutter: sdk: flutter cupertino_icons: ^1.0.0 - stream_chat: ^1.4.0 stream_chat_persistence: path: ../ +dependency_overrides: + stream_chat: + path: ../../stream_chat + dev_dependencies: flutter_test: sdk: flutter + flutter: uses-material-design: true \ No newline at end of file diff --git a/packages/stream_chat_persistence/lib/src/converter/list_converter.dart b/packages/stream_chat_persistence/lib/src/converter/list_converter.dart index cbb8ec05..d813afc3 100644 --- a/packages/stream_chat_persistence/lib/src/converter/list_converter.dart +++ b/packages/stream_chat_persistence/lib/src/converter/list_converter.dart @@ -6,7 +6,7 @@ import 'package:moor/moor.dart'; /// by the sqlite backend. class ListConverter extends TypeConverter, String> { @override - List mapToDart(String fromDb) { + List? mapToDart(String? fromDb) { if (fromDb == null) { return null; } @@ -14,7 +14,7 @@ class ListConverter extends TypeConverter, String> { } @override - String mapToSql(List value) { + String? mapToSql(List? value) { if (value == null) { return null; } diff --git a/packages/stream_chat_persistence/lib/src/converter/map_converter.dart b/packages/stream_chat_persistence/lib/src/converter/map_converter.dart index b11eb5b8..64284a9d 100644 --- a/packages/stream_chat_persistence/lib/src/converter/map_converter.dart +++ b/packages/stream_chat_persistence/lib/src/converter/map_converter.dart @@ -6,7 +6,7 @@ import 'package:moor/moor.dart'; /// by the sqlite backend. class MapConverter extends TypeConverter, String> { @override - Map mapToDart(String fromDb) { + Map? mapToDart(String? fromDb) { if (fromDb == null) { return null; } @@ -14,7 +14,7 @@ class MapConverter extends TypeConverter, String> { } @override - String mapToSql(Map value) { + String? mapToSql(Map? value) { if (value == null) { return null; } diff --git a/packages/stream_chat_persistence/lib/src/converter/message_sending_status_converter.dart b/packages/stream_chat_persistence/lib/src/converter/message_sending_status_converter.dart index 75b006bb..9d6ea11f 100644 --- a/packages/stream_chat_persistence/lib/src/converter/message_sending_status_converter.dart +++ b/packages/stream_chat_persistence/lib/src/converter/message_sending_status_converter.dart @@ -6,7 +6,7 @@ import 'package:stream_chat/stream_chat.dart'; class MessageSendingStatusConverter extends TypeConverter { @override - MessageSendingStatus mapToDart(int fromDb) { + MessageSendingStatus? mapToDart(int? fromDb) { switch (fromDb) { case 0: return MessageSendingStatus.sending; @@ -28,7 +28,7 @@ class MessageSendingStatusConverter } @override - int mapToSql(MessageSendingStatus value) { + int? mapToSql(MessageSendingStatus? value) { switch (value) { case MessageSendingStatus.sending: return 0; diff --git a/packages/stream_chat_persistence/lib/src/dao/channel_dao.dart b/packages/stream_chat_persistence/lib/src/dao/channel_dao.dart index e75cead6..fc488511 100644 --- a/packages/stream_chat_persistence/lib/src/dao/channel_dao.dart +++ b/packages/stream_chat_persistence/lib/src/dao/channel_dao.dart @@ -15,14 +15,14 @@ class ChannelDao extends DatabaseAccessor ChannelDao(MoorChatDatabase db) : super(db); /// Get channel by cid - Future getChannelByCid(String cid) async => + Future getChannelByCid(String cid) async => (select(channels)..where((c) => c.cid.equals(cid))).join([ leftOuterJoin(users, channels.createdById.equalsExp(users.id)), ]).map((rows) { final channel = rows.readTable(channels); - final createdBy = rows.readTable(users); + final createdBy = rows.readTableOrNull(users); return channel.toChannelModel(createdBy: createdBy?.toUser()); - }).getSingle(); + }).getSingleOrNull(); /// Delete all channels by matching cid in [cids] /// @@ -30,7 +30,7 @@ class ChannelDao extends DatabaseAccessor /// 1. Channel Reads /// 2. Channel Members /// 3. Channel Messages -> Messages Reactions - Future deleteChannelByCids(List cids) async => + Future deleteChannelByCids(List cids) async => (delete(channels)..where((tbl) => tbl.cid.isIn(cids))).go(); /// Get the channel cids saved in the storage diff --git a/packages/stream_chat_persistence/lib/src/dao/channel_query_dao.dart b/packages/stream_chat_persistence/lib/src/dao/channel_query_dao.dart index 2010ab89..9100e76d 100644 --- a/packages/stream_chat_persistence/lib/src/dao/channel_query_dao.dart +++ b/packages/stream_chat_persistence/lib/src/dao/channel_query_dao.dart @@ -6,7 +6,6 @@ import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; import 'package:stream_chat_persistence/src/entity/channel_queries.dart'; import 'package:stream_chat_persistence/src/entity/channels.dart'; import 'package:stream_chat_persistence/src/entity/users.dart'; - import 'package:stream_chat_persistence/src/mapper/mapper.dart'; part 'channel_query_dao.g.dart'; @@ -18,7 +17,7 @@ class ChannelQueryDao extends DatabaseAccessor /// Creates a new channel query dao instance ChannelQueryDao(MoorChatDatabase db) : super(db); - String _computeHash(Map filter) { + String _computeHash(Filter? filter) { if (filter == null) { return 'allchannels'; } @@ -30,7 +29,7 @@ class ChannelQueryDao extends DatabaseAccessor /// If [clearQueryCache] is true before the insert /// the list of matching rows will be deleted Future updateChannelQueries( - Map filter, + Filter? filter, List cids, { bool clearQueryCache = false, }) async => @@ -58,7 +57,7 @@ class ChannelQueryDao extends DatabaseAccessor }); /// - Future> getCachedChannelCids(Map filter) { + Future> getCachedChannelCids(Filter? filter) { final hash = _computeHash(filter); return (select(channelQueries)..where((c) => c.queryHash.equals(hash))) .map((c) => c.channelCid) @@ -67,9 +66,9 @@ class ChannelQueryDao extends DatabaseAccessor /// Get list of channels by filter, sort and paginationParams Future> getChannels({ - Map filter, - List> sort = const [], - PaginationParams paginationParams, + Filter? filter, + List>? sort, + PaginationParams? paginationParams, }) async { assert(() { if (sort != null && sort.any((it) => it.comparator == null)) { @@ -86,7 +85,7 @@ class ChannelQueryDao extends DatabaseAccessor final cachedChannels = await (query.join([ leftOuterJoin(users, channels.createdById.equalsExp(users.id)), ]).map((row) { - final createdByEntity = row.readTable(users); + final createdByEntity = row.readTableOrNull(users); final channelEntity = row.readTable(channels); return channelEntity.toChannelModel(createdBy: createdByEntity?.toUser()); })).get(); @@ -102,7 +101,7 @@ class ChannelQueryDao extends DatabaseAccessor int result; for (final comparator in sort.map((it) => it.comparator)) { try { - result = comparator(a, b); + result = comparator!(a, b); } catch (e) { result = 0; } @@ -115,11 +114,11 @@ class ChannelQueryDao extends DatabaseAccessor cachedChannels.sort(chainedComparator); if (paginationParams?.offset != null && cachedChannels.isNotEmpty) { - cachedChannels.removeRange(0, paginationParams.offset); + cachedChannels.removeRange(0, paginationParams!.offset); } if (paginationParams?.limit != null) { - return cachedChannels.take(paginationParams.limit).toList(); + return cachedChannels.take(paginationParams!.limit).toList(); } return cachedChannels; diff --git a/packages/stream_chat_persistence/lib/src/dao/connection_event_dao.dart b/packages/stream_chat_persistence/lib/src/dao/connection_event_dao.dart index 50528bd2..4a169cd2 100644 --- a/packages/stream_chat_persistence/lib/src/dao/connection_event_dao.dart +++ b/packages/stream_chat_persistence/lib/src/dao/connection_event_dao.dart @@ -15,19 +15,18 @@ class ConnectionEventDao extends DatabaseAccessor ConnectionEventDao(MoorChatDatabase db) : super(db); /// Get the latest stored connection event - Future get connectionEvent => select(connectionEvents) + Future get connectionEvent => select(connectionEvents) .map((eventEntity) => eventEntity.toEvent()) - .getSingle(); + .getSingleOrNull(); /// Get the latest stored lastSyncAt - Future get lastSyncAt => - select(connectionEvents).getSingle().then((r) => r?.lastSyncAt); + Future get lastSyncAt => + select(connectionEvents).getSingleOrNull().then((r) => r?.lastSyncAt); /// Update stored connection event with latest data - Future updateConnectionEvent(Event event) async => - transaction(() async { - final connectionInfo = await select(connectionEvents).getSingle(); - await into(connectionEvents).insert( + Future updateConnectionEvent(Event event) => transaction(() async { + final connectionInfo = await select(connectionEvents).getSingleOrNull(); + return into(connectionEvents).insert( ConnectionEventEntity( id: 1, lastSyncAt: connectionInfo?.lastSyncAt, diff --git a/packages/stream_chat_persistence/lib/src/dao/member_dao.dart b/packages/stream_chat_persistence/lib/src/dao/member_dao.dart index e6c03996..157b0a9a 100644 --- a/packages/stream_chat_persistence/lib/src/dao/member_dao.dart +++ b/packages/stream_chat_persistence/lib/src/dao/member_dao.dart @@ -26,7 +26,7 @@ class MemberDao extends DatabaseAccessor .map((row) { final userEntity = row.readTable(users); final memberEntity = row.readTable(members); - return memberEntity.toMember(user: userEntity?.toUser()); + return memberEntity.toMember(user: userEntity.toUser()); }).get(); /// Updates all the members using the new [memberList] data diff --git a/packages/stream_chat_persistence/lib/src/dao/message_dao.dart b/packages/stream_chat_persistence/lib/src/dao/message_dao.dart index 8d36b758..c2e1cde2 100644 --- a/packages/stream_chat_persistence/lib/src/dao/message_dao.dart +++ b/packages/stream_chat_persistence/lib/src/dao/message_dao.dart @@ -25,7 +25,7 @@ class MessageDao extends DatabaseAccessor /// /// This will automatically delete the following linked records /// 1. Message Reactions - Future deleteMessageByIds(List messageIds) => + Future deleteMessageByIds(List messageIds) => (delete(messages)..where((tbl) => tbl.id.isIn(messageIds))).go(); /// Removes all the messages by matching [Messages.channelCid] in [cids] @@ -36,17 +36,18 @@ class MessageDao extends DatabaseAccessor (delete(messages)..where((tbl) => tbl.channelCid.isIn(cids))).go(); Future _messageFromJoinRow(TypedResult rows) async { - final userEntity = rows.readTable(_users); - final pinnedByEntity = rows.readTable(_pinnedByUsers); + final userEntity = rows.readTableOrNull(_users); + final pinnedByEntity = rows.readTableOrNull(_pinnedByUsers); final msgEntity = rows.readTable(messages); final latestReactions = await _db.reactionDao.getReactions(msgEntity.id); final ownReactions = await _db.reactionDao.getReactionsByUserId( msgEntity.id, _db.userId, ); - Message quotedMessage; - if (msgEntity.quotedMessageId != null) { - quotedMessage = await getMessageById(msgEntity.quotedMessageId); + Message? quotedMessage; + final quotedMessageId = msgEntity.quotedMessageId; + if (quotedMessageId != null) { + quotedMessage = await getMessageById(quotedMessageId); } return msgEntity.toMessage( user: userEntity?.toUser(), @@ -58,7 +59,7 @@ class MessageDao extends DatabaseAccessor } /// Returns a single message by matching the [Messages.id] with [id] - Future getMessageById(String id) async => + Future getMessageById(String id) async => await (select(messages).join([ leftOuterJoin(_users, messages.userId.equalsExp(_users.id)), leftOuterJoin(_pinnedByUsers, @@ -66,7 +67,7 @@ class MessageDao extends DatabaseAccessor ]) ..where(messages.id.equals(id))) .map(_messageFromJoinRow) - .getSingle(); + .getSingleOrNull(); /// Returns all the messages of a particular thread by matching /// [Messages.channelCid] with [cid] @@ -77,7 +78,7 @@ class MessageDao extends DatabaseAccessor messages.pinnedByUserId.equalsExp(_pinnedByUsers.id)), ]) ..where(messages.channelCid.equals(cid)) - ..where(isNotNull(messages.parentId)) + ..where(messages.parentId.isNotNull()) ..orderBy([OrderingTerm.asc(messages.createdAt)])) .map(_messageFromJoinRow) .get()); @@ -86,14 +87,14 @@ class MessageDao extends DatabaseAccessor /// [Messages.parentId] with [parentId] Future> getThreadMessagesByParentId( String parentId, { - PaginationParams options, + PaginationParams? options, }) async { final msgList = await Future.wait(await (select(messages).join([ leftOuterJoin(_users, messages.userId.equalsExp(_users.id)), leftOuterJoin( _pinnedByUsers, messages.pinnedByUserId.equalsExp(_pinnedByUsers.id)), ]) - ..where(isNotNull(messages.parentId)) + ..where(messages.parentId.isNotNull()) ..where(messages.parentId.equals(parentId)) ..orderBy([OrderingTerm.asc(messages.createdAt)])) .map(_messageFromJoinRow) @@ -102,7 +103,7 @@ class MessageDao extends DatabaseAccessor if (msgList.isNotEmpty) { if (options?.lessThan != null) { final lessThanIndex = msgList.indexWhere( - (m) => m.id == options.lessThan, + (m) => m.id == options!.lessThan, ); if (lessThanIndex != -1) { msgList.removeRange(lessThanIndex, msgList.length); @@ -110,14 +111,14 @@ class MessageDao extends DatabaseAccessor } if (options?.greaterThanOrEqual != null) { final greaterThanIndex = msgList.indexWhere( - (m) => m.id == options.greaterThanOrEqual, + (m) => m.id == options!.greaterThanOrEqual, ); if (greaterThanIndex != -1) { msgList.removeRange(0, greaterThanIndex); } } if (options?.limit != null) { - return msgList.take(options.limit).toList(); + return msgList.take(options!.limit).toList(); } } return msgList; @@ -127,7 +128,7 @@ class MessageDao extends DatabaseAccessor /// [Messages.channelCid] with [parentId] Future> getMessagesByCid( String cid, { - PaginationParams messagePagination, + PaginationParams? messagePagination, }) async { final msgList = await Future.wait(await (select(messages).join([ leftOuterJoin(_users, messages.userId.equalsExp(_users.id)), @@ -136,7 +137,8 @@ class MessageDao extends DatabaseAccessor ]) ..where(messages.channelCid.equals(cid)) ..where( - isNull(messages.parentId) | messages.showInChannel.equals(true)) + messages.parentId.isNull() | messages.showInChannel.equals(true), + ) ..orderBy([OrderingTerm.asc(messages.createdAt)])) .map(_messageFromJoinRow) .get()); @@ -144,7 +146,7 @@ class MessageDao extends DatabaseAccessor if (msgList.isNotEmpty) { if (messagePagination?.lessThan != null) { final lessThanIndex = msgList.indexWhere( - (m) => m.id == messagePagination.lessThan, + (m) => m.id == messagePagination!.lessThan, ); if (lessThanIndex != -1) { msgList.removeRange(lessThanIndex, msgList.length); @@ -152,14 +154,14 @@ class MessageDao extends DatabaseAccessor } if (messagePagination?.greaterThanOrEqual != null) { final greaterThanIndex = msgList.indexWhere( - (m) => m.id == messagePagination.greaterThanOrEqual, + (m) => m.id == messagePagination!.greaterThanOrEqual, ); if (greaterThanIndex != -1) { msgList.removeRange(0, greaterThanIndex); } } if (messagePagination?.limit != null) { - return msgList.take(messagePagination.limit).toList(); + return msgList.take(messagePagination!.limit).toList(); } } return msgList; @@ -167,17 +169,13 @@ class MessageDao extends DatabaseAccessor /// Updates the message data of a particular channel with /// the new [messageList] data - Future updateMessages(String cid, List messageList) async { - if (messageList == null) { - return; - } - - return batch((batch) { - batch.insertAll( - messages, - messageList.map((it) => it.toEntity(cid: cid)).toList(), - mode: InsertMode.insertOrReplace, + Future updateMessages(String cid, List messageList) => batch( + (batch) { + batch.insertAll( + messages, + messageList.map((it) => it.toEntity(cid: cid)).toList(), + mode: InsertMode.insertOrReplace, + ); + }, ); - }); - } } diff --git a/packages/stream_chat_persistence/lib/src/dao/pinned_message_dao.dart b/packages/stream_chat_persistence/lib/src/dao/pinned_message_dao.dart index 34ee4c28..af6497f9 100644 --- a/packages/stream_chat_persistence/lib/src/dao/pinned_message_dao.dart +++ b/packages/stream_chat_persistence/lib/src/dao/pinned_message_dao.dart @@ -36,17 +36,18 @@ class PinnedMessageDao extends DatabaseAccessor (delete(pinnedMessages)..where((tbl) => tbl.channelCid.isIn(cids))).go(); Future _messageFromJoinRow(TypedResult rows) async { - final userEntity = rows.readTable(users); - final pinnedByEntity = rows.readTable(_pinnedByUsers); + final userEntity = rows.readTableOrNull(users); + final pinnedByEntity = rows.readTableOrNull(_pinnedByUsers); final msgEntity = rows.readTable(pinnedMessages); final latestReactions = await _db.reactionDao.getReactions(msgEntity.id); final ownReactions = await _db.reactionDao.getReactionsByUserId( msgEntity.id, _db.userId, ); - Message quotedMessage; - if (msgEntity.quotedMessageId != null) { - quotedMessage = await getMessageById(msgEntity.quotedMessageId); + Message? quotedMessage; + final quotedMessageId = msgEntity.quotedMessageId; + if (quotedMessageId != null) { + quotedMessage = await getMessageById(quotedMessageId); } return msgEntity.toMessage( user: userEntity?.toUser(), @@ -58,7 +59,7 @@ class PinnedMessageDao extends DatabaseAccessor } /// Returns a single message by matching the [PinnedMessages.id] with [id] - Future getMessageById(String id) async => + Future getMessageById(String id) async => await (select(pinnedMessages).join([ leftOuterJoin(_users, pinnedMessages.userId.equalsExp(_users.id)), leftOuterJoin(_pinnedByUsers, @@ -66,7 +67,7 @@ class PinnedMessageDao extends DatabaseAccessor ]) ..where(pinnedMessages.id.equals(id))) .map(_messageFromJoinRow) - .getSingle(); + .getSingleOrNull(); /// Returns all the messages of a particular thread by matching /// [PinnedMessages.channelCid] with [cid] @@ -77,7 +78,7 @@ class PinnedMessageDao extends DatabaseAccessor pinnedMessages.pinnedByUserId.equalsExp(_pinnedByUsers.id)), ]) ..where(pinnedMessages.channelCid.equals(cid)) - ..where(isNotNull(pinnedMessages.parentId)) + ..where(pinnedMessages.parentId.isNotNull()) ..orderBy([OrderingTerm.asc(pinnedMessages.createdAt)])) .map(_messageFromJoinRow) .get()); @@ -86,14 +87,14 @@ class PinnedMessageDao extends DatabaseAccessor /// [PinnedMessages.parentId] with [parentId] Future> getThreadMessagesByParentId( String parentId, { - PaginationParams options, + PaginationParams? options, }) async { final msgList = await Future.wait(await (select(pinnedMessages).join([ leftOuterJoin(_users, pinnedMessages.userId.equalsExp(_users.id)), leftOuterJoin(_pinnedByUsers, pinnedMessages.pinnedByUserId.equalsExp(_pinnedByUsers.id)), ]) - ..where(isNotNull(pinnedMessages.parentId)) + ..where(pinnedMessages.parentId.isNotNull()) ..where(pinnedMessages.parentId.equals(parentId)) ..orderBy([OrderingTerm.asc(pinnedMessages.createdAt)])) .map(_messageFromJoinRow) @@ -102,7 +103,7 @@ class PinnedMessageDao extends DatabaseAccessor if (msgList.isNotEmpty) { if (options?.lessThan != null) { final lessThanIndex = msgList.indexWhere( - (m) => m.id == options.lessThan, + (m) => m.id == options!.lessThan, ); if (lessThanIndex != -1) { msgList.removeRange(lessThanIndex, msgList.length); @@ -110,14 +111,14 @@ class PinnedMessageDao extends DatabaseAccessor } if (options?.greaterThanOrEqual != null) { final greaterThanIndex = msgList.indexWhere( - (m) => m.id == options.greaterThanOrEqual, + (m) => m.id == options!.greaterThanOrEqual, ); if (greaterThanIndex != -1) { msgList.removeRange(0, greaterThanIndex); } } if (options?.limit != null) { - return msgList.take(options.limit).toList(); + return msgList.take(options!.limit).toList(); } } return msgList; @@ -127,7 +128,7 @@ class PinnedMessageDao extends DatabaseAccessor /// [PinnedMessages.channelCid] with [parentId] Future> getMessagesByCid( String cid, { - PaginationParams messagePagination, + PaginationParams? messagePagination, }) async { final msgList = await Future.wait(await (select(pinnedMessages).join([ leftOuterJoin(_users, pinnedMessages.userId.equalsExp(_users.id)), @@ -135,7 +136,7 @@ class PinnedMessageDao extends DatabaseAccessor pinnedMessages.pinnedByUserId.equalsExp(_pinnedByUsers.id)), ]) ..where(pinnedMessages.channelCid.equals(cid)) - ..where(isNull(pinnedMessages.parentId) | + ..where(pinnedMessages.parentId.isNull() | pinnedMessages.showInChannel.equals(true)) ..orderBy([OrderingTerm.asc(pinnedMessages.createdAt)])) .map(_messageFromJoinRow) @@ -144,7 +145,7 @@ class PinnedMessageDao extends DatabaseAccessor if (msgList.isNotEmpty) { if (messagePagination?.lessThan != null) { final lessThanIndex = msgList.indexWhere( - (m) => m.id == messagePagination.lessThan, + (m) => m.id == messagePagination!.lessThan, ); if (lessThanIndex != -1) { msgList.removeRange(lessThanIndex, msgList.length); @@ -152,14 +153,14 @@ class PinnedMessageDao extends DatabaseAccessor } if (messagePagination?.greaterThanOrEqual != null) { final greaterThanIndex = msgList.indexWhere( - (m) => m.id == messagePagination.greaterThanOrEqual, + (m) => m.id == messagePagination!.greaterThanOrEqual, ); if (greaterThanIndex != -1) { msgList.removeRange(0, greaterThanIndex); } } if (messagePagination?.limit != null) { - return msgList.take(messagePagination.limit).toList(); + return msgList.take(messagePagination!.limit).toList(); } } return msgList; @@ -167,17 +168,13 @@ class PinnedMessageDao extends DatabaseAccessor /// Updates the message data of a particular channel with /// the new [messageList] data - Future updateMessages(String cid, List messageList) async { - if (messageList == null) { - return; - } - - return batch((batch) { - batch.insertAll( - pinnedMessages, - messageList.map((it) => it.toPinnedEntity(cid: cid)).toList(), - mode: InsertMode.insertOrReplace, + Future updateMessages(String cid, List messageList) => batch( + (batch) { + batch.insertAll( + pinnedMessages, + messageList.map((it) => it.toPinnedEntity(cid: cid)).toList(), + mode: InsertMode.insertOrReplace, + ); + }, ); - }); - } } diff --git a/packages/stream_chat_persistence/lib/src/dao/reaction_dao.dart b/packages/stream_chat_persistence/lib/src/dao/reaction_dao.dart index ef06326a..89da6081 100644 --- a/packages/stream_chat_persistence/lib/src/dao/reaction_dao.dart +++ b/packages/stream_chat_persistence/lib/src/dao/reaction_dao.dart @@ -23,7 +23,7 @@ class ReactionDao extends DatabaseAccessor ..where(reactions.messageId.equals(messageId)) ..orderBy([OrderingTerm.asc(reactions.createdAt)])) .map((rows) { - final userEntity = rows.readTable(users); + final userEntity = rows.readTableOrNull(users); final reactionEntity = rows.readTable(reactions); return reactionEntity.toReaction(user: userEntity?.toUser()); }).get(); diff --git a/packages/stream_chat_persistence/lib/src/dao/read_dao.dart b/packages/stream_chat_persistence/lib/src/dao/read_dao.dart index 95e23d8b..05d807fd 100644 --- a/packages/stream_chat_persistence/lib/src/dao/read_dao.dart +++ b/packages/stream_chat_persistence/lib/src/dao/read_dao.dart @@ -24,7 +24,7 @@ class ReadDao extends DatabaseAccessor with _$ReadDaoMixin { .map((row) { final userEntity = row.readTable(users); final readEntity = row.readTable(reads); - return readEntity.toRead(user: userEntity?.toUser()); + return readEntity.toRead(user: userEntity.toUser()); }).get(); /// Updates the read data of a particular channel with diff --git a/packages/stream_chat_persistence/lib/src/db/moor_chat_database.dart b/packages/stream_chat_persistence/lib/src/db/moor_chat_database.dart index 73c7b0fa..622e0a61 100644 --- a/packages/stream_chat_persistence/lib/src/db/moor_chat_database.dart +++ b/packages/stream_chat_persistence/lib/src/db/moor_chat_database.dart @@ -72,6 +72,13 @@ class MoorChatDatabase extends _$MoorChatDatabase { }, ); + /// Deletes all the tables + Future flush() => batch((batch) { + allTables.forEach((table) { + delete(table).go(); + }); + }); + /// Closes the database instance Future disconnect() => close(); } diff --git a/packages/stream_chat_persistence/lib/src/db/moor_chat_database.g.dart b/packages/stream_chat_persistence/lib/src/db/moor_chat_database.g.dart index b83b16da..8220ee4e 100644 --- a/packages/stream_chat_persistence/lib/src/db/moor_chat_database.g.dart +++ b/packages/stream_chat_persistence/lib/src/db/moor_chat_database.g.dart @@ -8,57 +8,82 @@ part of 'moor_chat_database.dart'; // ignore_for_file: unnecessary_brace_in_string_interps, unnecessary_this class ChannelEntity extends DataClass implements Insertable { + /// The id of this channel final String id; + + /// The type of this channel final String type; + + /// The cid of this channel final String cid; - final Map config; + + /// The channel configuration data + final Map config; + + /// True if this channel entity is frozen final bool frozen; - final DateTime lastMessageAt; + + /// The date of the last message + final DateTime? lastMessageAt; + + /// The date of channel creation final DateTime createdAt; + + /// The date of the last channel update final DateTime updatedAt; - final DateTime deletedAt; + + /// The date of channel deletion + final DateTime? deletedAt; + + /// The count of this channel members final int memberCount; - final String createdById; - final Map extraData; - ChannelEntity( - {@required this.id, - @required this.type, - @required this.cid, - @required this.config, - @required this.frozen, - this.lastMessageAt, - this.createdAt, - this.updatedAt, - this.deletedAt, - this.memberCount, - this.createdById, - this.extraData}); + + /// The id of the user that created this channel + final String? createdById; + + /// Map of custom channel extraData + final Map? extraData; + + ChannelEntity({ + required this.id, + required this.type, + required this.cid, + required this.config, + required this.frozen, + this.lastMessageAt, + required this.createdAt, + required this.updatedAt, + this.deletedAt, + required this.memberCount, + this.createdById, + this.extraData, + }); factory ChannelEntity.fromData( Map data, GeneratedDatabase db, - {String prefix}) { + {String? prefix}) { final effectivePrefix = prefix ?? ''; final stringType = db.typeSystem.forDartType(); final boolType = db.typeSystem.forDartType(); final dateTimeType = db.typeSystem.forDartType(); final intType = db.typeSystem.forDartType(); return ChannelEntity( - id: stringType.mapFromDatabaseResponse(data['${effectivePrefix}id']), - type: stringType.mapFromDatabaseResponse(data['${effectivePrefix}type']), - cid: stringType.mapFromDatabaseResponse(data['${effectivePrefix}cid']), - config: $ChannelsTable.$converter0.mapToDart( - stringType.mapFromDatabaseResponse(data['${effectivePrefix}config'])), + id: stringType.mapFromDatabaseResponse(data['${effectivePrefix}id'])!, + type: stringType.mapFromDatabaseResponse(data['${effectivePrefix}type'])!, + cid: stringType.mapFromDatabaseResponse(data['${effectivePrefix}cid'])!, + config: $ChannelsTable.$converter0.mapToDart(stringType + .mapFromDatabaseResponse(data['${effectivePrefix}config']))!, frozen: - boolType.mapFromDatabaseResponse(data['${effectivePrefix}frozen']), + boolType.mapFromDatabaseResponse(data['${effectivePrefix}frozen'])!, lastMessageAt: dateTimeType .mapFromDatabaseResponse(data['${effectivePrefix}last_message_at']), createdAt: dateTimeType - .mapFromDatabaseResponse(data['${effectivePrefix}created_at']), + .mapFromDatabaseResponse(data['${effectivePrefix}created_at'])!, updatedAt: dateTimeType - .mapFromDatabaseResponse(data['${effectivePrefix}updated_at']), + .mapFromDatabaseResponse(data['${effectivePrefix}updated_at'])!, deletedAt: dateTimeType .mapFromDatabaseResponse(data['${effectivePrefix}deleted_at']), memberCount: intType - .mapFromDatabaseResponse(data['${effectivePrefix}member_count']), + .mapFromDatabaseResponse(data['${effectivePrefix}member_count'])!, createdById: stringType .mapFromDatabaseResponse(data['${effectivePrefix}created_by_id']), extraData: $ChannelsTable.$converter1.mapToDart(stringType @@ -68,97 +93,83 @@ class ChannelEntity extends DataClass implements Insertable { @override Map toColumns(bool nullToAbsent) { final map = {}; - if (!nullToAbsent || id != null) { - map['id'] = Variable(id); - } - if (!nullToAbsent || type != null) { - map['type'] = Variable(type); - } - if (!nullToAbsent || cid != null) { - map['cid'] = Variable(cid); - } - if (!nullToAbsent || config != null) { + map['id'] = Variable(id); + map['type'] = Variable(type); + map['cid'] = Variable(cid); + { final converter = $ChannelsTable.$converter0; - map['config'] = Variable(converter.mapToSql(config)); - } - if (!nullToAbsent || frozen != null) { - map['frozen'] = Variable(frozen); + map['config'] = Variable(converter.mapToSql(config)!); } + map['frozen'] = Variable(frozen); if (!nullToAbsent || lastMessageAt != null) { - map['last_message_at'] = Variable(lastMessageAt); - } - if (!nullToAbsent || createdAt != null) { - map['created_at'] = Variable(createdAt); - } - if (!nullToAbsent || updatedAt != null) { - map['updated_at'] = Variable(updatedAt); + map['last_message_at'] = Variable(lastMessageAt); } + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); if (!nullToAbsent || deletedAt != null) { - map['deleted_at'] = Variable(deletedAt); - } - if (!nullToAbsent || memberCount != null) { - map['member_count'] = Variable(memberCount); + map['deleted_at'] = Variable(deletedAt); } + map['member_count'] = Variable(memberCount); if (!nullToAbsent || createdById != null) { - map['created_by_id'] = Variable(createdById); + map['created_by_id'] = Variable(createdById); } if (!nullToAbsent || extraData != null) { final converter = $ChannelsTable.$converter1; - map['extra_data'] = Variable(converter.mapToSql(extraData)); + map['extra_data'] = Variable(converter.mapToSql(extraData)); } return map; } factory ChannelEntity.fromJson(Map json, - {ValueSerializer serializer}) { + {ValueSerializer? serializer}) { serializer ??= moorRuntimeOptions.defaultSerializer; return ChannelEntity( id: serializer.fromJson(json['id']), type: serializer.fromJson(json['type']), cid: serializer.fromJson(json['cid']), - config: serializer.fromJson>(json['config']), + config: serializer.fromJson>(json['config']), frozen: serializer.fromJson(json['frozen']), - lastMessageAt: serializer.fromJson(json['lastMessageAt']), + lastMessageAt: serializer.fromJson(json['lastMessageAt']), createdAt: serializer.fromJson(json['createdAt']), updatedAt: serializer.fromJson(json['updatedAt']), - deletedAt: serializer.fromJson(json['deletedAt']), + deletedAt: serializer.fromJson(json['deletedAt']), memberCount: serializer.fromJson(json['memberCount']), - createdById: serializer.fromJson(json['createdById']), - extraData: serializer.fromJson>(json['extraData']), + createdById: serializer.fromJson(json['createdById']), + extraData: serializer.fromJson?>(json['extraData']), ); } @override - Map toJson({ValueSerializer serializer}) { + Map toJson({ValueSerializer? serializer}) { serializer ??= moorRuntimeOptions.defaultSerializer; return { 'id': serializer.toJson(id), 'type': serializer.toJson(type), 'cid': serializer.toJson(cid), - 'config': serializer.toJson>(config), + 'config': serializer.toJson>(config), 'frozen': serializer.toJson(frozen), - 'lastMessageAt': serializer.toJson(lastMessageAt), + 'lastMessageAt': serializer.toJson(lastMessageAt), 'createdAt': serializer.toJson(createdAt), 'updatedAt': serializer.toJson(updatedAt), - 'deletedAt': serializer.toJson(deletedAt), + 'deletedAt': serializer.toJson(deletedAt), 'memberCount': serializer.toJson(memberCount), - 'createdById': serializer.toJson(createdById), - 'extraData': serializer.toJson>(extraData), + 'createdById': serializer.toJson(createdById), + 'extraData': serializer.toJson?>(extraData), }; } ChannelEntity copyWith( - {String id, - String type, - String cid, - Map config, - bool frozen, - Value lastMessageAt = const Value.absent(), - Value createdAt = const Value.absent(), - Value updatedAt = const Value.absent(), - Value deletedAt = const Value.absent(), - Value memberCount = const Value.absent(), - Value createdById = const Value.absent(), - Value> extraData = const Value.absent()}) => + {String? id, + String? type, + String? cid, + Map? config, + bool? frozen, + Value lastMessageAt = const Value.absent(), + DateTime? createdAt, + DateTime? updatedAt, + Value deletedAt = const Value.absent(), + int? memberCount, + Value createdById = const Value.absent(), + Value?> extraData = const Value.absent()}) => ChannelEntity( id: id ?? this.id, type: type ?? this.type, @@ -167,10 +178,10 @@ class ChannelEntity extends DataClass implements Insertable { frozen: frozen ?? this.frozen, lastMessageAt: lastMessageAt.present ? lastMessageAt.value : this.lastMessageAt, - createdAt: createdAt.present ? createdAt.value : this.createdAt, - updatedAt: updatedAt.present ? updatedAt.value : this.updatedAt, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, - memberCount: memberCount.present ? memberCount.value : this.memberCount, + memberCount: memberCount ?? this.memberCount, createdById: createdById.present ? createdById.value : this.createdById, extraData: extraData.present ? extraData.value : this.extraData, ); @@ -238,15 +249,15 @@ class ChannelsCompanion extends UpdateCompanion { final Value id; final Value type; final Value cid; - final Value> config; + final Value> config; final Value frozen; - final Value lastMessageAt; + final Value lastMessageAt; final Value createdAt; final Value updatedAt; - final Value deletedAt; + final Value deletedAt; final Value memberCount; - final Value createdById; - final Value> extraData; + final Value createdById; + final Value?> extraData; const ChannelsCompanion({ this.id = const Value.absent(), this.type = const Value.absent(), @@ -262,10 +273,10 @@ class ChannelsCompanion extends UpdateCompanion { this.extraData = const Value.absent(), }); ChannelsCompanion.insert({ - @required String id, - @required String type, - @required String cid, - @required Map config, + required String id, + required String type, + required String cid, + required Map config, this.frozen = const Value.absent(), this.lastMessageAt = const Value.absent(), this.createdAt = const Value.absent(), @@ -279,18 +290,18 @@ class ChannelsCompanion extends UpdateCompanion { cid = Value(cid), config = Value(config); static Insertable custom({ - Expression id, - Expression type, - Expression cid, - Expression config, - Expression frozen, - Expression lastMessageAt, - Expression createdAt, - Expression updatedAt, - Expression deletedAt, - Expression memberCount, - Expression createdById, - Expression extraData, + Expression? id, + Expression? type, + Expression? cid, + Expression>? config, + Expression? frozen, + Expression? lastMessageAt, + Expression? createdAt, + Expression? updatedAt, + Expression? deletedAt, + Expression? memberCount, + Expression? createdById, + Expression?>? extraData, }) { return RawValuesInsertable({ if (id != null) 'id': id, @@ -309,18 +320,18 @@ class ChannelsCompanion extends UpdateCompanion { } ChannelsCompanion copyWith( - {Value id, - Value type, - Value cid, - Value> config, - Value frozen, - Value lastMessageAt, - Value createdAt, - Value updatedAt, - Value deletedAt, - Value memberCount, - Value createdById, - Value> extraData}) { + {Value? id, + Value? type, + Value? cid, + Value>? config, + Value? frozen, + Value? lastMessageAt, + Value? createdAt, + Value? updatedAt, + Value? deletedAt, + Value? memberCount, + Value? createdById, + Value?>? extraData}) { return ChannelsCompanion( id: id ?? this.id, type: type ?? this.type, @@ -351,13 +362,13 @@ class ChannelsCompanion extends UpdateCompanion { } if (config.present) { final converter = $ChannelsTable.$converter0; - map['config'] = Variable(converter.mapToSql(config.value)); + map['config'] = Variable(converter.mapToSql(config.value)!); } if (frozen.present) { map['frozen'] = Variable(frozen.value); } if (lastMessageAt.present) { - map['last_message_at'] = Variable(lastMessageAt.value); + map['last_message_at'] = Variable(lastMessageAt.value); } if (createdAt.present) { map['created_at'] = Variable(createdAt.value); @@ -366,17 +377,18 @@ class ChannelsCompanion extends UpdateCompanion { map['updated_at'] = Variable(updatedAt.value); } if (deletedAt.present) { - map['deleted_at'] = Variable(deletedAt.value); + map['deleted_at'] = Variable(deletedAt.value); } if (memberCount.present) { map['member_count'] = Variable(memberCount.value); } if (createdById.present) { - map['created_by_id'] = Variable(createdById.value); + map['created_by_id'] = Variable(createdById.value); } if (extraData.present) { final converter = $ChannelsTable.$converter1; - map['extra_data'] = Variable(converter.mapToSql(extraData.value)); + map['extra_data'] = + Variable(converter.mapToSql(extraData.value)); } return map; } @@ -404,12 +416,11 @@ class ChannelsCompanion extends UpdateCompanion { class $ChannelsTable extends Channels with TableInfo<$ChannelsTable, ChannelEntity> { final GeneratedDatabase _db; - final String _alias; + final String? _alias; $ChannelsTable(this._db, [this._alias]); final VerificationMeta _idMeta = const VerificationMeta('id'); - GeneratedTextColumn _id; @override - GeneratedTextColumn get id => _id ??= _constructId(); + late final GeneratedTextColumn id = _constructId(); GeneratedTextColumn _constructId() { return GeneratedTextColumn( 'id', @@ -419,9 +430,8 @@ class $ChannelsTable extends Channels } final VerificationMeta _typeMeta = const VerificationMeta('type'); - GeneratedTextColumn _type; @override - GeneratedTextColumn get type => _type ??= _constructType(); + late final GeneratedTextColumn type = _constructType(); GeneratedTextColumn _constructType() { return GeneratedTextColumn( 'type', @@ -431,9 +441,8 @@ class $ChannelsTable extends Channels } final VerificationMeta _cidMeta = const VerificationMeta('cid'); - GeneratedTextColumn _cid; @override - GeneratedTextColumn get cid => _cid ??= _constructCid(); + late final GeneratedTextColumn cid = _constructCid(); GeneratedTextColumn _constructCid() { return GeneratedTextColumn( 'cid', @@ -443,9 +452,8 @@ class $ChannelsTable extends Channels } final VerificationMeta _configMeta = const VerificationMeta('config'); - GeneratedTextColumn _config; @override - GeneratedTextColumn get config => _config ??= _constructConfig(); + late final GeneratedTextColumn config = _constructConfig(); GeneratedTextColumn _constructConfig() { return GeneratedTextColumn( 'config', @@ -455,20 +463,17 @@ class $ChannelsTable extends Channels } final VerificationMeta _frozenMeta = const VerificationMeta('frozen'); - GeneratedBoolColumn _frozen; @override - GeneratedBoolColumn get frozen => _frozen ??= _constructFrozen(); + late final GeneratedBoolColumn frozen = _constructFrozen(); GeneratedBoolColumn _constructFrozen() { return GeneratedBoolColumn('frozen', $tableName, false, - defaultValue: Constant(false)); + defaultValue: const Constant(false)); } final VerificationMeta _lastMessageAtMeta = const VerificationMeta('lastMessageAt'); - GeneratedDateTimeColumn _lastMessageAt; @override - GeneratedDateTimeColumn get lastMessageAt => - _lastMessageAt ??= _constructLastMessageAt(); + late final GeneratedDateTimeColumn lastMessageAt = _constructLastMessageAt(); GeneratedDateTimeColumn _constructLastMessageAt() { return GeneratedDateTimeColumn( 'last_message_at', @@ -478,33 +483,24 @@ class $ChannelsTable extends Channels } final VerificationMeta _createdAtMeta = const VerificationMeta('createdAt'); - GeneratedDateTimeColumn _createdAt; @override - GeneratedDateTimeColumn get createdAt => _createdAt ??= _constructCreatedAt(); + late final GeneratedDateTimeColumn createdAt = _constructCreatedAt(); GeneratedDateTimeColumn _constructCreatedAt() { - return GeneratedDateTimeColumn( - 'created_at', - $tableName, - true, - ); + return GeneratedDateTimeColumn('created_at', $tableName, false, + defaultValue: currentDateAndTime); } final VerificationMeta _updatedAtMeta = const VerificationMeta('updatedAt'); - GeneratedDateTimeColumn _updatedAt; @override - GeneratedDateTimeColumn get updatedAt => _updatedAt ??= _constructUpdatedAt(); + late final GeneratedDateTimeColumn updatedAt = _constructUpdatedAt(); GeneratedDateTimeColumn _constructUpdatedAt() { - return GeneratedDateTimeColumn( - 'updated_at', - $tableName, - true, - ); + return GeneratedDateTimeColumn('updated_at', $tableName, false, + defaultValue: currentDateAndTime); } final VerificationMeta _deletedAtMeta = const VerificationMeta('deletedAt'); - GeneratedDateTimeColumn _deletedAt; @override - GeneratedDateTimeColumn get deletedAt => _deletedAt ??= _constructDeletedAt(); + late final GeneratedDateTimeColumn deletedAt = _constructDeletedAt(); GeneratedDateTimeColumn _constructDeletedAt() { return GeneratedDateTimeColumn( 'deleted_at', @@ -515,24 +511,17 @@ class $ChannelsTable extends Channels final VerificationMeta _memberCountMeta = const VerificationMeta('memberCount'); - GeneratedIntColumn _memberCount; @override - GeneratedIntColumn get memberCount => - _memberCount ??= _constructMemberCount(); + late final GeneratedIntColumn memberCount = _constructMemberCount(); GeneratedIntColumn _constructMemberCount() { - return GeneratedIntColumn( - 'member_count', - $tableName, - true, - ); + return GeneratedIntColumn('member_count', $tableName, false, + defaultValue: const Constant(0)); } final VerificationMeta _createdByIdMeta = const VerificationMeta('createdById'); - GeneratedTextColumn _createdById; @override - GeneratedTextColumn get createdById => - _createdById ??= _constructCreatedById(); + late final GeneratedTextColumn createdById = _constructCreatedById(); GeneratedTextColumn _constructCreatedById() { return GeneratedTextColumn( 'created_by_id', @@ -542,9 +531,8 @@ class $ChannelsTable extends Channels } final VerificationMeta _extraDataMeta = const VerificationMeta('extraData'); - GeneratedTextColumn _extraData; @override - GeneratedTextColumn get extraData => _extraData ??= _constructExtraData(); + late final GeneratedTextColumn extraData = _constructExtraData(); GeneratedTextColumn _constructExtraData() { return GeneratedTextColumn( 'extra_data', @@ -580,56 +568,56 @@ class $ChannelsTable extends Channels final context = VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('id')) { - context.handle(_idMeta, id.isAcceptableOrUnknown(data['id'], _idMeta)); + context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); } else if (isInserting) { context.missing(_idMeta); } if (data.containsKey('type')) { context.handle( - _typeMeta, type.isAcceptableOrUnknown(data['type'], _typeMeta)); + _typeMeta, type.isAcceptableOrUnknown(data['type']!, _typeMeta)); } else if (isInserting) { context.missing(_typeMeta); } if (data.containsKey('cid')) { context.handle( - _cidMeta, cid.isAcceptableOrUnknown(data['cid'], _cidMeta)); + _cidMeta, cid.isAcceptableOrUnknown(data['cid']!, _cidMeta)); } else if (isInserting) { context.missing(_cidMeta); } context.handle(_configMeta, const VerificationResult.success()); if (data.containsKey('frozen')) { context.handle(_frozenMeta, - frozen.isAcceptableOrUnknown(data['frozen'], _frozenMeta)); + frozen.isAcceptableOrUnknown(data['frozen']!, _frozenMeta)); } if (data.containsKey('last_message_at')) { context.handle( _lastMessageAtMeta, lastMessageAt.isAcceptableOrUnknown( - data['last_message_at'], _lastMessageAtMeta)); + data['last_message_at']!, _lastMessageAtMeta)); } if (data.containsKey('created_at')) { context.handle(_createdAtMeta, - createdAt.isAcceptableOrUnknown(data['created_at'], _createdAtMeta)); + createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta)); } if (data.containsKey('updated_at')) { context.handle(_updatedAtMeta, - updatedAt.isAcceptableOrUnknown(data['updated_at'], _updatedAtMeta)); + updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta)); } if (data.containsKey('deleted_at')) { context.handle(_deletedAtMeta, - deletedAt.isAcceptableOrUnknown(data['deleted_at'], _deletedAtMeta)); + deletedAt.isAcceptableOrUnknown(data['deleted_at']!, _deletedAtMeta)); } if (data.containsKey('member_count')) { context.handle( _memberCountMeta, memberCount.isAcceptableOrUnknown( - data['member_count'], _memberCountMeta)); + data['member_count']!, _memberCountMeta)); } if (data.containsKey('created_by_id')) { context.handle( _createdByIdMeta, createdById.isAcceptableOrUnknown( - data['created_by_id'], _createdByIdMeta)); + data['created_by_id']!, _createdByIdMeta)); } context.handle(_extraDataMeta, const VerificationResult.success()); return context; @@ -638,7 +626,7 @@ class $ChannelsTable extends Channels @override Set get $primaryKey => {cid}; @override - ChannelEntity map(Map data, {String tablePrefix}) { + ChannelEntity map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : null; return ChannelEntity.fromData(data, _db, prefix: effectivePrefix); } @@ -648,57 +636,105 @@ class $ChannelsTable extends Channels return $ChannelsTable(_db, alias); } - static TypeConverter, String> $converter0 = - MapConverter(); + static TypeConverter, String> $converter0 = + MapConverter(); static TypeConverter, String> $converter1 = MapConverter(); } class MessageEntity extends DataClass implements Insertable { + /// The message id final String id; - final String messageText; + + /// The text of this message + final String? messageText; + + /// The list of attachments, either provided by the user + /// or generated from a command or as a result of URL scraping. final List attachments; + + /// The status of a sending message final MessageSendingStatus status; + + /// The message type final String type; + + /// The list of user mentioned in the message final List mentionedUsers; - final Map reactionCounts; - final Map reactionScores; - final String parentId; - final String quotedMessageId; - final int replyCount; - final bool showInChannel; + + /// A map describing the count of number of every reaction + final Map? reactionCounts; + + /// A map describing the count of score of every reaction + final Map? reactionScores; + + /// The ID of the parent message, if the message is a thread reply. + final String? parentId; + + /// The ID of the quoted message, if the message is a quoted reply. + final String? quotedMessageId; + + /// Number of replies for this message. + final int? replyCount; + + /// Check if this message needs to show in the channel. + final bool? showInChannel; + + /// If true the message is shadowed final bool shadowed; - final String command; + + /// A used command name. + final String? command; + + /// The DateTime when the message was created. final DateTime createdAt; + + /// The DateTime when the message was updated last time. final DateTime updatedAt; - final DateTime deletedAt; - final String userId; + + /// The DateTime when the message was deleted. + final DateTime? deletedAt; + + /// Id of the User who sent the message + final String? userId; + + /// Whether the message is pinned or not final bool pinned; - final DateTime pinnedAt; - final DateTime pinExpires; - final String pinnedByUserId; - final String channelCid; - final Map extraData; + + /// The DateTime at which the message was pinned + final DateTime? pinnedAt; + + /// The DateTime on which the message pin expires + final DateTime? pinExpires; + + /// Id of the User who pinned the message + final String? pinnedByUserId; + + /// The channel cid of which this message is part of + final String? channelCid; + + /// Message custom extraData + final Map? extraData; MessageEntity( - {@required this.id, + {required this.id, this.messageText, - this.attachments, - this.status, - this.type, - this.mentionedUsers, + required this.attachments, + required this.status, + required this.type, + required this.mentionedUsers, this.reactionCounts, this.reactionScores, this.parentId, this.quotedMessageId, this.replyCount, this.showInChannel, - this.shadowed, + required this.shadowed, this.command, - @required this.createdAt, - this.updatedAt, + required this.createdAt, + required this.updatedAt, this.deletedAt, this.userId, - @required this.pinned, + required this.pinned, this.pinnedAt, this.pinExpires, this.pinnedByUserId, @@ -706,23 +742,23 @@ class MessageEntity extends DataClass implements Insertable { this.extraData}); factory MessageEntity.fromData( Map data, GeneratedDatabase db, - {String prefix}) { + {String? prefix}) { final effectivePrefix = prefix ?? ''; final stringType = db.typeSystem.forDartType(); final intType = db.typeSystem.forDartType(); final boolType = db.typeSystem.forDartType(); final dateTimeType = db.typeSystem.forDartType(); return MessageEntity( - id: stringType.mapFromDatabaseResponse(data['${effectivePrefix}id']), + id: stringType.mapFromDatabaseResponse(data['${effectivePrefix}id'])!, messageText: stringType .mapFromDatabaseResponse(data['${effectivePrefix}message_text']), attachments: $MessagesTable.$converter0.mapToDart(stringType - .mapFromDatabaseResponse(data['${effectivePrefix}attachments'])), + .mapFromDatabaseResponse(data['${effectivePrefix}attachments']))!, status: $MessagesTable.$converter1.mapToDart( - intType.mapFromDatabaseResponse(data['${effectivePrefix}status'])), - type: stringType.mapFromDatabaseResponse(data['${effectivePrefix}type']), + intType.mapFromDatabaseResponse(data['${effectivePrefix}status']))!, + type: stringType.mapFromDatabaseResponse(data['${effectivePrefix}type'])!, mentionedUsers: $MessagesTable.$converter2.mapToDart(stringType - .mapFromDatabaseResponse(data['${effectivePrefix}mentioned_users'])), + .mapFromDatabaseResponse(data['${effectivePrefix}mentioned_users']))!, reactionCounts: $MessagesTable.$converter3.mapToDart(stringType .mapFromDatabaseResponse(data['${effectivePrefix}reaction_counts'])), reactionScores: $MessagesTable.$converter4.mapToDart(stringType @@ -736,19 +772,19 @@ class MessageEntity extends DataClass implements Insertable { showInChannel: boolType .mapFromDatabaseResponse(data['${effectivePrefix}show_in_channel']), shadowed: - boolType.mapFromDatabaseResponse(data['${effectivePrefix}shadowed']), + boolType.mapFromDatabaseResponse(data['${effectivePrefix}shadowed'])!, command: stringType.mapFromDatabaseResponse(data['${effectivePrefix}command']), createdAt: dateTimeType - .mapFromDatabaseResponse(data['${effectivePrefix}created_at']), + .mapFromDatabaseResponse(data['${effectivePrefix}created_at'])!, updatedAt: dateTimeType - .mapFromDatabaseResponse(data['${effectivePrefix}updated_at']), + .mapFromDatabaseResponse(data['${effectivePrefix}updated_at'])!, deletedAt: dateTimeType .mapFromDatabaseResponse(data['${effectivePrefix}deleted_at']), userId: stringType.mapFromDatabaseResponse(data['${effectivePrefix}user_id']), pinned: - boolType.mapFromDatabaseResponse(data['${effectivePrefix}pinned']), + boolType.mapFromDatabaseResponse(data['${effectivePrefix}pinned'])!, pinnedAt: dateTimeType .mapFromDatabaseResponse(data['${effectivePrefix}pinned_at']), pinExpires: dateTimeType @@ -764,186 +800,173 @@ class MessageEntity extends DataClass implements Insertable { @override Map toColumns(bool nullToAbsent) { final map = {}; - if (!nullToAbsent || id != null) { - map['id'] = Variable(id); - } + map['id'] = Variable(id); if (!nullToAbsent || messageText != null) { - map['message_text'] = Variable(messageText); + map['message_text'] = Variable(messageText); } - if (!nullToAbsent || attachments != null) { + { final converter = $MessagesTable.$converter0; - map['attachments'] = Variable(converter.mapToSql(attachments)); + map['attachments'] = Variable(converter.mapToSql(attachments)!); } - if (!nullToAbsent || status != null) { + { final converter = $MessagesTable.$converter1; - map['status'] = Variable(converter.mapToSql(status)); + map['status'] = Variable(converter.mapToSql(status)!); } - if (!nullToAbsent || type != null) { - map['type'] = Variable(type); - } - if (!nullToAbsent || mentionedUsers != null) { + map['type'] = Variable(type); + { final converter = $MessagesTable.$converter2; map['mentioned_users'] = - Variable(converter.mapToSql(mentionedUsers)); + Variable(converter.mapToSql(mentionedUsers)!); } if (!nullToAbsent || reactionCounts != null) { final converter = $MessagesTable.$converter3; map['reaction_counts'] = - Variable(converter.mapToSql(reactionCounts)); + Variable(converter.mapToSql(reactionCounts)); } if (!nullToAbsent || reactionScores != null) { final converter = $MessagesTable.$converter4; map['reaction_scores'] = - Variable(converter.mapToSql(reactionScores)); + Variable(converter.mapToSql(reactionScores)); } if (!nullToAbsent || parentId != null) { - map['parent_id'] = Variable(parentId); + map['parent_id'] = Variable(parentId); } if (!nullToAbsent || quotedMessageId != null) { - map['quoted_message_id'] = Variable(quotedMessageId); + map['quoted_message_id'] = Variable(quotedMessageId); } if (!nullToAbsent || replyCount != null) { - map['reply_count'] = Variable(replyCount); + map['reply_count'] = Variable(replyCount); } if (!nullToAbsent || showInChannel != null) { - map['show_in_channel'] = Variable(showInChannel); - } - if (!nullToAbsent || shadowed != null) { - map['shadowed'] = Variable(shadowed); + map['show_in_channel'] = Variable(showInChannel); } + map['shadowed'] = Variable(shadowed); if (!nullToAbsent || command != null) { - map['command'] = Variable(command); - } - if (!nullToAbsent || createdAt != null) { - map['created_at'] = Variable(createdAt); - } - if (!nullToAbsent || updatedAt != null) { - map['updated_at'] = Variable(updatedAt); + map['command'] = Variable(command); } + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); if (!nullToAbsent || deletedAt != null) { - map['deleted_at'] = Variable(deletedAt); + map['deleted_at'] = Variable(deletedAt); } if (!nullToAbsent || userId != null) { - map['user_id'] = Variable(userId); - } - if (!nullToAbsent || pinned != null) { - map['pinned'] = Variable(pinned); + map['user_id'] = Variable(userId); } + map['pinned'] = Variable(pinned); if (!nullToAbsent || pinnedAt != null) { - map['pinned_at'] = Variable(pinnedAt); + map['pinned_at'] = Variable(pinnedAt); } if (!nullToAbsent || pinExpires != null) { - map['pin_expires'] = Variable(pinExpires); + map['pin_expires'] = Variable(pinExpires); } if (!nullToAbsent || pinnedByUserId != null) { - map['pinned_by_user_id'] = Variable(pinnedByUserId); + map['pinned_by_user_id'] = Variable(pinnedByUserId); } if (!nullToAbsent || channelCid != null) { - map['channel_cid'] = Variable(channelCid); + map['channel_cid'] = Variable(channelCid); } if (!nullToAbsent || extraData != null) { final converter = $MessagesTable.$converter5; - map['extra_data'] = Variable(converter.mapToSql(extraData)); + map['extra_data'] = Variable(converter.mapToSql(extraData)); } return map; } factory MessageEntity.fromJson(Map json, - {ValueSerializer serializer}) { + {ValueSerializer? serializer}) { serializer ??= moorRuntimeOptions.defaultSerializer; return MessageEntity( id: serializer.fromJson(json['id']), - messageText: serializer.fromJson(json['messageText']), + messageText: serializer.fromJson(json['messageText']), attachments: serializer.fromJson>(json['attachments']), status: serializer.fromJson(json['status']), type: serializer.fromJson(json['type']), mentionedUsers: serializer.fromJson>(json['mentionedUsers']), reactionCounts: - serializer.fromJson>(json['reactionCounts']), + serializer.fromJson?>(json['reactionCounts']), reactionScores: - serializer.fromJson>(json['reactionScores']), - parentId: serializer.fromJson(json['parentId']), - quotedMessageId: serializer.fromJson(json['quotedMessageId']), - replyCount: serializer.fromJson(json['replyCount']), - showInChannel: serializer.fromJson(json['showInChannel']), + serializer.fromJson?>(json['reactionScores']), + parentId: serializer.fromJson(json['parentId']), + quotedMessageId: serializer.fromJson(json['quotedMessageId']), + replyCount: serializer.fromJson(json['replyCount']), + showInChannel: serializer.fromJson(json['showInChannel']), shadowed: serializer.fromJson(json['shadowed']), - command: serializer.fromJson(json['command']), + command: serializer.fromJson(json['command']), createdAt: serializer.fromJson(json['createdAt']), updatedAt: serializer.fromJson(json['updatedAt']), - deletedAt: serializer.fromJson(json['deletedAt']), - userId: serializer.fromJson(json['userId']), + deletedAt: serializer.fromJson(json['deletedAt']), + userId: serializer.fromJson(json['userId']), pinned: serializer.fromJson(json['pinned']), - pinnedAt: serializer.fromJson(json['pinnedAt']), - pinExpires: serializer.fromJson(json['pinExpires']), - pinnedByUserId: serializer.fromJson(json['pinnedByUserId']), - channelCid: serializer.fromJson(json['channelCid']), - extraData: serializer.fromJson>(json['extraData']), + pinnedAt: serializer.fromJson(json['pinnedAt']), + pinExpires: serializer.fromJson(json['pinExpires']), + pinnedByUserId: serializer.fromJson(json['pinnedByUserId']), + channelCid: serializer.fromJson(json['channelCid']), + extraData: serializer.fromJson?>(json['extraData']), ); } @override - Map toJson({ValueSerializer serializer}) { + Map toJson({ValueSerializer? serializer}) { serializer ??= moorRuntimeOptions.defaultSerializer; return { 'id': serializer.toJson(id), - 'messageText': serializer.toJson(messageText), + 'messageText': serializer.toJson(messageText), 'attachments': serializer.toJson>(attachments), 'status': serializer.toJson(status), 'type': serializer.toJson(type), 'mentionedUsers': serializer.toJson>(mentionedUsers), - 'reactionCounts': serializer.toJson>(reactionCounts), - 'reactionScores': serializer.toJson>(reactionScores), - 'parentId': serializer.toJson(parentId), - 'quotedMessageId': serializer.toJson(quotedMessageId), - 'replyCount': serializer.toJson(replyCount), - 'showInChannel': serializer.toJson(showInChannel), + 'reactionCounts': serializer.toJson?>(reactionCounts), + 'reactionScores': serializer.toJson?>(reactionScores), + 'parentId': serializer.toJson(parentId), + 'quotedMessageId': serializer.toJson(quotedMessageId), + 'replyCount': serializer.toJson(replyCount), + 'showInChannel': serializer.toJson(showInChannel), 'shadowed': serializer.toJson(shadowed), - 'command': serializer.toJson(command), + 'command': serializer.toJson(command), 'createdAt': serializer.toJson(createdAt), 'updatedAt': serializer.toJson(updatedAt), - 'deletedAt': serializer.toJson(deletedAt), - 'userId': serializer.toJson(userId), + 'deletedAt': serializer.toJson(deletedAt), + 'userId': serializer.toJson(userId), 'pinned': serializer.toJson(pinned), - 'pinnedAt': serializer.toJson(pinnedAt), - 'pinExpires': serializer.toJson(pinExpires), - 'pinnedByUserId': serializer.toJson(pinnedByUserId), - 'channelCid': serializer.toJson(channelCid), - 'extraData': serializer.toJson>(extraData), + 'pinnedAt': serializer.toJson(pinnedAt), + 'pinExpires': serializer.toJson(pinExpires), + 'pinnedByUserId': serializer.toJson(pinnedByUserId), + 'channelCid': serializer.toJson(channelCid), + 'extraData': serializer.toJson?>(extraData), }; } MessageEntity copyWith( - {String id, - Value messageText = const Value.absent(), - Value> attachments = const Value.absent(), - Value status = const Value.absent(), - Value type = const Value.absent(), - Value> mentionedUsers = const Value.absent(), - Value> reactionCounts = const Value.absent(), - Value> reactionScores = const Value.absent(), - Value parentId = const Value.absent(), - Value quotedMessageId = const Value.absent(), - Value replyCount = const Value.absent(), - Value showInChannel = const Value.absent(), - Value shadowed = const Value.absent(), - Value command = const Value.absent(), - DateTime createdAt, - Value updatedAt = const Value.absent(), - Value deletedAt = const Value.absent(), - Value userId = const Value.absent(), - bool pinned, - Value pinnedAt = const Value.absent(), - Value pinExpires = const Value.absent(), - Value pinnedByUserId = const Value.absent(), - Value channelCid = const Value.absent(), - Value> extraData = const Value.absent()}) => + {String? id, + Value messageText = const Value.absent(), + List? attachments, + MessageSendingStatus? status, + String? type, + List? mentionedUsers, + Value?> reactionCounts = const Value.absent(), + Value?> reactionScores = const Value.absent(), + Value parentId = const Value.absent(), + Value quotedMessageId = const Value.absent(), + Value replyCount = const Value.absent(), + Value showInChannel = const Value.absent(), + bool? shadowed, + Value command = const Value.absent(), + DateTime? createdAt, + DateTime? updatedAt, + Value deletedAt = const Value.absent(), + Value userId = const Value.absent(), + bool? pinned, + Value pinnedAt = const Value.absent(), + Value pinExpires = const Value.absent(), + Value pinnedByUserId = const Value.absent(), + Value channelCid = const Value.absent(), + Value?> extraData = const Value.absent()}) => MessageEntity( id: id ?? this.id, messageText: messageText.present ? messageText.value : this.messageText, - attachments: attachments.present ? attachments.value : this.attachments, - status: status.present ? status.value : this.status, - type: type.present ? type.value : this.type, - mentionedUsers: - mentionedUsers.present ? mentionedUsers.value : this.mentionedUsers, + attachments: attachments ?? this.attachments, + status: status ?? this.status, + type: type ?? this.type, + mentionedUsers: mentionedUsers ?? this.mentionedUsers, reactionCounts: reactionCounts.present ? reactionCounts.value : this.reactionCounts, reactionScores: @@ -955,10 +978,10 @@ class MessageEntity extends DataClass implements Insertable { replyCount: replyCount.present ? replyCount.value : this.replyCount, showInChannel: showInChannel.present ? showInChannel.value : this.showInChannel, - shadowed: shadowed.present ? shadowed.value : this.shadowed, + shadowed: shadowed ?? this.shadowed, command: command.present ? command.value : this.command, createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt.present ? updatedAt.value : this.updatedAt, + updatedAt: updatedAt ?? this.updatedAt, deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, userId: userId.present ? userId.value : this.userId, pinned: pinned ?? this.pinned, @@ -1076,29 +1099,29 @@ class MessageEntity extends DataClass implements Insertable { class MessagesCompanion extends UpdateCompanion { final Value id; - final Value messageText; + final Value messageText; final Value> attachments; final Value status; final Value type; final Value> mentionedUsers; - final Value> reactionCounts; - final Value> reactionScores; - final Value parentId; - final Value quotedMessageId; - final Value replyCount; - final Value showInChannel; + final Value?> reactionCounts; + final Value?> reactionScores; + final Value parentId; + final Value quotedMessageId; + final Value replyCount; + final Value showInChannel; final Value shadowed; - final Value command; + final Value command; final Value createdAt; final Value updatedAt; - final Value deletedAt; - final Value userId; + final Value deletedAt; + final Value userId; final Value pinned; - final Value pinnedAt; - final Value pinExpires; - final Value pinnedByUserId; - final Value channelCid; - final Value> extraData; + final Value pinnedAt; + final Value pinExpires; + final Value pinnedByUserId; + final Value channelCid; + final Value?> extraData; const MessagesCompanion({ this.id = const Value.absent(), this.messageText = const Value.absent(), @@ -1126,12 +1149,12 @@ class MessagesCompanion extends UpdateCompanion { this.extraData = const Value.absent(), }); MessagesCompanion.insert({ - @required String id, + required String id, this.messageText = const Value.absent(), - this.attachments = const Value.absent(), + required List attachments, this.status = const Value.absent(), this.type = const Value.absent(), - this.mentionedUsers = const Value.absent(), + required List mentionedUsers, this.reactionCounts = const Value.absent(), this.reactionScores = const Value.absent(), this.parentId = const Value.absent(), @@ -1140,7 +1163,7 @@ class MessagesCompanion extends UpdateCompanion { this.showInChannel = const Value.absent(), this.shadowed = const Value.absent(), this.command = const Value.absent(), - @required DateTime createdAt, + this.createdAt = const Value.absent(), this.updatedAt = const Value.absent(), this.deletedAt = const Value.absent(), this.userId = const Value.absent(), @@ -1151,32 +1174,33 @@ class MessagesCompanion extends UpdateCompanion { this.channelCid = const Value.absent(), this.extraData = const Value.absent(), }) : id = Value(id), - createdAt = Value(createdAt); + attachments = Value(attachments), + mentionedUsers = Value(mentionedUsers); static Insertable custom({ - Expression id, - Expression messageText, - Expression attachments, - Expression status, - Expression type, - Expression mentionedUsers, - Expression reactionCounts, - Expression reactionScores, - Expression parentId, - Expression quotedMessageId, - Expression replyCount, - Expression showInChannel, - Expression shadowed, - Expression command, - Expression createdAt, - Expression updatedAt, - Expression deletedAt, - Expression userId, - Expression pinned, - Expression pinnedAt, - Expression pinExpires, - Expression pinnedByUserId, - Expression channelCid, - Expression extraData, + Expression? id, + Expression? messageText, + Expression>? attachments, + Expression? status, + Expression? type, + Expression>? mentionedUsers, + Expression?>? reactionCounts, + Expression?>? reactionScores, + Expression? parentId, + Expression? quotedMessageId, + Expression? replyCount, + Expression? showInChannel, + Expression? shadowed, + Expression? command, + Expression? createdAt, + Expression? updatedAt, + Expression? deletedAt, + Expression? userId, + Expression? pinned, + Expression? pinnedAt, + Expression? pinExpires, + Expression? pinnedByUserId, + Expression? channelCid, + Expression?>? extraData, }) { return RawValuesInsertable({ if (id != null) 'id': id, @@ -1207,30 +1231,30 @@ class MessagesCompanion extends UpdateCompanion { } MessagesCompanion copyWith( - {Value id, - Value messageText, - Value> attachments, - Value status, - Value type, - Value> mentionedUsers, - Value> reactionCounts, - Value> reactionScores, - Value parentId, - Value quotedMessageId, - Value replyCount, - Value showInChannel, - Value shadowed, - Value command, - Value createdAt, - Value updatedAt, - Value deletedAt, - Value userId, - Value pinned, - Value pinnedAt, - Value pinExpires, - Value pinnedByUserId, - Value channelCid, - Value> extraData}) { + {Value? id, + Value? messageText, + Value>? attachments, + Value? status, + Value? type, + Value>? mentionedUsers, + Value?>? reactionCounts, + Value?>? reactionScores, + Value? parentId, + Value? quotedMessageId, + Value? replyCount, + Value? showInChannel, + Value? shadowed, + Value? command, + Value? createdAt, + Value? updatedAt, + Value? deletedAt, + Value? userId, + Value? pinned, + Value? pinnedAt, + Value? pinExpires, + Value? pinnedByUserId, + Value? channelCid, + Value?>? extraData}) { return MessagesCompanion( id: id ?? this.id, messageText: messageText ?? this.messageText, @@ -1266,16 +1290,16 @@ class MessagesCompanion extends UpdateCompanion { map['id'] = Variable(id.value); } if (messageText.present) { - map['message_text'] = Variable(messageText.value); + map['message_text'] = Variable(messageText.value); } if (attachments.present) { final converter = $MessagesTable.$converter0; map['attachments'] = - Variable(converter.mapToSql(attachments.value)); + Variable(converter.mapToSql(attachments.value)!); } if (status.present) { final converter = $MessagesTable.$converter1; - map['status'] = Variable(converter.mapToSql(status.value)); + map['status'] = Variable(converter.mapToSql(status.value)!); } if (type.present) { map['type'] = Variable(type.value); @@ -1283,35 +1307,35 @@ class MessagesCompanion extends UpdateCompanion { if (mentionedUsers.present) { final converter = $MessagesTable.$converter2; map['mentioned_users'] = - Variable(converter.mapToSql(mentionedUsers.value)); + Variable(converter.mapToSql(mentionedUsers.value)!); } if (reactionCounts.present) { final converter = $MessagesTable.$converter3; map['reaction_counts'] = - Variable(converter.mapToSql(reactionCounts.value)); + Variable(converter.mapToSql(reactionCounts.value)); } if (reactionScores.present) { final converter = $MessagesTable.$converter4; map['reaction_scores'] = - Variable(converter.mapToSql(reactionScores.value)); + Variable(converter.mapToSql(reactionScores.value)); } if (parentId.present) { - map['parent_id'] = Variable(parentId.value); + map['parent_id'] = Variable(parentId.value); } if (quotedMessageId.present) { - map['quoted_message_id'] = Variable(quotedMessageId.value); + map['quoted_message_id'] = Variable(quotedMessageId.value); } if (replyCount.present) { - map['reply_count'] = Variable(replyCount.value); + map['reply_count'] = Variable(replyCount.value); } if (showInChannel.present) { - map['show_in_channel'] = Variable(showInChannel.value); + map['show_in_channel'] = Variable(showInChannel.value); } if (shadowed.present) { map['shadowed'] = Variable(shadowed.value); } if (command.present) { - map['command'] = Variable(command.value); + map['command'] = Variable(command.value); } if (createdAt.present) { map['created_at'] = Variable(createdAt.value); @@ -1320,29 +1344,30 @@ class MessagesCompanion extends UpdateCompanion { map['updated_at'] = Variable(updatedAt.value); } if (deletedAt.present) { - map['deleted_at'] = Variable(deletedAt.value); + map['deleted_at'] = Variable(deletedAt.value); } if (userId.present) { - map['user_id'] = Variable(userId.value); + map['user_id'] = Variable(userId.value); } if (pinned.present) { map['pinned'] = Variable(pinned.value); } if (pinnedAt.present) { - map['pinned_at'] = Variable(pinnedAt.value); + map['pinned_at'] = Variable(pinnedAt.value); } if (pinExpires.present) { - map['pin_expires'] = Variable(pinExpires.value); + map['pin_expires'] = Variable(pinExpires.value); } if (pinnedByUserId.present) { - map['pinned_by_user_id'] = Variable(pinnedByUserId.value); + map['pinned_by_user_id'] = Variable(pinnedByUserId.value); } if (channelCid.present) { - map['channel_cid'] = Variable(channelCid.value); + map['channel_cid'] = Variable(channelCid.value); } if (extraData.present) { final converter = $MessagesTable.$converter5; - map['extra_data'] = Variable(converter.mapToSql(extraData.value)); + map['extra_data'] = + Variable(converter.mapToSql(extraData.value)); } return map; } @@ -1382,12 +1407,11 @@ class MessagesCompanion extends UpdateCompanion { class $MessagesTable extends Messages with TableInfo<$MessagesTable, MessageEntity> { final GeneratedDatabase _db; - final String _alias; + final String? _alias; $MessagesTable(this._db, [this._alias]); final VerificationMeta _idMeta = const VerificationMeta('id'); - GeneratedTextColumn _id; @override - GeneratedTextColumn get id => _id ??= _constructId(); + late final GeneratedTextColumn id = _constructId(); GeneratedTextColumn _constructId() { return GeneratedTextColumn( 'id', @@ -1398,10 +1422,8 @@ class $MessagesTable extends Messages final VerificationMeta _messageTextMeta = const VerificationMeta('messageText'); - GeneratedTextColumn _messageText; @override - GeneratedTextColumn get messageText => - _messageText ??= _constructMessageText(); + late final GeneratedTextColumn messageText = _constructMessageText(); GeneratedTextColumn _constructMessageText() { return GeneratedTextColumn( 'message_text', @@ -1412,62 +1434,48 @@ class $MessagesTable extends Messages final VerificationMeta _attachmentsMeta = const VerificationMeta('attachments'); - GeneratedTextColumn _attachments; @override - GeneratedTextColumn get attachments => - _attachments ??= _constructAttachments(); + late final GeneratedTextColumn attachments = _constructAttachments(); GeneratedTextColumn _constructAttachments() { return GeneratedTextColumn( 'attachments', $tableName, - true, + false, ); } final VerificationMeta _statusMeta = const VerificationMeta('status'); - GeneratedIntColumn _status; @override - GeneratedIntColumn get status => _status ??= _constructStatus(); + late final GeneratedIntColumn status = _constructStatus(); GeneratedIntColumn _constructStatus() { - return GeneratedIntColumn( - 'status', - $tableName, - true, - ); + return GeneratedIntColumn('status', $tableName, false, + defaultValue: const Constant(1)); } final VerificationMeta _typeMeta = const VerificationMeta('type'); - GeneratedTextColumn _type; @override - GeneratedTextColumn get type => _type ??= _constructType(); + late final GeneratedTextColumn type = _constructType(); GeneratedTextColumn _constructType() { - return GeneratedTextColumn( - 'type', - $tableName, - true, - ); + return GeneratedTextColumn('type', $tableName, false, + defaultValue: const Constant('regular')); } final VerificationMeta _mentionedUsersMeta = const VerificationMeta('mentionedUsers'); - GeneratedTextColumn _mentionedUsers; @override - GeneratedTextColumn get mentionedUsers => - _mentionedUsers ??= _constructMentionedUsers(); + late final GeneratedTextColumn mentionedUsers = _constructMentionedUsers(); GeneratedTextColumn _constructMentionedUsers() { return GeneratedTextColumn( 'mentioned_users', $tableName, - true, + false, ); } final VerificationMeta _reactionCountsMeta = const VerificationMeta('reactionCounts'); - GeneratedTextColumn _reactionCounts; @override - GeneratedTextColumn get reactionCounts => - _reactionCounts ??= _constructReactionCounts(); + late final GeneratedTextColumn reactionCounts = _constructReactionCounts(); GeneratedTextColumn _constructReactionCounts() { return GeneratedTextColumn( 'reaction_counts', @@ -1478,10 +1486,8 @@ class $MessagesTable extends Messages final VerificationMeta _reactionScoresMeta = const VerificationMeta('reactionScores'); - GeneratedTextColumn _reactionScores; @override - GeneratedTextColumn get reactionScores => - _reactionScores ??= _constructReactionScores(); + late final GeneratedTextColumn reactionScores = _constructReactionScores(); GeneratedTextColumn _constructReactionScores() { return GeneratedTextColumn( 'reaction_scores', @@ -1491,9 +1497,8 @@ class $MessagesTable extends Messages } final VerificationMeta _parentIdMeta = const VerificationMeta('parentId'); - GeneratedTextColumn _parentId; @override - GeneratedTextColumn get parentId => _parentId ??= _constructParentId(); + late final GeneratedTextColumn parentId = _constructParentId(); GeneratedTextColumn _constructParentId() { return GeneratedTextColumn( 'parent_id', @@ -1504,10 +1509,8 @@ class $MessagesTable extends Messages final VerificationMeta _quotedMessageIdMeta = const VerificationMeta('quotedMessageId'); - GeneratedTextColumn _quotedMessageId; @override - GeneratedTextColumn get quotedMessageId => - _quotedMessageId ??= _constructQuotedMessageId(); + late final GeneratedTextColumn quotedMessageId = _constructQuotedMessageId(); GeneratedTextColumn _constructQuotedMessageId() { return GeneratedTextColumn( 'quoted_message_id', @@ -1517,9 +1520,8 @@ class $MessagesTable extends Messages } final VerificationMeta _replyCountMeta = const VerificationMeta('replyCount'); - GeneratedIntColumn _replyCount; @override - GeneratedIntColumn get replyCount => _replyCount ??= _constructReplyCount(); + late final GeneratedIntColumn replyCount = _constructReplyCount(); GeneratedIntColumn _constructReplyCount() { return GeneratedIntColumn( 'reply_count', @@ -1530,10 +1532,8 @@ class $MessagesTable extends Messages final VerificationMeta _showInChannelMeta = const VerificationMeta('showInChannel'); - GeneratedBoolColumn _showInChannel; @override - GeneratedBoolColumn get showInChannel => - _showInChannel ??= _constructShowInChannel(); + late final GeneratedBoolColumn showInChannel = _constructShowInChannel(); GeneratedBoolColumn _constructShowInChannel() { return GeneratedBoolColumn( 'show_in_channel', @@ -1543,21 +1543,16 @@ class $MessagesTable extends Messages } final VerificationMeta _shadowedMeta = const VerificationMeta('shadowed'); - GeneratedBoolColumn _shadowed; @override - GeneratedBoolColumn get shadowed => _shadowed ??= _constructShadowed(); + late final GeneratedBoolColumn shadowed = _constructShadowed(); GeneratedBoolColumn _constructShadowed() { - return GeneratedBoolColumn( - 'shadowed', - $tableName, - true, - ); + return GeneratedBoolColumn('shadowed', $tableName, false, + defaultValue: const Constant(false)); } final VerificationMeta _commandMeta = const VerificationMeta('command'); - GeneratedTextColumn _command; @override - GeneratedTextColumn get command => _command ??= _constructCommand(); + late final GeneratedTextColumn command = _constructCommand(); GeneratedTextColumn _constructCommand() { return GeneratedTextColumn( 'command', @@ -1567,33 +1562,24 @@ class $MessagesTable extends Messages } final VerificationMeta _createdAtMeta = const VerificationMeta('createdAt'); - GeneratedDateTimeColumn _createdAt; @override - GeneratedDateTimeColumn get createdAt => _createdAt ??= _constructCreatedAt(); + late final GeneratedDateTimeColumn createdAt = _constructCreatedAt(); GeneratedDateTimeColumn _constructCreatedAt() { - return GeneratedDateTimeColumn( - 'created_at', - $tableName, - false, - ); + return GeneratedDateTimeColumn('created_at', $tableName, false, + defaultValue: currentDateAndTime); } final VerificationMeta _updatedAtMeta = const VerificationMeta('updatedAt'); - GeneratedDateTimeColumn _updatedAt; @override - GeneratedDateTimeColumn get updatedAt => _updatedAt ??= _constructUpdatedAt(); + late final GeneratedDateTimeColumn updatedAt = _constructUpdatedAt(); GeneratedDateTimeColumn _constructUpdatedAt() { - return GeneratedDateTimeColumn( - 'updated_at', - $tableName, - true, - ); + return GeneratedDateTimeColumn('updated_at', $tableName, false, + defaultValue: currentDateAndTime); } final VerificationMeta _deletedAtMeta = const VerificationMeta('deletedAt'); - GeneratedDateTimeColumn _deletedAt; @override - GeneratedDateTimeColumn get deletedAt => _deletedAt ??= _constructDeletedAt(); + late final GeneratedDateTimeColumn deletedAt = _constructDeletedAt(); GeneratedDateTimeColumn _constructDeletedAt() { return GeneratedDateTimeColumn( 'deleted_at', @@ -1603,9 +1589,8 @@ class $MessagesTable extends Messages } final VerificationMeta _userIdMeta = const VerificationMeta('userId'); - GeneratedTextColumn _userId; @override - GeneratedTextColumn get userId => _userId ??= _constructUserId(); + late final GeneratedTextColumn userId = _constructUserId(); GeneratedTextColumn _constructUserId() { return GeneratedTextColumn( 'user_id', @@ -1615,18 +1600,16 @@ class $MessagesTable extends Messages } final VerificationMeta _pinnedMeta = const VerificationMeta('pinned'); - GeneratedBoolColumn _pinned; @override - GeneratedBoolColumn get pinned => _pinned ??= _constructPinned(); + late final GeneratedBoolColumn pinned = _constructPinned(); GeneratedBoolColumn _constructPinned() { return GeneratedBoolColumn('pinned', $tableName, false, defaultValue: const Constant(false)); } final VerificationMeta _pinnedAtMeta = const VerificationMeta('pinnedAt'); - GeneratedDateTimeColumn _pinnedAt; @override - GeneratedDateTimeColumn get pinnedAt => _pinnedAt ??= _constructPinnedAt(); + late final GeneratedDateTimeColumn pinnedAt = _constructPinnedAt(); GeneratedDateTimeColumn _constructPinnedAt() { return GeneratedDateTimeColumn( 'pinned_at', @@ -1636,10 +1619,8 @@ class $MessagesTable extends Messages } final VerificationMeta _pinExpiresMeta = const VerificationMeta('pinExpires'); - GeneratedDateTimeColumn _pinExpires; @override - GeneratedDateTimeColumn get pinExpires => - _pinExpires ??= _constructPinExpires(); + late final GeneratedDateTimeColumn pinExpires = _constructPinExpires(); GeneratedDateTimeColumn _constructPinExpires() { return GeneratedDateTimeColumn( 'pin_expires', @@ -1650,10 +1631,8 @@ class $MessagesTable extends Messages final VerificationMeta _pinnedByUserIdMeta = const VerificationMeta('pinnedByUserId'); - GeneratedTextColumn _pinnedByUserId; @override - GeneratedTextColumn get pinnedByUserId => - _pinnedByUserId ??= _constructPinnedByUserId(); + late final GeneratedTextColumn pinnedByUserId = _constructPinnedByUserId(); GeneratedTextColumn _constructPinnedByUserId() { return GeneratedTextColumn( 'pinned_by_user_id', @@ -1663,9 +1642,8 @@ class $MessagesTable extends Messages } final VerificationMeta _channelCidMeta = const VerificationMeta('channelCid'); - GeneratedTextColumn _channelCid; @override - GeneratedTextColumn get channelCid => _channelCid ??= _constructChannelCid(); + late final GeneratedTextColumn channelCid = _constructChannelCid(); GeneratedTextColumn _constructChannelCid() { return GeneratedTextColumn('channel_cid', $tableName, true, $customConstraints: @@ -1673,9 +1651,8 @@ class $MessagesTable extends Messages } final VerificationMeta _extraDataMeta = const VerificationMeta('extraData'); - GeneratedTextColumn _extraData; @override - GeneratedTextColumn get extraData => _extraData ??= _constructExtraData(); + late final GeneratedTextColumn extraData = _constructExtraData(); GeneratedTextColumn _constructExtraData() { return GeneratedTextColumn( 'extra_data', @@ -1723,7 +1700,7 @@ class $MessagesTable extends Messages final context = VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('id')) { - context.handle(_idMeta, id.isAcceptableOrUnknown(data['id'], _idMeta)); + context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); } else if (isInserting) { context.missing(_idMeta); } @@ -1731,90 +1708,88 @@ class $MessagesTable extends Messages context.handle( _messageTextMeta, messageText.isAcceptableOrUnknown( - data['message_text'], _messageTextMeta)); + data['message_text']!, _messageTextMeta)); } context.handle(_attachmentsMeta, const VerificationResult.success()); context.handle(_statusMeta, const VerificationResult.success()); if (data.containsKey('type')) { context.handle( - _typeMeta, type.isAcceptableOrUnknown(data['type'], _typeMeta)); + _typeMeta, type.isAcceptableOrUnknown(data['type']!, _typeMeta)); } context.handle(_mentionedUsersMeta, const VerificationResult.success()); context.handle(_reactionCountsMeta, const VerificationResult.success()); context.handle(_reactionScoresMeta, const VerificationResult.success()); if (data.containsKey('parent_id')) { context.handle(_parentIdMeta, - parentId.isAcceptableOrUnknown(data['parent_id'], _parentIdMeta)); + parentId.isAcceptableOrUnknown(data['parent_id']!, _parentIdMeta)); } if (data.containsKey('quoted_message_id')) { context.handle( _quotedMessageIdMeta, quotedMessageId.isAcceptableOrUnknown( - data['quoted_message_id'], _quotedMessageIdMeta)); + data['quoted_message_id']!, _quotedMessageIdMeta)); } if (data.containsKey('reply_count')) { context.handle( _replyCountMeta, replyCount.isAcceptableOrUnknown( - data['reply_count'], _replyCountMeta)); + data['reply_count']!, _replyCountMeta)); } if (data.containsKey('show_in_channel')) { context.handle( _showInChannelMeta, showInChannel.isAcceptableOrUnknown( - data['show_in_channel'], _showInChannelMeta)); + data['show_in_channel']!, _showInChannelMeta)); } if (data.containsKey('shadowed')) { context.handle(_shadowedMeta, - shadowed.isAcceptableOrUnknown(data['shadowed'], _shadowedMeta)); + shadowed.isAcceptableOrUnknown(data['shadowed']!, _shadowedMeta)); } if (data.containsKey('command')) { context.handle(_commandMeta, - command.isAcceptableOrUnknown(data['command'], _commandMeta)); + command.isAcceptableOrUnknown(data['command']!, _commandMeta)); } if (data.containsKey('created_at')) { context.handle(_createdAtMeta, - createdAt.isAcceptableOrUnknown(data['created_at'], _createdAtMeta)); - } else if (isInserting) { - context.missing(_createdAtMeta); + createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta)); } if (data.containsKey('updated_at')) { context.handle(_updatedAtMeta, - updatedAt.isAcceptableOrUnknown(data['updated_at'], _updatedAtMeta)); + updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta)); } if (data.containsKey('deleted_at')) { context.handle(_deletedAtMeta, - deletedAt.isAcceptableOrUnknown(data['deleted_at'], _deletedAtMeta)); + deletedAt.isAcceptableOrUnknown(data['deleted_at']!, _deletedAtMeta)); } if (data.containsKey('user_id')) { context.handle(_userIdMeta, - userId.isAcceptableOrUnknown(data['user_id'], _userIdMeta)); + userId.isAcceptableOrUnknown(data['user_id']!, _userIdMeta)); } if (data.containsKey('pinned')) { context.handle(_pinnedMeta, - pinned.isAcceptableOrUnknown(data['pinned'], _pinnedMeta)); + pinned.isAcceptableOrUnknown(data['pinned']!, _pinnedMeta)); } if (data.containsKey('pinned_at')) { context.handle(_pinnedAtMeta, - pinnedAt.isAcceptableOrUnknown(data['pinned_at'], _pinnedAtMeta)); + pinnedAt.isAcceptableOrUnknown(data['pinned_at']!, _pinnedAtMeta)); } if (data.containsKey('pin_expires')) { context.handle( _pinExpiresMeta, pinExpires.isAcceptableOrUnknown( - data['pin_expires'], _pinExpiresMeta)); + data['pin_expires']!, _pinExpiresMeta)); } if (data.containsKey('pinned_by_user_id')) { context.handle( _pinnedByUserIdMeta, pinnedByUserId.isAcceptableOrUnknown( - data['pinned_by_user_id'], _pinnedByUserIdMeta)); + data['pinned_by_user_id']!, _pinnedByUserIdMeta)); } if (data.containsKey('channel_cid')) { context.handle( _channelCidMeta, channelCid.isAcceptableOrUnknown( - data['channel_cid'], _channelCidMeta)); + data['channel_cid']!, _channelCidMeta)); } context.handle(_extraDataMeta, const VerificationResult.success()); return context; @@ -1823,7 +1798,7 @@ class $MessagesTable extends Messages @override Set get $primaryKey => {id}; @override - MessageEntity map(Map data, {String tablePrefix}) { + MessageEntity map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : null; return MessageEntity.fromData(data, _db, prefix: effectivePrefix); } @@ -1849,50 +1824,98 @@ class $MessagesTable extends Messages class PinnedMessageEntity extends DataClass implements Insertable { + /// The message id final String id; - final String messageText; + + /// The text of this message + final String? messageText; + + /// The list of attachments, either provided by the user + /// or generated from a command or as a result of URL scraping. final List attachments; + + /// The status of a sending message final MessageSendingStatus status; + + /// The message type final String type; + + /// The list of user mentioned in the message final List mentionedUsers; - final Map reactionCounts; - final Map reactionScores; - final String parentId; - final String quotedMessageId; - final int replyCount; - final bool showInChannel; + + /// A map describing the count of number of every reaction + final Map? reactionCounts; + + /// A map describing the count of score of every reaction + final Map? reactionScores; + + /// The ID of the parent message, if the message is a thread reply. + final String? parentId; + + /// The ID of the quoted message, if the message is a quoted reply. + final String? quotedMessageId; + + /// Number of replies for this message. + final int? replyCount; + + /// Check if this message needs to show in the channel. + final bool? showInChannel; + + /// If true the message is shadowed final bool shadowed; - final String command; + + /// A used command name. + final String? command; + + /// The DateTime when the message was created. final DateTime createdAt; + + /// The DateTime when the message was updated last time. final DateTime updatedAt; - final DateTime deletedAt; - final String userId; + + /// The DateTime when the message was deleted. + final DateTime? deletedAt; + + /// Id of the User who sent the message + final String? userId; + + /// Whether the message is pinned or not final bool pinned; - final DateTime pinnedAt; - final DateTime pinExpires; - final String pinnedByUserId; - final String channelCid; - final Map extraData; + + /// The DateTime at which the message was pinned + final DateTime? pinnedAt; + + /// The DateTime on which the message pin expires + final DateTime? pinExpires; + + /// Id of the User who pinned the message + final String? pinnedByUserId; + + /// The channel cid of which this message is part of + final String? channelCid; + + /// Message custom extraData + final Map? extraData; PinnedMessageEntity( - {@required this.id, + {required this.id, this.messageText, - this.attachments, - this.status, - this.type, - this.mentionedUsers, + required this.attachments, + required this.status, + required this.type, + required this.mentionedUsers, this.reactionCounts, this.reactionScores, this.parentId, this.quotedMessageId, this.replyCount, this.showInChannel, - this.shadowed, + required this.shadowed, this.command, - @required this.createdAt, - this.updatedAt, + required this.createdAt, + required this.updatedAt, this.deletedAt, this.userId, - @required this.pinned, + required this.pinned, this.pinnedAt, this.pinExpires, this.pinnedByUserId, @@ -1900,23 +1923,23 @@ class PinnedMessageEntity extends DataClass this.extraData}); factory PinnedMessageEntity.fromData( Map data, GeneratedDatabase db, - {String prefix}) { + {String? prefix}) { final effectivePrefix = prefix ?? ''; final stringType = db.typeSystem.forDartType(); final intType = db.typeSystem.forDartType(); final boolType = db.typeSystem.forDartType(); final dateTimeType = db.typeSystem.forDartType(); return PinnedMessageEntity( - id: stringType.mapFromDatabaseResponse(data['${effectivePrefix}id']), + id: stringType.mapFromDatabaseResponse(data['${effectivePrefix}id'])!, messageText: stringType .mapFromDatabaseResponse(data['${effectivePrefix}message_text']), attachments: $PinnedMessagesTable.$converter0.mapToDart(stringType - .mapFromDatabaseResponse(data['${effectivePrefix}attachments'])), + .mapFromDatabaseResponse(data['${effectivePrefix}attachments']))!, status: $PinnedMessagesTable.$converter1.mapToDart( - intType.mapFromDatabaseResponse(data['${effectivePrefix}status'])), - type: stringType.mapFromDatabaseResponse(data['${effectivePrefix}type']), + intType.mapFromDatabaseResponse(data['${effectivePrefix}status']))!, + type: stringType.mapFromDatabaseResponse(data['${effectivePrefix}type'])!, mentionedUsers: $PinnedMessagesTable.$converter2.mapToDart(stringType - .mapFromDatabaseResponse(data['${effectivePrefix}mentioned_users'])), + .mapFromDatabaseResponse(data['${effectivePrefix}mentioned_users']))!, reactionCounts: $PinnedMessagesTable.$converter3.mapToDart(stringType .mapFromDatabaseResponse(data['${effectivePrefix}reaction_counts'])), reactionScores: $PinnedMessagesTable.$converter4.mapToDart(stringType @@ -1930,19 +1953,19 @@ class PinnedMessageEntity extends DataClass showInChannel: boolType .mapFromDatabaseResponse(data['${effectivePrefix}show_in_channel']), shadowed: - boolType.mapFromDatabaseResponse(data['${effectivePrefix}shadowed']), + boolType.mapFromDatabaseResponse(data['${effectivePrefix}shadowed'])!, command: stringType.mapFromDatabaseResponse(data['${effectivePrefix}command']), createdAt: dateTimeType - .mapFromDatabaseResponse(data['${effectivePrefix}created_at']), + .mapFromDatabaseResponse(data['${effectivePrefix}created_at'])!, updatedAt: dateTimeType - .mapFromDatabaseResponse(data['${effectivePrefix}updated_at']), + .mapFromDatabaseResponse(data['${effectivePrefix}updated_at'])!, deletedAt: dateTimeType .mapFromDatabaseResponse(data['${effectivePrefix}deleted_at']), userId: stringType.mapFromDatabaseResponse(data['${effectivePrefix}user_id']), pinned: - boolType.mapFromDatabaseResponse(data['${effectivePrefix}pinned']), + boolType.mapFromDatabaseResponse(data['${effectivePrefix}pinned'])!, pinnedAt: dateTimeType .mapFromDatabaseResponse(data['${effectivePrefix}pinned_at']), pinExpires: dateTimeType @@ -1958,186 +1981,173 @@ class PinnedMessageEntity extends DataClass @override Map toColumns(bool nullToAbsent) { final map = {}; - if (!nullToAbsent || id != null) { - map['id'] = Variable(id); - } + map['id'] = Variable(id); if (!nullToAbsent || messageText != null) { - map['message_text'] = Variable(messageText); + map['message_text'] = Variable(messageText); } - if (!nullToAbsent || attachments != null) { + { final converter = $PinnedMessagesTable.$converter0; - map['attachments'] = Variable(converter.mapToSql(attachments)); + map['attachments'] = Variable(converter.mapToSql(attachments)!); } - if (!nullToAbsent || status != null) { + { final converter = $PinnedMessagesTable.$converter1; - map['status'] = Variable(converter.mapToSql(status)); + map['status'] = Variable(converter.mapToSql(status)!); } - if (!nullToAbsent || type != null) { - map['type'] = Variable(type); - } - if (!nullToAbsent || mentionedUsers != null) { + map['type'] = Variable(type); + { final converter = $PinnedMessagesTable.$converter2; map['mentioned_users'] = - Variable(converter.mapToSql(mentionedUsers)); + Variable(converter.mapToSql(mentionedUsers)!); } if (!nullToAbsent || reactionCounts != null) { final converter = $PinnedMessagesTable.$converter3; map['reaction_counts'] = - Variable(converter.mapToSql(reactionCounts)); + Variable(converter.mapToSql(reactionCounts)); } if (!nullToAbsent || reactionScores != null) { final converter = $PinnedMessagesTable.$converter4; map['reaction_scores'] = - Variable(converter.mapToSql(reactionScores)); + Variable(converter.mapToSql(reactionScores)); } if (!nullToAbsent || parentId != null) { - map['parent_id'] = Variable(parentId); + map['parent_id'] = Variable(parentId); } if (!nullToAbsent || quotedMessageId != null) { - map['quoted_message_id'] = Variable(quotedMessageId); + map['quoted_message_id'] = Variable(quotedMessageId); } if (!nullToAbsent || replyCount != null) { - map['reply_count'] = Variable(replyCount); + map['reply_count'] = Variable(replyCount); } if (!nullToAbsent || showInChannel != null) { - map['show_in_channel'] = Variable(showInChannel); - } - if (!nullToAbsent || shadowed != null) { - map['shadowed'] = Variable(shadowed); + map['show_in_channel'] = Variable(showInChannel); } + map['shadowed'] = Variable(shadowed); if (!nullToAbsent || command != null) { - map['command'] = Variable(command); - } - if (!nullToAbsent || createdAt != null) { - map['created_at'] = Variable(createdAt); - } - if (!nullToAbsent || updatedAt != null) { - map['updated_at'] = Variable(updatedAt); + map['command'] = Variable(command); } + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); if (!nullToAbsent || deletedAt != null) { - map['deleted_at'] = Variable(deletedAt); + map['deleted_at'] = Variable(deletedAt); } if (!nullToAbsent || userId != null) { - map['user_id'] = Variable(userId); - } - if (!nullToAbsent || pinned != null) { - map['pinned'] = Variable(pinned); + map['user_id'] = Variable(userId); } + map['pinned'] = Variable(pinned); if (!nullToAbsent || pinnedAt != null) { - map['pinned_at'] = Variable(pinnedAt); + map['pinned_at'] = Variable(pinnedAt); } if (!nullToAbsent || pinExpires != null) { - map['pin_expires'] = Variable(pinExpires); + map['pin_expires'] = Variable(pinExpires); } if (!nullToAbsent || pinnedByUserId != null) { - map['pinned_by_user_id'] = Variable(pinnedByUserId); + map['pinned_by_user_id'] = Variable(pinnedByUserId); } if (!nullToAbsent || channelCid != null) { - map['channel_cid'] = Variable(channelCid); + map['channel_cid'] = Variable(channelCid); } if (!nullToAbsent || extraData != null) { final converter = $PinnedMessagesTable.$converter5; - map['extra_data'] = Variable(converter.mapToSql(extraData)); + map['extra_data'] = Variable(converter.mapToSql(extraData)); } return map; } factory PinnedMessageEntity.fromJson(Map json, - {ValueSerializer serializer}) { + {ValueSerializer? serializer}) { serializer ??= moorRuntimeOptions.defaultSerializer; return PinnedMessageEntity( id: serializer.fromJson(json['id']), - messageText: serializer.fromJson(json['messageText']), + messageText: serializer.fromJson(json['messageText']), attachments: serializer.fromJson>(json['attachments']), status: serializer.fromJson(json['status']), type: serializer.fromJson(json['type']), mentionedUsers: serializer.fromJson>(json['mentionedUsers']), reactionCounts: - serializer.fromJson>(json['reactionCounts']), + serializer.fromJson?>(json['reactionCounts']), reactionScores: - serializer.fromJson>(json['reactionScores']), - parentId: serializer.fromJson(json['parentId']), - quotedMessageId: serializer.fromJson(json['quotedMessageId']), - replyCount: serializer.fromJson(json['replyCount']), - showInChannel: serializer.fromJson(json['showInChannel']), + serializer.fromJson?>(json['reactionScores']), + parentId: serializer.fromJson(json['parentId']), + quotedMessageId: serializer.fromJson(json['quotedMessageId']), + replyCount: serializer.fromJson(json['replyCount']), + showInChannel: serializer.fromJson(json['showInChannel']), shadowed: serializer.fromJson(json['shadowed']), - command: serializer.fromJson(json['command']), + command: serializer.fromJson(json['command']), createdAt: serializer.fromJson(json['createdAt']), updatedAt: serializer.fromJson(json['updatedAt']), - deletedAt: serializer.fromJson(json['deletedAt']), - userId: serializer.fromJson(json['userId']), + deletedAt: serializer.fromJson(json['deletedAt']), + userId: serializer.fromJson(json['userId']), pinned: serializer.fromJson(json['pinned']), - pinnedAt: serializer.fromJson(json['pinnedAt']), - pinExpires: serializer.fromJson(json['pinExpires']), - pinnedByUserId: serializer.fromJson(json['pinnedByUserId']), - channelCid: serializer.fromJson(json['channelCid']), - extraData: serializer.fromJson>(json['extraData']), + pinnedAt: serializer.fromJson(json['pinnedAt']), + pinExpires: serializer.fromJson(json['pinExpires']), + pinnedByUserId: serializer.fromJson(json['pinnedByUserId']), + channelCid: serializer.fromJson(json['channelCid']), + extraData: serializer.fromJson?>(json['extraData']), ); } @override - Map toJson({ValueSerializer serializer}) { + Map toJson({ValueSerializer? serializer}) { serializer ??= moorRuntimeOptions.defaultSerializer; return { 'id': serializer.toJson(id), - 'messageText': serializer.toJson(messageText), + 'messageText': serializer.toJson(messageText), 'attachments': serializer.toJson>(attachments), 'status': serializer.toJson(status), 'type': serializer.toJson(type), 'mentionedUsers': serializer.toJson>(mentionedUsers), - 'reactionCounts': serializer.toJson>(reactionCounts), - 'reactionScores': serializer.toJson>(reactionScores), - 'parentId': serializer.toJson(parentId), - 'quotedMessageId': serializer.toJson(quotedMessageId), - 'replyCount': serializer.toJson(replyCount), - 'showInChannel': serializer.toJson(showInChannel), + 'reactionCounts': serializer.toJson?>(reactionCounts), + 'reactionScores': serializer.toJson?>(reactionScores), + 'parentId': serializer.toJson(parentId), + 'quotedMessageId': serializer.toJson(quotedMessageId), + 'replyCount': serializer.toJson(replyCount), + 'showInChannel': serializer.toJson(showInChannel), 'shadowed': serializer.toJson(shadowed), - 'command': serializer.toJson(command), + 'command': serializer.toJson(command), 'createdAt': serializer.toJson(createdAt), 'updatedAt': serializer.toJson(updatedAt), - 'deletedAt': serializer.toJson(deletedAt), - 'userId': serializer.toJson(userId), + 'deletedAt': serializer.toJson(deletedAt), + 'userId': serializer.toJson(userId), 'pinned': serializer.toJson(pinned), - 'pinnedAt': serializer.toJson(pinnedAt), - 'pinExpires': serializer.toJson(pinExpires), - 'pinnedByUserId': serializer.toJson(pinnedByUserId), - 'channelCid': serializer.toJson(channelCid), - 'extraData': serializer.toJson>(extraData), + 'pinnedAt': serializer.toJson(pinnedAt), + 'pinExpires': serializer.toJson(pinExpires), + 'pinnedByUserId': serializer.toJson(pinnedByUserId), + 'channelCid': serializer.toJson(channelCid), + 'extraData': serializer.toJson?>(extraData), }; } PinnedMessageEntity copyWith( - {String id, - Value messageText = const Value.absent(), - Value> attachments = const Value.absent(), - Value status = const Value.absent(), - Value type = const Value.absent(), - Value> mentionedUsers = const Value.absent(), - Value> reactionCounts = const Value.absent(), - Value> reactionScores = const Value.absent(), - Value parentId = const Value.absent(), - Value quotedMessageId = const Value.absent(), - Value replyCount = const Value.absent(), - Value showInChannel = const Value.absent(), - Value shadowed = const Value.absent(), - Value command = const Value.absent(), - DateTime createdAt, - Value updatedAt = const Value.absent(), - Value deletedAt = const Value.absent(), - Value userId = const Value.absent(), - bool pinned, - Value pinnedAt = const Value.absent(), - Value pinExpires = const Value.absent(), - Value pinnedByUserId = const Value.absent(), - Value channelCid = const Value.absent(), - Value> extraData = const Value.absent()}) => + {String? id, + Value messageText = const Value.absent(), + List? attachments, + MessageSendingStatus? status, + String? type, + List? mentionedUsers, + Value?> reactionCounts = const Value.absent(), + Value?> reactionScores = const Value.absent(), + Value parentId = const Value.absent(), + Value quotedMessageId = const Value.absent(), + Value replyCount = const Value.absent(), + Value showInChannel = const Value.absent(), + bool? shadowed, + Value command = const Value.absent(), + DateTime? createdAt, + DateTime? updatedAt, + Value deletedAt = const Value.absent(), + Value userId = const Value.absent(), + bool? pinned, + Value pinnedAt = const Value.absent(), + Value pinExpires = const Value.absent(), + Value pinnedByUserId = const Value.absent(), + Value channelCid = const Value.absent(), + Value?> extraData = const Value.absent()}) => PinnedMessageEntity( id: id ?? this.id, messageText: messageText.present ? messageText.value : this.messageText, - attachments: attachments.present ? attachments.value : this.attachments, - status: status.present ? status.value : this.status, - type: type.present ? type.value : this.type, - mentionedUsers: - mentionedUsers.present ? mentionedUsers.value : this.mentionedUsers, + attachments: attachments ?? this.attachments, + status: status ?? this.status, + type: type ?? this.type, + mentionedUsers: mentionedUsers ?? this.mentionedUsers, reactionCounts: reactionCounts.present ? reactionCounts.value : this.reactionCounts, reactionScores: @@ -2149,10 +2159,10 @@ class PinnedMessageEntity extends DataClass replyCount: replyCount.present ? replyCount.value : this.replyCount, showInChannel: showInChannel.present ? showInChannel.value : this.showInChannel, - shadowed: shadowed.present ? shadowed.value : this.shadowed, + shadowed: shadowed ?? this.shadowed, command: command.present ? command.value : this.command, createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt.present ? updatedAt.value : this.updatedAt, + updatedAt: updatedAt ?? this.updatedAt, deletedAt: deletedAt.present ? deletedAt.value : this.deletedAt, userId: userId.present ? userId.value : this.userId, pinned: pinned ?? this.pinned, @@ -2270,29 +2280,29 @@ class PinnedMessageEntity extends DataClass class PinnedMessagesCompanion extends UpdateCompanion { final Value id; - final Value messageText; + final Value messageText; final Value> attachments; final Value status; final Value type; final Value> mentionedUsers; - final Value> reactionCounts; - final Value> reactionScores; - final Value parentId; - final Value quotedMessageId; - final Value replyCount; - final Value showInChannel; + final Value?> reactionCounts; + final Value?> reactionScores; + final Value parentId; + final Value quotedMessageId; + final Value replyCount; + final Value showInChannel; final Value shadowed; - final Value command; + final Value command; final Value createdAt; final Value updatedAt; - final Value deletedAt; - final Value userId; + final Value deletedAt; + final Value userId; final Value pinned; - final Value pinnedAt; - final Value pinExpires; - final Value pinnedByUserId; - final Value channelCid; - final Value> extraData; + final Value pinnedAt; + final Value pinExpires; + final Value pinnedByUserId; + final Value channelCid; + final Value?> extraData; const PinnedMessagesCompanion({ this.id = const Value.absent(), this.messageText = const Value.absent(), @@ -2320,12 +2330,12 @@ class PinnedMessagesCompanion extends UpdateCompanion { this.extraData = const Value.absent(), }); PinnedMessagesCompanion.insert({ - @required String id, + required String id, this.messageText = const Value.absent(), - this.attachments = const Value.absent(), + required List attachments, this.status = const Value.absent(), this.type = const Value.absent(), - this.mentionedUsers = const Value.absent(), + required List mentionedUsers, this.reactionCounts = const Value.absent(), this.reactionScores = const Value.absent(), this.parentId = const Value.absent(), @@ -2334,7 +2344,7 @@ class PinnedMessagesCompanion extends UpdateCompanion { this.showInChannel = const Value.absent(), this.shadowed = const Value.absent(), this.command = const Value.absent(), - @required DateTime createdAt, + this.createdAt = const Value.absent(), this.updatedAt = const Value.absent(), this.deletedAt = const Value.absent(), this.userId = const Value.absent(), @@ -2345,32 +2355,33 @@ class PinnedMessagesCompanion extends UpdateCompanion { this.channelCid = const Value.absent(), this.extraData = const Value.absent(), }) : id = Value(id), - createdAt = Value(createdAt); + attachments = Value(attachments), + mentionedUsers = Value(mentionedUsers); static Insertable custom({ - Expression id, - Expression messageText, - Expression attachments, - Expression status, - Expression type, - Expression mentionedUsers, - Expression reactionCounts, - Expression reactionScores, - Expression parentId, - Expression quotedMessageId, - Expression replyCount, - Expression showInChannel, - Expression shadowed, - Expression command, - Expression createdAt, - Expression updatedAt, - Expression deletedAt, - Expression userId, - Expression pinned, - Expression pinnedAt, - Expression pinExpires, - Expression pinnedByUserId, - Expression channelCid, - Expression extraData, + Expression? id, + Expression? messageText, + Expression>? attachments, + Expression? status, + Expression? type, + Expression>? mentionedUsers, + Expression?>? reactionCounts, + Expression?>? reactionScores, + Expression? parentId, + Expression? quotedMessageId, + Expression? replyCount, + Expression? showInChannel, + Expression? shadowed, + Expression? command, + Expression? createdAt, + Expression? updatedAt, + Expression? deletedAt, + Expression? userId, + Expression? pinned, + Expression? pinnedAt, + Expression? pinExpires, + Expression? pinnedByUserId, + Expression? channelCid, + Expression?>? extraData, }) { return RawValuesInsertable({ if (id != null) 'id': id, @@ -2401,30 +2412,30 @@ class PinnedMessagesCompanion extends UpdateCompanion { } PinnedMessagesCompanion copyWith( - {Value id, - Value messageText, - Value> attachments, - Value status, - Value type, - Value> mentionedUsers, - Value> reactionCounts, - Value> reactionScores, - Value parentId, - Value quotedMessageId, - Value replyCount, - Value showInChannel, - Value shadowed, - Value command, - Value createdAt, - Value updatedAt, - Value deletedAt, - Value userId, - Value pinned, - Value pinnedAt, - Value pinExpires, - Value pinnedByUserId, - Value channelCid, - Value> extraData}) { + {Value? id, + Value? messageText, + Value>? attachments, + Value? status, + Value? type, + Value>? mentionedUsers, + Value?>? reactionCounts, + Value?>? reactionScores, + Value? parentId, + Value? quotedMessageId, + Value? replyCount, + Value? showInChannel, + Value? shadowed, + Value? command, + Value? createdAt, + Value? updatedAt, + Value? deletedAt, + Value? userId, + Value? pinned, + Value? pinnedAt, + Value? pinExpires, + Value? pinnedByUserId, + Value? channelCid, + Value?>? extraData}) { return PinnedMessagesCompanion( id: id ?? this.id, messageText: messageText ?? this.messageText, @@ -2460,16 +2471,16 @@ class PinnedMessagesCompanion extends UpdateCompanion { map['id'] = Variable(id.value); } if (messageText.present) { - map['message_text'] = Variable(messageText.value); + map['message_text'] = Variable(messageText.value); } if (attachments.present) { final converter = $PinnedMessagesTable.$converter0; map['attachments'] = - Variable(converter.mapToSql(attachments.value)); + Variable(converter.mapToSql(attachments.value)!); } if (status.present) { final converter = $PinnedMessagesTable.$converter1; - map['status'] = Variable(converter.mapToSql(status.value)); + map['status'] = Variable(converter.mapToSql(status.value)!); } if (type.present) { map['type'] = Variable(type.value); @@ -2477,35 +2488,35 @@ class PinnedMessagesCompanion extends UpdateCompanion { if (mentionedUsers.present) { final converter = $PinnedMessagesTable.$converter2; map['mentioned_users'] = - Variable(converter.mapToSql(mentionedUsers.value)); + Variable(converter.mapToSql(mentionedUsers.value)!); } if (reactionCounts.present) { final converter = $PinnedMessagesTable.$converter3; map['reaction_counts'] = - Variable(converter.mapToSql(reactionCounts.value)); + Variable(converter.mapToSql(reactionCounts.value)); } if (reactionScores.present) { final converter = $PinnedMessagesTable.$converter4; map['reaction_scores'] = - Variable(converter.mapToSql(reactionScores.value)); + Variable(converter.mapToSql(reactionScores.value)); } if (parentId.present) { - map['parent_id'] = Variable(parentId.value); + map['parent_id'] = Variable(parentId.value); } if (quotedMessageId.present) { - map['quoted_message_id'] = Variable(quotedMessageId.value); + map['quoted_message_id'] = Variable(quotedMessageId.value); } if (replyCount.present) { - map['reply_count'] = Variable(replyCount.value); + map['reply_count'] = Variable(replyCount.value); } if (showInChannel.present) { - map['show_in_channel'] = Variable(showInChannel.value); + map['show_in_channel'] = Variable(showInChannel.value); } if (shadowed.present) { map['shadowed'] = Variable(shadowed.value); } if (command.present) { - map['command'] = Variable(command.value); + map['command'] = Variable(command.value); } if (createdAt.present) { map['created_at'] = Variable(createdAt.value); @@ -2514,29 +2525,30 @@ class PinnedMessagesCompanion extends UpdateCompanion { map['updated_at'] = Variable(updatedAt.value); } if (deletedAt.present) { - map['deleted_at'] = Variable(deletedAt.value); + map['deleted_at'] = Variable(deletedAt.value); } if (userId.present) { - map['user_id'] = Variable(userId.value); + map['user_id'] = Variable(userId.value); } if (pinned.present) { map['pinned'] = Variable(pinned.value); } if (pinnedAt.present) { - map['pinned_at'] = Variable(pinnedAt.value); + map['pinned_at'] = Variable(pinnedAt.value); } if (pinExpires.present) { - map['pin_expires'] = Variable(pinExpires.value); + map['pin_expires'] = Variable(pinExpires.value); } if (pinnedByUserId.present) { - map['pinned_by_user_id'] = Variable(pinnedByUserId.value); + map['pinned_by_user_id'] = Variable(pinnedByUserId.value); } if (channelCid.present) { - map['channel_cid'] = Variable(channelCid.value); + map['channel_cid'] = Variable(channelCid.value); } if (extraData.present) { final converter = $PinnedMessagesTable.$converter5; - map['extra_data'] = Variable(converter.mapToSql(extraData.value)); + map['extra_data'] = + Variable(converter.mapToSql(extraData.value)); } return map; } @@ -2576,12 +2588,11 @@ class PinnedMessagesCompanion extends UpdateCompanion { class $PinnedMessagesTable extends PinnedMessages with TableInfo<$PinnedMessagesTable, PinnedMessageEntity> { final GeneratedDatabase _db; - final String _alias; + final String? _alias; $PinnedMessagesTable(this._db, [this._alias]); final VerificationMeta _idMeta = const VerificationMeta('id'); - GeneratedTextColumn _id; @override - GeneratedTextColumn get id => _id ??= _constructId(); + late final GeneratedTextColumn id = _constructId(); GeneratedTextColumn _constructId() { return GeneratedTextColumn( 'id', @@ -2592,10 +2603,8 @@ class $PinnedMessagesTable extends PinnedMessages final VerificationMeta _messageTextMeta = const VerificationMeta('messageText'); - GeneratedTextColumn _messageText; @override - GeneratedTextColumn get messageText => - _messageText ??= _constructMessageText(); + late final GeneratedTextColumn messageText = _constructMessageText(); GeneratedTextColumn _constructMessageText() { return GeneratedTextColumn( 'message_text', @@ -2606,62 +2615,48 @@ class $PinnedMessagesTable extends PinnedMessages final VerificationMeta _attachmentsMeta = const VerificationMeta('attachments'); - GeneratedTextColumn _attachments; @override - GeneratedTextColumn get attachments => - _attachments ??= _constructAttachments(); + late final GeneratedTextColumn attachments = _constructAttachments(); GeneratedTextColumn _constructAttachments() { return GeneratedTextColumn( 'attachments', $tableName, - true, + false, ); } final VerificationMeta _statusMeta = const VerificationMeta('status'); - GeneratedIntColumn _status; @override - GeneratedIntColumn get status => _status ??= _constructStatus(); + late final GeneratedIntColumn status = _constructStatus(); GeneratedIntColumn _constructStatus() { - return GeneratedIntColumn( - 'status', - $tableName, - true, - ); + return GeneratedIntColumn('status', $tableName, false, + defaultValue: const Constant(1)); } final VerificationMeta _typeMeta = const VerificationMeta('type'); - GeneratedTextColumn _type; @override - GeneratedTextColumn get type => _type ??= _constructType(); + late final GeneratedTextColumn type = _constructType(); GeneratedTextColumn _constructType() { - return GeneratedTextColumn( - 'type', - $tableName, - true, - ); + return GeneratedTextColumn('type', $tableName, false, + defaultValue: const Constant('regular')); } final VerificationMeta _mentionedUsersMeta = const VerificationMeta('mentionedUsers'); - GeneratedTextColumn _mentionedUsers; @override - GeneratedTextColumn get mentionedUsers => - _mentionedUsers ??= _constructMentionedUsers(); + late final GeneratedTextColumn mentionedUsers = _constructMentionedUsers(); GeneratedTextColumn _constructMentionedUsers() { return GeneratedTextColumn( 'mentioned_users', $tableName, - true, + false, ); } final VerificationMeta _reactionCountsMeta = const VerificationMeta('reactionCounts'); - GeneratedTextColumn _reactionCounts; @override - GeneratedTextColumn get reactionCounts => - _reactionCounts ??= _constructReactionCounts(); + late final GeneratedTextColumn reactionCounts = _constructReactionCounts(); GeneratedTextColumn _constructReactionCounts() { return GeneratedTextColumn( 'reaction_counts', @@ -2672,10 +2667,8 @@ class $PinnedMessagesTable extends PinnedMessages final VerificationMeta _reactionScoresMeta = const VerificationMeta('reactionScores'); - GeneratedTextColumn _reactionScores; @override - GeneratedTextColumn get reactionScores => - _reactionScores ??= _constructReactionScores(); + late final GeneratedTextColumn reactionScores = _constructReactionScores(); GeneratedTextColumn _constructReactionScores() { return GeneratedTextColumn( 'reaction_scores', @@ -2685,9 +2678,8 @@ class $PinnedMessagesTable extends PinnedMessages } final VerificationMeta _parentIdMeta = const VerificationMeta('parentId'); - GeneratedTextColumn _parentId; @override - GeneratedTextColumn get parentId => _parentId ??= _constructParentId(); + late final GeneratedTextColumn parentId = _constructParentId(); GeneratedTextColumn _constructParentId() { return GeneratedTextColumn( 'parent_id', @@ -2698,10 +2690,8 @@ class $PinnedMessagesTable extends PinnedMessages final VerificationMeta _quotedMessageIdMeta = const VerificationMeta('quotedMessageId'); - GeneratedTextColumn _quotedMessageId; @override - GeneratedTextColumn get quotedMessageId => - _quotedMessageId ??= _constructQuotedMessageId(); + late final GeneratedTextColumn quotedMessageId = _constructQuotedMessageId(); GeneratedTextColumn _constructQuotedMessageId() { return GeneratedTextColumn( 'quoted_message_id', @@ -2711,9 +2701,8 @@ class $PinnedMessagesTable extends PinnedMessages } final VerificationMeta _replyCountMeta = const VerificationMeta('replyCount'); - GeneratedIntColumn _replyCount; @override - GeneratedIntColumn get replyCount => _replyCount ??= _constructReplyCount(); + late final GeneratedIntColumn replyCount = _constructReplyCount(); GeneratedIntColumn _constructReplyCount() { return GeneratedIntColumn( 'reply_count', @@ -2724,10 +2713,8 @@ class $PinnedMessagesTable extends PinnedMessages final VerificationMeta _showInChannelMeta = const VerificationMeta('showInChannel'); - GeneratedBoolColumn _showInChannel; @override - GeneratedBoolColumn get showInChannel => - _showInChannel ??= _constructShowInChannel(); + late final GeneratedBoolColumn showInChannel = _constructShowInChannel(); GeneratedBoolColumn _constructShowInChannel() { return GeneratedBoolColumn( 'show_in_channel', @@ -2737,21 +2724,16 @@ class $PinnedMessagesTable extends PinnedMessages } final VerificationMeta _shadowedMeta = const VerificationMeta('shadowed'); - GeneratedBoolColumn _shadowed; @override - GeneratedBoolColumn get shadowed => _shadowed ??= _constructShadowed(); + late final GeneratedBoolColumn shadowed = _constructShadowed(); GeneratedBoolColumn _constructShadowed() { - return GeneratedBoolColumn( - 'shadowed', - $tableName, - true, - ); + return GeneratedBoolColumn('shadowed', $tableName, false, + defaultValue: const Constant(false)); } final VerificationMeta _commandMeta = const VerificationMeta('command'); - GeneratedTextColumn _command; @override - GeneratedTextColumn get command => _command ??= _constructCommand(); + late final GeneratedTextColumn command = _constructCommand(); GeneratedTextColumn _constructCommand() { return GeneratedTextColumn( 'command', @@ -2761,33 +2743,24 @@ class $PinnedMessagesTable extends PinnedMessages } final VerificationMeta _createdAtMeta = const VerificationMeta('createdAt'); - GeneratedDateTimeColumn _createdAt; @override - GeneratedDateTimeColumn get createdAt => _createdAt ??= _constructCreatedAt(); + late final GeneratedDateTimeColumn createdAt = _constructCreatedAt(); GeneratedDateTimeColumn _constructCreatedAt() { - return GeneratedDateTimeColumn( - 'created_at', - $tableName, - false, - ); + return GeneratedDateTimeColumn('created_at', $tableName, false, + defaultValue: currentDateAndTime); } final VerificationMeta _updatedAtMeta = const VerificationMeta('updatedAt'); - GeneratedDateTimeColumn _updatedAt; @override - GeneratedDateTimeColumn get updatedAt => _updatedAt ??= _constructUpdatedAt(); + late final GeneratedDateTimeColumn updatedAt = _constructUpdatedAt(); GeneratedDateTimeColumn _constructUpdatedAt() { - return GeneratedDateTimeColumn( - 'updated_at', - $tableName, - true, - ); + return GeneratedDateTimeColumn('updated_at', $tableName, false, + defaultValue: currentDateAndTime); } final VerificationMeta _deletedAtMeta = const VerificationMeta('deletedAt'); - GeneratedDateTimeColumn _deletedAt; @override - GeneratedDateTimeColumn get deletedAt => _deletedAt ??= _constructDeletedAt(); + late final GeneratedDateTimeColumn deletedAt = _constructDeletedAt(); GeneratedDateTimeColumn _constructDeletedAt() { return GeneratedDateTimeColumn( 'deleted_at', @@ -2797,9 +2770,8 @@ class $PinnedMessagesTable extends PinnedMessages } final VerificationMeta _userIdMeta = const VerificationMeta('userId'); - GeneratedTextColumn _userId; @override - GeneratedTextColumn get userId => _userId ??= _constructUserId(); + late final GeneratedTextColumn userId = _constructUserId(); GeneratedTextColumn _constructUserId() { return GeneratedTextColumn( 'user_id', @@ -2809,18 +2781,16 @@ class $PinnedMessagesTable extends PinnedMessages } final VerificationMeta _pinnedMeta = const VerificationMeta('pinned'); - GeneratedBoolColumn _pinned; @override - GeneratedBoolColumn get pinned => _pinned ??= _constructPinned(); + late final GeneratedBoolColumn pinned = _constructPinned(); GeneratedBoolColumn _constructPinned() { return GeneratedBoolColumn('pinned', $tableName, false, defaultValue: const Constant(false)); } final VerificationMeta _pinnedAtMeta = const VerificationMeta('pinnedAt'); - GeneratedDateTimeColumn _pinnedAt; @override - GeneratedDateTimeColumn get pinnedAt => _pinnedAt ??= _constructPinnedAt(); + late final GeneratedDateTimeColumn pinnedAt = _constructPinnedAt(); GeneratedDateTimeColumn _constructPinnedAt() { return GeneratedDateTimeColumn( 'pinned_at', @@ -2830,10 +2800,8 @@ class $PinnedMessagesTable extends PinnedMessages } final VerificationMeta _pinExpiresMeta = const VerificationMeta('pinExpires'); - GeneratedDateTimeColumn _pinExpires; @override - GeneratedDateTimeColumn get pinExpires => - _pinExpires ??= _constructPinExpires(); + late final GeneratedDateTimeColumn pinExpires = _constructPinExpires(); GeneratedDateTimeColumn _constructPinExpires() { return GeneratedDateTimeColumn( 'pin_expires', @@ -2844,10 +2812,8 @@ class $PinnedMessagesTable extends PinnedMessages final VerificationMeta _pinnedByUserIdMeta = const VerificationMeta('pinnedByUserId'); - GeneratedTextColumn _pinnedByUserId; @override - GeneratedTextColumn get pinnedByUserId => - _pinnedByUserId ??= _constructPinnedByUserId(); + late final GeneratedTextColumn pinnedByUserId = _constructPinnedByUserId(); GeneratedTextColumn _constructPinnedByUserId() { return GeneratedTextColumn( 'pinned_by_user_id', @@ -2857,9 +2823,8 @@ class $PinnedMessagesTable extends PinnedMessages } final VerificationMeta _channelCidMeta = const VerificationMeta('channelCid'); - GeneratedTextColumn _channelCid; @override - GeneratedTextColumn get channelCid => _channelCid ??= _constructChannelCid(); + late final GeneratedTextColumn channelCid = _constructChannelCid(); GeneratedTextColumn _constructChannelCid() { return GeneratedTextColumn('channel_cid', $tableName, true, $customConstraints: @@ -2867,9 +2832,8 @@ class $PinnedMessagesTable extends PinnedMessages } final VerificationMeta _extraDataMeta = const VerificationMeta('extraData'); - GeneratedTextColumn _extraData; @override - GeneratedTextColumn get extraData => _extraData ??= _constructExtraData(); + late final GeneratedTextColumn extraData = _constructExtraData(); GeneratedTextColumn _constructExtraData() { return GeneratedTextColumn( 'extra_data', @@ -2918,7 +2882,7 @@ class $PinnedMessagesTable extends PinnedMessages final context = VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('id')) { - context.handle(_idMeta, id.isAcceptableOrUnknown(data['id'], _idMeta)); + context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); } else if (isInserting) { context.missing(_idMeta); } @@ -2926,90 +2890,88 @@ class $PinnedMessagesTable extends PinnedMessages context.handle( _messageTextMeta, messageText.isAcceptableOrUnknown( - data['message_text'], _messageTextMeta)); + data['message_text']!, _messageTextMeta)); } context.handle(_attachmentsMeta, const VerificationResult.success()); context.handle(_statusMeta, const VerificationResult.success()); if (data.containsKey('type')) { context.handle( - _typeMeta, type.isAcceptableOrUnknown(data['type'], _typeMeta)); + _typeMeta, type.isAcceptableOrUnknown(data['type']!, _typeMeta)); } context.handle(_mentionedUsersMeta, const VerificationResult.success()); context.handle(_reactionCountsMeta, const VerificationResult.success()); context.handle(_reactionScoresMeta, const VerificationResult.success()); if (data.containsKey('parent_id')) { context.handle(_parentIdMeta, - parentId.isAcceptableOrUnknown(data['parent_id'], _parentIdMeta)); + parentId.isAcceptableOrUnknown(data['parent_id']!, _parentIdMeta)); } if (data.containsKey('quoted_message_id')) { context.handle( _quotedMessageIdMeta, quotedMessageId.isAcceptableOrUnknown( - data['quoted_message_id'], _quotedMessageIdMeta)); + data['quoted_message_id']!, _quotedMessageIdMeta)); } if (data.containsKey('reply_count')) { context.handle( _replyCountMeta, replyCount.isAcceptableOrUnknown( - data['reply_count'], _replyCountMeta)); + data['reply_count']!, _replyCountMeta)); } if (data.containsKey('show_in_channel')) { context.handle( _showInChannelMeta, showInChannel.isAcceptableOrUnknown( - data['show_in_channel'], _showInChannelMeta)); + data['show_in_channel']!, _showInChannelMeta)); } if (data.containsKey('shadowed')) { context.handle(_shadowedMeta, - shadowed.isAcceptableOrUnknown(data['shadowed'], _shadowedMeta)); + shadowed.isAcceptableOrUnknown(data['shadowed']!, _shadowedMeta)); } if (data.containsKey('command')) { context.handle(_commandMeta, - command.isAcceptableOrUnknown(data['command'], _commandMeta)); + command.isAcceptableOrUnknown(data['command']!, _commandMeta)); } if (data.containsKey('created_at')) { context.handle(_createdAtMeta, - createdAt.isAcceptableOrUnknown(data['created_at'], _createdAtMeta)); - } else if (isInserting) { - context.missing(_createdAtMeta); + createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta)); } if (data.containsKey('updated_at')) { context.handle(_updatedAtMeta, - updatedAt.isAcceptableOrUnknown(data['updated_at'], _updatedAtMeta)); + updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta)); } if (data.containsKey('deleted_at')) { context.handle(_deletedAtMeta, - deletedAt.isAcceptableOrUnknown(data['deleted_at'], _deletedAtMeta)); + deletedAt.isAcceptableOrUnknown(data['deleted_at']!, _deletedAtMeta)); } if (data.containsKey('user_id')) { context.handle(_userIdMeta, - userId.isAcceptableOrUnknown(data['user_id'], _userIdMeta)); + userId.isAcceptableOrUnknown(data['user_id']!, _userIdMeta)); } if (data.containsKey('pinned')) { context.handle(_pinnedMeta, - pinned.isAcceptableOrUnknown(data['pinned'], _pinnedMeta)); + pinned.isAcceptableOrUnknown(data['pinned']!, _pinnedMeta)); } if (data.containsKey('pinned_at')) { context.handle(_pinnedAtMeta, - pinnedAt.isAcceptableOrUnknown(data['pinned_at'], _pinnedAtMeta)); + pinnedAt.isAcceptableOrUnknown(data['pinned_at']!, _pinnedAtMeta)); } if (data.containsKey('pin_expires')) { context.handle( _pinExpiresMeta, pinExpires.isAcceptableOrUnknown( - data['pin_expires'], _pinExpiresMeta)); + data['pin_expires']!, _pinExpiresMeta)); } if (data.containsKey('pinned_by_user_id')) { context.handle( _pinnedByUserIdMeta, pinnedByUserId.isAcceptableOrUnknown( - data['pinned_by_user_id'], _pinnedByUserIdMeta)); + data['pinned_by_user_id']!, _pinnedByUserIdMeta)); } if (data.containsKey('channel_cid')) { context.handle( _channelCidMeta, channelCid.isAcceptableOrUnknown( - data['channel_cid'], _channelCidMeta)); + data['channel_cid']!, _channelCidMeta)); } context.handle(_extraDataMeta, const VerificationResult.success()); return context; @@ -3018,7 +2980,7 @@ class $PinnedMessagesTable extends PinnedMessages @override Set get $primaryKey => {id}; @override - PinnedMessageEntity map(Map data, {String tablePrefix}) { + PinnedMessageEntity map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : null; return PinnedMessageEntity.fromData(data, _db, prefix: effectivePrefix); } @@ -3043,35 +3005,46 @@ class $PinnedMessagesTable extends PinnedMessages } class ReactionEntity extends DataClass implements Insertable { + /// The id of the user that sent the reaction final String userId; + + /// The messageId to which the reaction belongs final String messageId; + + /// The type of the reaction final String type; + + /// The DateTime on which the reaction is created final DateTime createdAt; + + /// The score of the reaction (ie. number of reactions sent) final int score; - final Map extraData; + + /// Reaction custom extraData + final Map? extraData; ReactionEntity( - {@required this.userId, - @required this.messageId, - @required this.type, - @required this.createdAt, - this.score, + {required this.userId, + required this.messageId, + required this.type, + required this.createdAt, + required this.score, this.extraData}); factory ReactionEntity.fromData( Map data, GeneratedDatabase db, - {String prefix}) { + {String? prefix}) { final effectivePrefix = prefix ?? ''; final stringType = db.typeSystem.forDartType(); final dateTimeType = db.typeSystem.forDartType(); final intType = db.typeSystem.forDartType(); return ReactionEntity( - userId: - stringType.mapFromDatabaseResponse(data['${effectivePrefix}user_id']), + userId: stringType + .mapFromDatabaseResponse(data['${effectivePrefix}user_id'])!, messageId: stringType - .mapFromDatabaseResponse(data['${effectivePrefix}message_id']), - type: stringType.mapFromDatabaseResponse(data['${effectivePrefix}type']), + .mapFromDatabaseResponse(data['${effectivePrefix}message_id'])!, + type: stringType.mapFromDatabaseResponse(data['${effectivePrefix}type'])!, createdAt: dateTimeType - .mapFromDatabaseResponse(data['${effectivePrefix}created_at']), - score: intType.mapFromDatabaseResponse(data['${effectivePrefix}score']), + .mapFromDatabaseResponse(data['${effectivePrefix}created_at'])!, + score: intType.mapFromDatabaseResponse(data['${effectivePrefix}score'])!, extraData: $ReactionsTable.$converter0.mapToDart(stringType .mapFromDatabaseResponse(data['${effectivePrefix}extra_data'])), ); @@ -3079,30 +3052,20 @@ class ReactionEntity extends DataClass implements Insertable { @override Map toColumns(bool nullToAbsent) { final map = {}; - if (!nullToAbsent || userId != null) { - map['user_id'] = Variable(userId); - } - if (!nullToAbsent || messageId != null) { - map['message_id'] = Variable(messageId); - } - if (!nullToAbsent || type != null) { - map['type'] = Variable(type); - } - if (!nullToAbsent || createdAt != null) { - map['created_at'] = Variable(createdAt); - } - if (!nullToAbsent || score != null) { - map['score'] = Variable(score); - } + map['user_id'] = Variable(userId); + map['message_id'] = Variable(messageId); + map['type'] = Variable(type); + map['created_at'] = Variable(createdAt); + map['score'] = Variable(score); if (!nullToAbsent || extraData != null) { final converter = $ReactionsTable.$converter0; - map['extra_data'] = Variable(converter.mapToSql(extraData)); + map['extra_data'] = Variable(converter.mapToSql(extraData)); } return map; } factory ReactionEntity.fromJson(Map json, - {ValueSerializer serializer}) { + {ValueSerializer? serializer}) { serializer ??= moorRuntimeOptions.defaultSerializer; return ReactionEntity( userId: serializer.fromJson(json['userId']), @@ -3110,11 +3073,11 @@ class ReactionEntity extends DataClass implements Insertable { type: serializer.fromJson(json['type']), createdAt: serializer.fromJson(json['createdAt']), score: serializer.fromJson(json['score']), - extraData: serializer.fromJson>(json['extraData']), + extraData: serializer.fromJson?>(json['extraData']), ); } @override - Map toJson({ValueSerializer serializer}) { + Map toJson({ValueSerializer? serializer}) { serializer ??= moorRuntimeOptions.defaultSerializer; return { 'userId': serializer.toJson(userId), @@ -3122,23 +3085,23 @@ class ReactionEntity extends DataClass implements Insertable { 'type': serializer.toJson(type), 'createdAt': serializer.toJson(createdAt), 'score': serializer.toJson(score), - 'extraData': serializer.toJson>(extraData), + 'extraData': serializer.toJson?>(extraData), }; } ReactionEntity copyWith( - {String userId, - String messageId, - String type, - DateTime createdAt, - Value score = const Value.absent(), - Value> extraData = const Value.absent()}) => + {String? userId, + String? messageId, + String? type, + DateTime? createdAt, + int? score, + Value?> extraData = const Value.absent()}) => ReactionEntity( userId: userId ?? this.userId, messageId: messageId ?? this.messageId, type: type ?? this.type, createdAt: createdAt ?? this.createdAt, - score: score.present ? score.value : this.score, + score: score ?? this.score, extraData: extraData.present ? extraData.value : this.extraData, ); @override @@ -3181,7 +3144,7 @@ class ReactionsCompanion extends UpdateCompanion { final Value type; final Value createdAt; final Value score; - final Value> extraData; + final Value?> extraData; const ReactionsCompanion({ this.userId = const Value.absent(), this.messageId = const Value.absent(), @@ -3191,23 +3154,22 @@ class ReactionsCompanion extends UpdateCompanion { this.extraData = const Value.absent(), }); ReactionsCompanion.insert({ - @required String userId, - @required String messageId, - @required String type, - @required DateTime createdAt, + required String userId, + required String messageId, + required String type, + this.createdAt = const Value.absent(), this.score = const Value.absent(), this.extraData = const Value.absent(), }) : userId = Value(userId), messageId = Value(messageId), - type = Value(type), - createdAt = Value(createdAt); + type = Value(type); static Insertable custom({ - Expression userId, - Expression messageId, - Expression type, - Expression createdAt, - Expression score, - Expression extraData, + Expression? userId, + Expression? messageId, + Expression? type, + Expression? createdAt, + Expression? score, + Expression?>? extraData, }) { return RawValuesInsertable({ if (userId != null) 'user_id': userId, @@ -3220,12 +3182,12 @@ class ReactionsCompanion extends UpdateCompanion { } ReactionsCompanion copyWith( - {Value userId, - Value messageId, - Value type, - Value createdAt, - Value score, - Value> extraData}) { + {Value? userId, + Value? messageId, + Value? type, + Value? createdAt, + Value? score, + Value?>? extraData}) { return ReactionsCompanion( userId: userId ?? this.userId, messageId: messageId ?? this.messageId, @@ -3256,7 +3218,8 @@ class ReactionsCompanion extends UpdateCompanion { } if (extraData.present) { final converter = $ReactionsTable.$converter0; - map['extra_data'] = Variable(converter.mapToSql(extraData.value)); + map['extra_data'] = + Variable(converter.mapToSql(extraData.value)); } return map; } @@ -3278,12 +3241,11 @@ class ReactionsCompanion extends UpdateCompanion { class $ReactionsTable extends Reactions with TableInfo<$ReactionsTable, ReactionEntity> { final GeneratedDatabase _db; - final String _alias; + final String? _alias; $ReactionsTable(this._db, [this._alias]); final VerificationMeta _userIdMeta = const VerificationMeta('userId'); - GeneratedTextColumn _userId; @override - GeneratedTextColumn get userId => _userId ??= _constructUserId(); + late final GeneratedTextColumn userId = _constructUserId(); GeneratedTextColumn _constructUserId() { return GeneratedTextColumn( 'user_id', @@ -3293,18 +3255,16 @@ class $ReactionsTable extends Reactions } final VerificationMeta _messageIdMeta = const VerificationMeta('messageId'); - GeneratedTextColumn _messageId; @override - GeneratedTextColumn get messageId => _messageId ??= _constructMessageId(); + late final GeneratedTextColumn messageId = _constructMessageId(); GeneratedTextColumn _constructMessageId() { return GeneratedTextColumn('message_id', $tableName, false, $customConstraints: 'REFERENCES messages(id) ON DELETE CASCADE'); } final VerificationMeta _typeMeta = const VerificationMeta('type'); - GeneratedTextColumn _type; @override - GeneratedTextColumn get type => _type ??= _constructType(); + late final GeneratedTextColumn type = _constructType(); GeneratedTextColumn _constructType() { return GeneratedTextColumn( 'type', @@ -3314,33 +3274,24 @@ class $ReactionsTable extends Reactions } final VerificationMeta _createdAtMeta = const VerificationMeta('createdAt'); - GeneratedDateTimeColumn _createdAt; @override - GeneratedDateTimeColumn get createdAt => _createdAt ??= _constructCreatedAt(); + late final GeneratedDateTimeColumn createdAt = _constructCreatedAt(); GeneratedDateTimeColumn _constructCreatedAt() { - return GeneratedDateTimeColumn( - 'created_at', - $tableName, - false, - ); + return GeneratedDateTimeColumn('created_at', $tableName, false, + defaultValue: currentDateAndTime); } final VerificationMeta _scoreMeta = const VerificationMeta('score'); - GeneratedIntColumn _score; @override - GeneratedIntColumn get score => _score ??= _constructScore(); + late final GeneratedIntColumn score = _constructScore(); GeneratedIntColumn _constructScore() { - return GeneratedIntColumn( - 'score', - $tableName, - true, - ); + return GeneratedIntColumn('score', $tableName, false, + defaultValue: const Constant(0)); } final VerificationMeta _extraDataMeta = const VerificationMeta('extraData'); - GeneratedTextColumn _extraData; @override - GeneratedTextColumn get extraData => _extraData ??= _constructExtraData(); + late final GeneratedTextColumn extraData = _constructExtraData(); GeneratedTextColumn _constructExtraData() { return GeneratedTextColumn( 'extra_data', @@ -3365,31 +3316,29 @@ class $ReactionsTable extends Reactions final data = instance.toColumns(true); if (data.containsKey('user_id')) { context.handle(_userIdMeta, - userId.isAcceptableOrUnknown(data['user_id'], _userIdMeta)); + userId.isAcceptableOrUnknown(data['user_id']!, _userIdMeta)); } else if (isInserting) { context.missing(_userIdMeta); } if (data.containsKey('message_id')) { context.handle(_messageIdMeta, - messageId.isAcceptableOrUnknown(data['message_id'], _messageIdMeta)); + messageId.isAcceptableOrUnknown(data['message_id']!, _messageIdMeta)); } else if (isInserting) { context.missing(_messageIdMeta); } if (data.containsKey('type')) { context.handle( - _typeMeta, type.isAcceptableOrUnknown(data['type'], _typeMeta)); + _typeMeta, type.isAcceptableOrUnknown(data['type']!, _typeMeta)); } else if (isInserting) { context.missing(_typeMeta); } if (data.containsKey('created_at')) { context.handle(_createdAtMeta, - createdAt.isAcceptableOrUnknown(data['created_at'], _createdAtMeta)); - } else if (isInserting) { - context.missing(_createdAtMeta); + createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta)); } if (data.containsKey('score')) { context.handle( - _scoreMeta, score.isAcceptableOrUnknown(data['score'], _scoreMeta)); + _scoreMeta, score.isAcceptableOrUnknown(data['score']!, _scoreMeta)); } context.handle(_extraDataMeta, const VerificationResult.success()); return context; @@ -3398,7 +3347,7 @@ class $ReactionsTable extends Reactions @override Set get $primaryKey => {messageId, type, userId}; @override - ReactionEntity map(Map data, {String tablePrefix}) { + ReactionEntity map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : null; return ReactionEntity.fromData(data, _db, prefix: effectivePrefix); } @@ -3413,100 +3362,105 @@ class $ReactionsTable extends Reactions } class UserEntity extends DataClass implements Insertable { + /// User id final String id; - final String role; + + /// User role + final String? role; + + /// Date of user creation final DateTime createdAt; + + /// Date of last user update final DateTime updatedAt; - final DateTime lastActive; + + /// Date of last user connection + final DateTime? lastActive; + + /// True if user is online final bool online; + + /// True if user is banned from the chat final bool banned; + + /// Map of custom user extraData final Map extraData; UserEntity( - {@required this.id, + {required this.id, this.role, - this.createdAt, - this.updatedAt, + required this.createdAt, + required this.updatedAt, this.lastActive, - this.online, - this.banned, - this.extraData}); + required this.online, + required this.banned, + required this.extraData}); factory UserEntity.fromData(Map data, GeneratedDatabase db, - {String prefix}) { + {String? prefix}) { final effectivePrefix = prefix ?? ''; final stringType = db.typeSystem.forDartType(); final dateTimeType = db.typeSystem.forDartType(); final boolType = db.typeSystem.forDartType(); return UserEntity( - id: stringType.mapFromDatabaseResponse(data['${effectivePrefix}id']), + id: stringType.mapFromDatabaseResponse(data['${effectivePrefix}id'])!, role: stringType.mapFromDatabaseResponse(data['${effectivePrefix}role']), createdAt: dateTimeType - .mapFromDatabaseResponse(data['${effectivePrefix}created_at']), + .mapFromDatabaseResponse(data['${effectivePrefix}created_at'])!, updatedAt: dateTimeType - .mapFromDatabaseResponse(data['${effectivePrefix}updated_at']), + .mapFromDatabaseResponse(data['${effectivePrefix}updated_at'])!, lastActive: dateTimeType .mapFromDatabaseResponse(data['${effectivePrefix}last_active']), online: - boolType.mapFromDatabaseResponse(data['${effectivePrefix}online']), + boolType.mapFromDatabaseResponse(data['${effectivePrefix}online'])!, banned: - boolType.mapFromDatabaseResponse(data['${effectivePrefix}banned']), + boolType.mapFromDatabaseResponse(data['${effectivePrefix}banned'])!, extraData: $UsersTable.$converter0.mapToDart(stringType - .mapFromDatabaseResponse(data['${effectivePrefix}extra_data'])), + .mapFromDatabaseResponse(data['${effectivePrefix}extra_data']))!, ); } @override Map toColumns(bool nullToAbsent) { final map = {}; - if (!nullToAbsent || id != null) { - map['id'] = Variable(id); - } + map['id'] = Variable(id); if (!nullToAbsent || role != null) { - map['role'] = Variable(role); - } - if (!nullToAbsent || createdAt != null) { - map['created_at'] = Variable(createdAt); - } - if (!nullToAbsent || updatedAt != null) { - map['updated_at'] = Variable(updatedAt); + map['role'] = Variable(role); } + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); if (!nullToAbsent || lastActive != null) { - map['last_active'] = Variable(lastActive); + map['last_active'] = Variable(lastActive); } - if (!nullToAbsent || online != null) { - map['online'] = Variable(online); - } - if (!nullToAbsent || banned != null) { - map['banned'] = Variable(banned); - } - if (!nullToAbsent || extraData != null) { + map['online'] = Variable(online); + map['banned'] = Variable(banned); + { final converter = $UsersTable.$converter0; - map['extra_data'] = Variable(converter.mapToSql(extraData)); + map['extra_data'] = Variable(converter.mapToSql(extraData)!); } return map; } factory UserEntity.fromJson(Map json, - {ValueSerializer serializer}) { + {ValueSerializer? serializer}) { serializer ??= moorRuntimeOptions.defaultSerializer; return UserEntity( id: serializer.fromJson(json['id']), - role: serializer.fromJson(json['role']), + role: serializer.fromJson(json['role']), createdAt: serializer.fromJson(json['createdAt']), updatedAt: serializer.fromJson(json['updatedAt']), - lastActive: serializer.fromJson(json['lastActive']), + lastActive: serializer.fromJson(json['lastActive']), online: serializer.fromJson(json['online']), banned: serializer.fromJson(json['banned']), extraData: serializer.fromJson>(json['extraData']), ); } @override - Map toJson({ValueSerializer serializer}) { + Map toJson({ValueSerializer? serializer}) { serializer ??= moorRuntimeOptions.defaultSerializer; return { 'id': serializer.toJson(id), - 'role': serializer.toJson(role), + 'role': serializer.toJson(role), 'createdAt': serializer.toJson(createdAt), 'updatedAt': serializer.toJson(updatedAt), - 'lastActive': serializer.toJson(lastActive), + 'lastActive': serializer.toJson(lastActive), 'online': serializer.toJson(online), 'banned': serializer.toJson(banned), 'extraData': serializer.toJson>(extraData), @@ -3514,23 +3468,23 @@ class UserEntity extends DataClass implements Insertable { } UserEntity copyWith( - {String id, - Value role = const Value.absent(), - Value createdAt = const Value.absent(), - Value updatedAt = const Value.absent(), - Value lastActive = const Value.absent(), - Value online = const Value.absent(), - Value banned = const Value.absent(), - Value> extraData = const Value.absent()}) => + {String? id, + Value role = const Value.absent(), + DateTime? createdAt, + DateTime? updatedAt, + Value lastActive = const Value.absent(), + bool? online, + bool? banned, + Map? extraData}) => UserEntity( id: id ?? this.id, role: role.present ? role.value : this.role, - createdAt: createdAt.present ? createdAt.value : this.createdAt, - updatedAt: updatedAt.present ? updatedAt.value : this.updatedAt, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, lastActive: lastActive.present ? lastActive.value : this.lastActive, - online: online.present ? online.value : this.online, - banned: banned.present ? banned.value : this.banned, - extraData: extraData.present ? extraData.value : this.extraData, + online: online ?? this.online, + banned: banned ?? this.banned, + extraData: extraData ?? this.extraData, ); @override String toString() { @@ -3576,10 +3530,10 @@ class UserEntity extends DataClass implements Insertable { class UsersCompanion extends UpdateCompanion { final Value id; - final Value role; + final Value role; final Value createdAt; final Value updatedAt; - final Value lastActive; + final Value lastActive; final Value online; final Value banned; final Value> extraData; @@ -3594,24 +3548,25 @@ class UsersCompanion extends UpdateCompanion { this.extraData = const Value.absent(), }); UsersCompanion.insert({ - @required String id, + required String id, this.role = const Value.absent(), this.createdAt = const Value.absent(), this.updatedAt = const Value.absent(), this.lastActive = const Value.absent(), this.online = const Value.absent(), this.banned = const Value.absent(), - this.extraData = const Value.absent(), - }) : id = Value(id); + required Map extraData, + }) : id = Value(id), + extraData = Value(extraData); static Insertable custom({ - Expression id, - Expression role, - Expression createdAt, - Expression updatedAt, - Expression lastActive, - Expression online, - Expression banned, - Expression extraData, + Expression? id, + Expression? role, + Expression? createdAt, + Expression? updatedAt, + Expression? lastActive, + Expression? online, + Expression? banned, + Expression>? extraData, }) { return RawValuesInsertable({ if (id != null) 'id': id, @@ -3626,14 +3581,14 @@ class UsersCompanion extends UpdateCompanion { } UsersCompanion copyWith( - {Value id, - Value role, - Value createdAt, - Value updatedAt, - Value lastActive, - Value online, - Value banned, - Value> extraData}) { + {Value? id, + Value? role, + Value? createdAt, + Value? updatedAt, + Value? lastActive, + Value? online, + Value? banned, + Value>? extraData}) { return UsersCompanion( id: id ?? this.id, role: role ?? this.role, @@ -3653,7 +3608,7 @@ class UsersCompanion extends UpdateCompanion { map['id'] = Variable(id.value); } if (role.present) { - map['role'] = Variable(role.value); + map['role'] = Variable(role.value); } if (createdAt.present) { map['created_at'] = Variable(createdAt.value); @@ -3662,7 +3617,7 @@ class UsersCompanion extends UpdateCompanion { map['updated_at'] = Variable(updatedAt.value); } if (lastActive.present) { - map['last_active'] = Variable(lastActive.value); + map['last_active'] = Variable(lastActive.value); } if (online.present) { map['online'] = Variable(online.value); @@ -3672,7 +3627,8 @@ class UsersCompanion extends UpdateCompanion { } if (extraData.present) { final converter = $UsersTable.$converter0; - map['extra_data'] = Variable(converter.mapToSql(extraData.value)); + map['extra_data'] = + Variable(converter.mapToSql(extraData.value)!); } return map; } @@ -3695,12 +3651,11 @@ class UsersCompanion extends UpdateCompanion { class $UsersTable extends Users with TableInfo<$UsersTable, UserEntity> { final GeneratedDatabase _db; - final String _alias; + final String? _alias; $UsersTable(this._db, [this._alias]); final VerificationMeta _idMeta = const VerificationMeta('id'); - GeneratedTextColumn _id; @override - GeneratedTextColumn get id => _id ??= _constructId(); + late final GeneratedTextColumn id = _constructId(); GeneratedTextColumn _constructId() { return GeneratedTextColumn( 'id', @@ -3710,9 +3665,8 @@ class $UsersTable extends Users with TableInfo<$UsersTable, UserEntity> { } final VerificationMeta _roleMeta = const VerificationMeta('role'); - GeneratedTextColumn _role; @override - GeneratedTextColumn get role => _role ??= _constructRole(); + late final GeneratedTextColumn role = _constructRole(); GeneratedTextColumn _constructRole() { return GeneratedTextColumn( 'role', @@ -3722,34 +3676,24 @@ class $UsersTable extends Users with TableInfo<$UsersTable, UserEntity> { } final VerificationMeta _createdAtMeta = const VerificationMeta('createdAt'); - GeneratedDateTimeColumn _createdAt; @override - GeneratedDateTimeColumn get createdAt => _createdAt ??= _constructCreatedAt(); + late final GeneratedDateTimeColumn createdAt = _constructCreatedAt(); GeneratedDateTimeColumn _constructCreatedAt() { - return GeneratedDateTimeColumn( - 'created_at', - $tableName, - true, - ); + return GeneratedDateTimeColumn('created_at', $tableName, false, + defaultValue: currentDateAndTime); } final VerificationMeta _updatedAtMeta = const VerificationMeta('updatedAt'); - GeneratedDateTimeColumn _updatedAt; @override - GeneratedDateTimeColumn get updatedAt => _updatedAt ??= _constructUpdatedAt(); + late final GeneratedDateTimeColumn updatedAt = _constructUpdatedAt(); GeneratedDateTimeColumn _constructUpdatedAt() { - return GeneratedDateTimeColumn( - 'updated_at', - $tableName, - true, - ); + return GeneratedDateTimeColumn('updated_at', $tableName, false, + defaultValue: currentDateAndTime); } final VerificationMeta _lastActiveMeta = const VerificationMeta('lastActive'); - GeneratedDateTimeColumn _lastActive; @override - GeneratedDateTimeColumn get lastActive => - _lastActive ??= _constructLastActive(); + late final GeneratedDateTimeColumn lastActive = _constructLastActive(); GeneratedDateTimeColumn _constructLastActive() { return GeneratedDateTimeColumn( 'last_active', @@ -3759,38 +3703,29 @@ class $UsersTable extends Users with TableInfo<$UsersTable, UserEntity> { } final VerificationMeta _onlineMeta = const VerificationMeta('online'); - GeneratedBoolColumn _online; @override - GeneratedBoolColumn get online => _online ??= _constructOnline(); + late final GeneratedBoolColumn online = _constructOnline(); GeneratedBoolColumn _constructOnline() { - return GeneratedBoolColumn( - 'online', - $tableName, - true, - ); + return GeneratedBoolColumn('online', $tableName, false, + defaultValue: const Constant(false)); } final VerificationMeta _bannedMeta = const VerificationMeta('banned'); - GeneratedBoolColumn _banned; @override - GeneratedBoolColumn get banned => _banned ??= _constructBanned(); + late final GeneratedBoolColumn banned = _constructBanned(); GeneratedBoolColumn _constructBanned() { - return GeneratedBoolColumn( - 'banned', - $tableName, - true, - ); + return GeneratedBoolColumn('banned', $tableName, false, + defaultValue: const Constant(false)); } final VerificationMeta _extraDataMeta = const VerificationMeta('extraData'); - GeneratedTextColumn _extraData; @override - GeneratedTextColumn get extraData => _extraData ??= _constructExtraData(); + late final GeneratedTextColumn extraData = _constructExtraData(); GeneratedTextColumn _constructExtraData() { return GeneratedTextColumn( 'extra_data', $tableName, - true, + false, ); } @@ -3809,35 +3744,35 @@ class $UsersTable extends Users with TableInfo<$UsersTable, UserEntity> { final context = VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('id')) { - context.handle(_idMeta, id.isAcceptableOrUnknown(data['id'], _idMeta)); + context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); } else if (isInserting) { context.missing(_idMeta); } if (data.containsKey('role')) { context.handle( - _roleMeta, role.isAcceptableOrUnknown(data['role'], _roleMeta)); + _roleMeta, role.isAcceptableOrUnknown(data['role']!, _roleMeta)); } if (data.containsKey('created_at')) { context.handle(_createdAtMeta, - createdAt.isAcceptableOrUnknown(data['created_at'], _createdAtMeta)); + createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta)); } if (data.containsKey('updated_at')) { context.handle(_updatedAtMeta, - updatedAt.isAcceptableOrUnknown(data['updated_at'], _updatedAtMeta)); + updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta)); } if (data.containsKey('last_active')) { context.handle( _lastActiveMeta, lastActive.isAcceptableOrUnknown( - data['last_active'], _lastActiveMeta)); + data['last_active']!, _lastActiveMeta)); } if (data.containsKey('online')) { context.handle(_onlineMeta, - online.isAcceptableOrUnknown(data['online'], _onlineMeta)); + online.isAcceptableOrUnknown(data['online']!, _onlineMeta)); } if (data.containsKey('banned')) { context.handle(_bannedMeta, - banned.isAcceptableOrUnknown(data['banned'], _bannedMeta)); + banned.isAcceptableOrUnknown(data['banned']!, _bannedMeta)); } context.handle(_extraDataMeta, const VerificationResult.success()); return context; @@ -3846,7 +3781,7 @@ class $UsersTable extends Users with TableInfo<$UsersTable, UserEntity> { @override Set get $primaryKey => {id}; @override - UserEntity map(Map data, {String tablePrefix}) { + UserEntity map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : null; return UserEntity.fromData(data, _db, prefix: effectivePrefix); } @@ -3861,107 +3796,114 @@ class $UsersTable extends Users with TableInfo<$UsersTable, UserEntity> { } class MemberEntity extends DataClass implements Insertable { + /// The interested user id final String userId; + + /// The channel cid of which this user is part of final String channelCid; - final String role; - final DateTime inviteAcceptedAt; - final DateTime inviteRejectedAt; + + /// The role of the user in the channel + final String? role; + + /// The date on which the user accepted the invite to the channel + final DateTime? inviteAcceptedAt; + + /// The date on which the user rejected the invite to the channel + final DateTime? inviteRejectedAt; + + /// True if the user has been invited to the channel final bool invited; + + /// True if the member is banned from the channel final bool banned; + + /// True if the member is shadow banned from the channel final bool shadowBanned; + + /// True if the user is a moderator of the channel final bool isModerator; + + /// The date of creation final DateTime createdAt; + + /// The last date of update final DateTime updatedAt; MemberEntity( - {@required this.userId, - @required this.channelCid, + {required this.userId, + required this.channelCid, this.role, this.inviteAcceptedAt, this.inviteRejectedAt, - this.invited, - this.banned, - this.shadowBanned, - this.isModerator, - @required this.createdAt, - this.updatedAt}); + required this.invited, + required this.banned, + required this.shadowBanned, + required this.isModerator, + required this.createdAt, + required this.updatedAt}); factory MemberEntity.fromData(Map data, GeneratedDatabase db, - {String prefix}) { + {String? prefix}) { final effectivePrefix = prefix ?? ''; final stringType = db.typeSystem.forDartType(); final dateTimeType = db.typeSystem.forDartType(); final boolType = db.typeSystem.forDartType(); return MemberEntity( - userId: - stringType.mapFromDatabaseResponse(data['${effectivePrefix}user_id']), + userId: stringType + .mapFromDatabaseResponse(data['${effectivePrefix}user_id'])!, channelCid: stringType - .mapFromDatabaseResponse(data['${effectivePrefix}channel_cid']), + .mapFromDatabaseResponse(data['${effectivePrefix}channel_cid'])!, role: stringType.mapFromDatabaseResponse(data['${effectivePrefix}role']), inviteAcceptedAt: dateTimeType.mapFromDatabaseResponse( data['${effectivePrefix}invite_accepted_at']), inviteRejectedAt: dateTimeType.mapFromDatabaseResponse( data['${effectivePrefix}invite_rejected_at']), invited: - boolType.mapFromDatabaseResponse(data['${effectivePrefix}invited']), + boolType.mapFromDatabaseResponse(data['${effectivePrefix}invited'])!, banned: - boolType.mapFromDatabaseResponse(data['${effectivePrefix}banned']), + boolType.mapFromDatabaseResponse(data['${effectivePrefix}banned'])!, shadowBanned: boolType - .mapFromDatabaseResponse(data['${effectivePrefix}shadow_banned']), + .mapFromDatabaseResponse(data['${effectivePrefix}shadow_banned'])!, isModerator: boolType - .mapFromDatabaseResponse(data['${effectivePrefix}is_moderator']), + .mapFromDatabaseResponse(data['${effectivePrefix}is_moderator'])!, createdAt: dateTimeType - .mapFromDatabaseResponse(data['${effectivePrefix}created_at']), + .mapFromDatabaseResponse(data['${effectivePrefix}created_at'])!, updatedAt: dateTimeType - .mapFromDatabaseResponse(data['${effectivePrefix}updated_at']), + .mapFromDatabaseResponse(data['${effectivePrefix}updated_at'])!, ); } @override Map toColumns(bool nullToAbsent) { final map = {}; - if (!nullToAbsent || userId != null) { - map['user_id'] = Variable(userId); - } - if (!nullToAbsent || channelCid != null) { - map['channel_cid'] = Variable(channelCid); - } + map['user_id'] = Variable(userId); + map['channel_cid'] = Variable(channelCid); if (!nullToAbsent || role != null) { - map['role'] = Variable(role); + map['role'] = Variable(role); } if (!nullToAbsent || inviteAcceptedAt != null) { - map['invite_accepted_at'] = Variable(inviteAcceptedAt); + map['invite_accepted_at'] = Variable(inviteAcceptedAt); } if (!nullToAbsent || inviteRejectedAt != null) { - map['invite_rejected_at'] = Variable(inviteRejectedAt); - } - if (!nullToAbsent || invited != null) { - map['invited'] = Variable(invited); - } - if (!nullToAbsent || banned != null) { - map['banned'] = Variable(banned); - } - if (!nullToAbsent || shadowBanned != null) { - map['shadow_banned'] = Variable(shadowBanned); - } - if (!nullToAbsent || isModerator != null) { - map['is_moderator'] = Variable(isModerator); - } - if (!nullToAbsent || createdAt != null) { - map['created_at'] = Variable(createdAt); - } - if (!nullToAbsent || updatedAt != null) { - map['updated_at'] = Variable(updatedAt); + map['invite_rejected_at'] = Variable(inviteRejectedAt); } + map['invited'] = Variable(invited); + map['banned'] = Variable(banned); + map['shadow_banned'] = Variable(shadowBanned); + map['is_moderator'] = Variable(isModerator); + map['created_at'] = Variable(createdAt); + map['updated_at'] = Variable(updatedAt); return map; } factory MemberEntity.fromJson(Map json, - {ValueSerializer serializer}) { + {ValueSerializer? serializer}) { serializer ??= moorRuntimeOptions.defaultSerializer; return MemberEntity( userId: serializer.fromJson(json['userId']), channelCid: serializer.fromJson(json['channelCid']), - role: serializer.fromJson(json['role']), - inviteAcceptedAt: serializer.fromJson(json['inviteAcceptedAt']), - inviteRejectedAt: serializer.fromJson(json['inviteRejectedAt']), + role: serializer.fromJson(json['role']), + inviteAcceptedAt: + serializer.fromJson(json['inviteAcceptedAt']), + inviteRejectedAt: + serializer.fromJson(json['inviteRejectedAt']), invited: serializer.fromJson(json['invited']), banned: serializer.fromJson(json['banned']), shadowBanned: serializer.fromJson(json['shadowBanned']), @@ -3971,14 +3913,14 @@ class MemberEntity extends DataClass implements Insertable { ); } @override - Map toJson({ValueSerializer serializer}) { + Map toJson({ValueSerializer? serializer}) { serializer ??= moorRuntimeOptions.defaultSerializer; return { 'userId': serializer.toJson(userId), 'channelCid': serializer.toJson(channelCid), - 'role': serializer.toJson(role), - 'inviteAcceptedAt': serializer.toJson(inviteAcceptedAt), - 'inviteRejectedAt': serializer.toJson(inviteRejectedAt), + 'role': serializer.toJson(role), + 'inviteAcceptedAt': serializer.toJson(inviteAcceptedAt), + 'inviteRejectedAt': serializer.toJson(inviteRejectedAt), 'invited': serializer.toJson(invited), 'banned': serializer.toJson(banned), 'shadowBanned': serializer.toJson(shadowBanned), @@ -3989,17 +3931,17 @@ class MemberEntity extends DataClass implements Insertable { } MemberEntity copyWith( - {String userId, - String channelCid, - Value role = const Value.absent(), - Value inviteAcceptedAt = const Value.absent(), - Value inviteRejectedAt = const Value.absent(), - Value invited = const Value.absent(), - Value banned = const Value.absent(), - Value shadowBanned = const Value.absent(), - Value isModerator = const Value.absent(), - DateTime createdAt, - Value updatedAt = const Value.absent()}) => + {String? userId, + String? channelCid, + Value role = const Value.absent(), + Value inviteAcceptedAt = const Value.absent(), + Value inviteRejectedAt = const Value.absent(), + bool? invited, + bool? banned, + bool? shadowBanned, + bool? isModerator, + DateTime? createdAt, + DateTime? updatedAt}) => MemberEntity( userId: userId ?? this.userId, channelCid: channelCid ?? this.channelCid, @@ -4010,13 +3952,12 @@ class MemberEntity extends DataClass implements Insertable { inviteRejectedAt: inviteRejectedAt.present ? inviteRejectedAt.value : this.inviteRejectedAt, - invited: invited.present ? invited.value : this.invited, - banned: banned.present ? banned.value : this.banned, - shadowBanned: - shadowBanned.present ? shadowBanned.value : this.shadowBanned, - isModerator: isModerator.present ? isModerator.value : this.isModerator, + invited: invited ?? this.invited, + banned: banned ?? this.banned, + shadowBanned: shadowBanned ?? this.shadowBanned, + isModerator: isModerator ?? this.isModerator, createdAt: createdAt ?? this.createdAt, - updatedAt: updatedAt.present ? updatedAt.value : this.updatedAt, + updatedAt: updatedAt ?? this.updatedAt, ); @override String toString() { @@ -4077,9 +4018,9 @@ class MemberEntity extends DataClass implements Insertable { class MembersCompanion extends UpdateCompanion { final Value userId; final Value channelCid; - final Value role; - final Value inviteAcceptedAt; - final Value inviteRejectedAt; + final Value role; + final Value inviteAcceptedAt; + final Value inviteRejectedAt; final Value invited; final Value banned; final Value shadowBanned; @@ -4100,8 +4041,8 @@ class MembersCompanion extends UpdateCompanion { this.updatedAt = const Value.absent(), }); MembersCompanion.insert({ - @required String userId, - @required String channelCid, + required String userId, + required String channelCid, this.role = const Value.absent(), this.inviteAcceptedAt = const Value.absent(), this.inviteRejectedAt = const Value.absent(), @@ -4109,23 +4050,22 @@ class MembersCompanion extends UpdateCompanion { this.banned = const Value.absent(), this.shadowBanned = const Value.absent(), this.isModerator = const Value.absent(), - @required DateTime createdAt, + this.createdAt = const Value.absent(), this.updatedAt = const Value.absent(), }) : userId = Value(userId), - channelCid = Value(channelCid), - createdAt = Value(createdAt); + channelCid = Value(channelCid); static Insertable custom({ - Expression userId, - Expression channelCid, - Expression role, - Expression inviteAcceptedAt, - Expression inviteRejectedAt, - Expression invited, - Expression banned, - Expression shadowBanned, - Expression isModerator, - Expression createdAt, - Expression updatedAt, + Expression? userId, + Expression? channelCid, + Expression? role, + Expression? inviteAcceptedAt, + Expression? inviteRejectedAt, + Expression? invited, + Expression? banned, + Expression? shadowBanned, + Expression? isModerator, + Expression? createdAt, + Expression? updatedAt, }) { return RawValuesInsertable({ if (userId != null) 'user_id': userId, @@ -4143,17 +4083,17 @@ class MembersCompanion extends UpdateCompanion { } MembersCompanion copyWith( - {Value userId, - Value channelCid, - Value role, - Value inviteAcceptedAt, - Value inviteRejectedAt, - Value invited, - Value banned, - Value shadowBanned, - Value isModerator, - Value createdAt, - Value updatedAt}) { + {Value? userId, + Value? channelCid, + Value? role, + Value? inviteAcceptedAt, + Value? inviteRejectedAt, + Value? invited, + Value? banned, + Value? shadowBanned, + Value? isModerator, + Value? createdAt, + Value? updatedAt}) { return MembersCompanion( userId: userId ?? this.userId, channelCid: channelCid ?? this.channelCid, @@ -4179,13 +4119,13 @@ class MembersCompanion extends UpdateCompanion { map['channel_cid'] = Variable(channelCid.value); } if (role.present) { - map['role'] = Variable(role.value); + map['role'] = Variable(role.value); } if (inviteAcceptedAt.present) { - map['invite_accepted_at'] = Variable(inviteAcceptedAt.value); + map['invite_accepted_at'] = Variable(inviteAcceptedAt.value); } if (inviteRejectedAt.present) { - map['invite_rejected_at'] = Variable(inviteRejectedAt.value); + map['invite_rejected_at'] = Variable(inviteRejectedAt.value); } if (invited.present) { map['invited'] = Variable(invited.value); @@ -4230,12 +4170,11 @@ class MembersCompanion extends UpdateCompanion { class $MembersTable extends Members with TableInfo<$MembersTable, MemberEntity> { final GeneratedDatabase _db; - final String _alias; + final String? _alias; $MembersTable(this._db, [this._alias]); final VerificationMeta _userIdMeta = const VerificationMeta('userId'); - GeneratedTextColumn _userId; @override - GeneratedTextColumn get userId => _userId ??= _constructUserId(); + late final GeneratedTextColumn userId = _constructUserId(); GeneratedTextColumn _constructUserId() { return GeneratedTextColumn( 'user_id', @@ -4245,18 +4184,16 @@ class $MembersTable extends Members } final VerificationMeta _channelCidMeta = const VerificationMeta('channelCid'); - GeneratedTextColumn _channelCid; @override - GeneratedTextColumn get channelCid => _channelCid ??= _constructChannelCid(); + late final GeneratedTextColumn channelCid = _constructChannelCid(); GeneratedTextColumn _constructChannelCid() { return GeneratedTextColumn('channel_cid', $tableName, false, $customConstraints: 'REFERENCES channels(cid) ON DELETE CASCADE'); } final VerificationMeta _roleMeta = const VerificationMeta('role'); - GeneratedTextColumn _role; @override - GeneratedTextColumn get role => _role ??= _constructRole(); + late final GeneratedTextColumn role = _constructRole(); GeneratedTextColumn _constructRole() { return GeneratedTextColumn( 'role', @@ -4267,10 +4204,9 @@ class $MembersTable extends Members final VerificationMeta _inviteAcceptedAtMeta = const VerificationMeta('inviteAcceptedAt'); - GeneratedDateTimeColumn _inviteAcceptedAt; @override - GeneratedDateTimeColumn get inviteAcceptedAt => - _inviteAcceptedAt ??= _constructInviteAcceptedAt(); + late final GeneratedDateTimeColumn inviteAcceptedAt = + _constructInviteAcceptedAt(); GeneratedDateTimeColumn _constructInviteAcceptedAt() { return GeneratedDateTimeColumn( 'invite_accepted_at', @@ -4281,10 +4217,9 @@ class $MembersTable extends Members final VerificationMeta _inviteRejectedAtMeta = const VerificationMeta('inviteRejectedAt'); - GeneratedDateTimeColumn _inviteRejectedAt; @override - GeneratedDateTimeColumn get inviteRejectedAt => - _inviteRejectedAt ??= _constructInviteRejectedAt(); + late final GeneratedDateTimeColumn inviteRejectedAt = + _constructInviteRejectedAt(); GeneratedDateTimeColumn _constructInviteRejectedAt() { return GeneratedDateTimeColumn( 'invite_rejected_at', @@ -4294,79 +4229,53 @@ class $MembersTable extends Members } final VerificationMeta _invitedMeta = const VerificationMeta('invited'); - GeneratedBoolColumn _invited; @override - GeneratedBoolColumn get invited => _invited ??= _constructInvited(); + late final GeneratedBoolColumn invited = _constructInvited(); GeneratedBoolColumn _constructInvited() { - return GeneratedBoolColumn( - 'invited', - $tableName, - true, - ); + return GeneratedBoolColumn('invited', $tableName, false, + defaultValue: const Constant(false)); } final VerificationMeta _bannedMeta = const VerificationMeta('banned'); - GeneratedBoolColumn _banned; @override - GeneratedBoolColumn get banned => _banned ??= _constructBanned(); + late final GeneratedBoolColumn banned = _constructBanned(); GeneratedBoolColumn _constructBanned() { - return GeneratedBoolColumn( - 'banned', - $tableName, - true, - ); + return GeneratedBoolColumn('banned', $tableName, false, + defaultValue: const Constant(false)); } final VerificationMeta _shadowBannedMeta = const VerificationMeta('shadowBanned'); - GeneratedBoolColumn _shadowBanned; @override - GeneratedBoolColumn get shadowBanned => - _shadowBanned ??= _constructShadowBanned(); + late final GeneratedBoolColumn shadowBanned = _constructShadowBanned(); GeneratedBoolColumn _constructShadowBanned() { - return GeneratedBoolColumn( - 'shadow_banned', - $tableName, - true, - ); + return GeneratedBoolColumn('shadow_banned', $tableName, false, + defaultValue: const Constant(false)); } final VerificationMeta _isModeratorMeta = const VerificationMeta('isModerator'); - GeneratedBoolColumn _isModerator; @override - GeneratedBoolColumn get isModerator => - _isModerator ??= _constructIsModerator(); + late final GeneratedBoolColumn isModerator = _constructIsModerator(); GeneratedBoolColumn _constructIsModerator() { - return GeneratedBoolColumn( - 'is_moderator', - $tableName, - true, - ); + return GeneratedBoolColumn('is_moderator', $tableName, false, + defaultValue: const Constant(false)); } final VerificationMeta _createdAtMeta = const VerificationMeta('createdAt'); - GeneratedDateTimeColumn _createdAt; @override - GeneratedDateTimeColumn get createdAt => _createdAt ??= _constructCreatedAt(); + late final GeneratedDateTimeColumn createdAt = _constructCreatedAt(); GeneratedDateTimeColumn _constructCreatedAt() { - return GeneratedDateTimeColumn( - 'created_at', - $tableName, - false, - ); + return GeneratedDateTimeColumn('created_at', $tableName, false, + defaultValue: currentDateAndTime); } final VerificationMeta _updatedAtMeta = const VerificationMeta('updatedAt'); - GeneratedDateTimeColumn _updatedAt; @override - GeneratedDateTimeColumn get updatedAt => _updatedAt ??= _constructUpdatedAt(); + late final GeneratedDateTimeColumn updatedAt = _constructUpdatedAt(); GeneratedDateTimeColumn _constructUpdatedAt() { - return GeneratedDateTimeColumn( - 'updated_at', - $tableName, - true, - ); + return GeneratedDateTimeColumn('updated_at', $tableName, false, + defaultValue: currentDateAndTime); } @override @@ -4396,7 +4305,7 @@ class $MembersTable extends Members final data = instance.toColumns(true); if (data.containsKey('user_id')) { context.handle(_userIdMeta, - userId.isAcceptableOrUnknown(data['user_id'], _userIdMeta)); + userId.isAcceptableOrUnknown(data['user_id']!, _userIdMeta)); } else if (isInserting) { context.missing(_userIdMeta); } @@ -4404,55 +4313,53 @@ class $MembersTable extends Members context.handle( _channelCidMeta, channelCid.isAcceptableOrUnknown( - data['channel_cid'], _channelCidMeta)); + data['channel_cid']!, _channelCidMeta)); } else if (isInserting) { context.missing(_channelCidMeta); } if (data.containsKey('role')) { context.handle( - _roleMeta, role.isAcceptableOrUnknown(data['role'], _roleMeta)); + _roleMeta, role.isAcceptableOrUnknown(data['role']!, _roleMeta)); } if (data.containsKey('invite_accepted_at')) { context.handle( _inviteAcceptedAtMeta, inviteAcceptedAt.isAcceptableOrUnknown( - data['invite_accepted_at'], _inviteAcceptedAtMeta)); + data['invite_accepted_at']!, _inviteAcceptedAtMeta)); } if (data.containsKey('invite_rejected_at')) { context.handle( _inviteRejectedAtMeta, inviteRejectedAt.isAcceptableOrUnknown( - data['invite_rejected_at'], _inviteRejectedAtMeta)); + data['invite_rejected_at']!, _inviteRejectedAtMeta)); } if (data.containsKey('invited')) { context.handle(_invitedMeta, - invited.isAcceptableOrUnknown(data['invited'], _invitedMeta)); + invited.isAcceptableOrUnknown(data['invited']!, _invitedMeta)); } if (data.containsKey('banned')) { context.handle(_bannedMeta, - banned.isAcceptableOrUnknown(data['banned'], _bannedMeta)); + banned.isAcceptableOrUnknown(data['banned']!, _bannedMeta)); } if (data.containsKey('shadow_banned')) { context.handle( _shadowBannedMeta, shadowBanned.isAcceptableOrUnknown( - data['shadow_banned'], _shadowBannedMeta)); + data['shadow_banned']!, _shadowBannedMeta)); } if (data.containsKey('is_moderator')) { context.handle( _isModeratorMeta, isModerator.isAcceptableOrUnknown( - data['is_moderator'], _isModeratorMeta)); + data['is_moderator']!, _isModeratorMeta)); } if (data.containsKey('created_at')) { context.handle(_createdAtMeta, - createdAt.isAcceptableOrUnknown(data['created_at'], _createdAtMeta)); - } else if (isInserting) { - context.missing(_createdAtMeta); + createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta)); } if (data.containsKey('updated_at')) { context.handle(_updatedAtMeta, - updatedAt.isAcceptableOrUnknown(data['updated_at'], _updatedAtMeta)); + updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta)); } return context; } @@ -4460,7 +4367,7 @@ class $MembersTable extends Members @override Set get $primaryKey => {userId, channelCid}; @override - MemberEntity map(Map data, {String tablePrefix}) { + MemberEntity map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : null; return MemberEntity.fromData(data, _db, prefix: effectivePrefix); } @@ -4472,52 +4379,51 @@ class $MembersTable extends Members } class ReadEntity extends DataClass implements Insertable { + /// Date of the read event final DateTime lastRead; + + /// Id of the User who sent the event final String userId; + + /// The channel cid of which this read belongs final String channelCid; + + /// Number of unread messages final int unreadMessages; ReadEntity( - {@required this.lastRead, - @required this.userId, - @required this.channelCid, - this.unreadMessages}); + {required this.lastRead, + required this.userId, + required this.channelCid, + required this.unreadMessages}); factory ReadEntity.fromData(Map data, GeneratedDatabase db, - {String prefix}) { + {String? prefix}) { final effectivePrefix = prefix ?? ''; final dateTimeType = db.typeSystem.forDartType(); final stringType = db.typeSystem.forDartType(); final intType = db.typeSystem.forDartType(); return ReadEntity( lastRead: dateTimeType - .mapFromDatabaseResponse(data['${effectivePrefix}last_read']), - userId: - stringType.mapFromDatabaseResponse(data['${effectivePrefix}user_id']), + .mapFromDatabaseResponse(data['${effectivePrefix}last_read'])!, + userId: stringType + .mapFromDatabaseResponse(data['${effectivePrefix}user_id'])!, channelCid: stringType - .mapFromDatabaseResponse(data['${effectivePrefix}channel_cid']), + .mapFromDatabaseResponse(data['${effectivePrefix}channel_cid'])!, unreadMessages: intType - .mapFromDatabaseResponse(data['${effectivePrefix}unread_messages']), + .mapFromDatabaseResponse(data['${effectivePrefix}unread_messages'])!, ); } @override Map toColumns(bool nullToAbsent) { final map = {}; - if (!nullToAbsent || lastRead != null) { - map['last_read'] = Variable(lastRead); - } - if (!nullToAbsent || userId != null) { - map['user_id'] = Variable(userId); - } - if (!nullToAbsent || channelCid != null) { - map['channel_cid'] = Variable(channelCid); - } - if (!nullToAbsent || unreadMessages != null) { - map['unread_messages'] = Variable(unreadMessages); - } + map['last_read'] = Variable(lastRead); + map['user_id'] = Variable(userId); + map['channel_cid'] = Variable(channelCid); + map['unread_messages'] = Variable(unreadMessages); return map; } factory ReadEntity.fromJson(Map json, - {ValueSerializer serializer}) { + {ValueSerializer? serializer}) { serializer ??= moorRuntimeOptions.defaultSerializer; return ReadEntity( lastRead: serializer.fromJson(json['lastRead']), @@ -4527,7 +4433,7 @@ class ReadEntity extends DataClass implements Insertable { ); } @override - Map toJson({ValueSerializer serializer}) { + Map toJson({ValueSerializer? serializer}) { serializer ??= moorRuntimeOptions.defaultSerializer; return { 'lastRead': serializer.toJson(lastRead), @@ -4538,16 +4444,15 @@ class ReadEntity extends DataClass implements Insertable { } ReadEntity copyWith( - {DateTime lastRead, - String userId, - String channelCid, - Value unreadMessages = const Value.absent()}) => + {DateTime? lastRead, + String? userId, + String? channelCid, + int? unreadMessages}) => ReadEntity( lastRead: lastRead ?? this.lastRead, userId: userId ?? this.userId, channelCid: channelCid ?? this.channelCid, - unreadMessages: - unreadMessages.present ? unreadMessages.value : this.unreadMessages, + unreadMessages: unreadMessages ?? this.unreadMessages, ); @override String toString() { @@ -4587,18 +4492,18 @@ class ReadsCompanion extends UpdateCompanion { this.unreadMessages = const Value.absent(), }); ReadsCompanion.insert({ - @required DateTime lastRead, - @required String userId, - @required String channelCid, + required DateTime lastRead, + required String userId, + required String channelCid, this.unreadMessages = const Value.absent(), }) : lastRead = Value(lastRead), userId = Value(userId), channelCid = Value(channelCid); static Insertable custom({ - Expression lastRead, - Expression userId, - Expression channelCid, - Expression unreadMessages, + Expression? lastRead, + Expression? userId, + Expression? channelCid, + Expression? unreadMessages, }) { return RawValuesInsertable({ if (lastRead != null) 'last_read': lastRead, @@ -4609,10 +4514,10 @@ class ReadsCompanion extends UpdateCompanion { } ReadsCompanion copyWith( - {Value lastRead, - Value userId, - Value channelCid, - Value unreadMessages}) { + {Value? lastRead, + Value? userId, + Value? channelCid, + Value? unreadMessages}) { return ReadsCompanion( lastRead: lastRead ?? this.lastRead, userId: userId ?? this.userId, @@ -4653,12 +4558,11 @@ class ReadsCompanion extends UpdateCompanion { class $ReadsTable extends Reads with TableInfo<$ReadsTable, ReadEntity> { final GeneratedDatabase _db; - final String _alias; + final String? _alias; $ReadsTable(this._db, [this._alias]); final VerificationMeta _lastReadMeta = const VerificationMeta('lastRead'); - GeneratedDateTimeColumn _lastRead; @override - GeneratedDateTimeColumn get lastRead => _lastRead ??= _constructLastRead(); + late final GeneratedDateTimeColumn lastRead = _constructLastRead(); GeneratedDateTimeColumn _constructLastRead() { return GeneratedDateTimeColumn( 'last_read', @@ -4668,9 +4572,8 @@ class $ReadsTable extends Reads with TableInfo<$ReadsTable, ReadEntity> { } final VerificationMeta _userIdMeta = const VerificationMeta('userId'); - GeneratedTextColumn _userId; @override - GeneratedTextColumn get userId => _userId ??= _constructUserId(); + late final GeneratedTextColumn userId = _constructUserId(); GeneratedTextColumn _constructUserId() { return GeneratedTextColumn( 'user_id', @@ -4680,9 +4583,8 @@ class $ReadsTable extends Reads with TableInfo<$ReadsTable, ReadEntity> { } final VerificationMeta _channelCidMeta = const VerificationMeta('channelCid'); - GeneratedTextColumn _channelCid; @override - GeneratedTextColumn get channelCid => _channelCid ??= _constructChannelCid(); + late final GeneratedTextColumn channelCid = _constructChannelCid(); GeneratedTextColumn _constructChannelCid() { return GeneratedTextColumn('channel_cid', $tableName, false, $customConstraints: 'REFERENCES channels(cid) ON DELETE CASCADE'); @@ -4690,16 +4592,11 @@ class $ReadsTable extends Reads with TableInfo<$ReadsTable, ReadEntity> { final VerificationMeta _unreadMessagesMeta = const VerificationMeta('unreadMessages'); - GeneratedIntColumn _unreadMessages; @override - GeneratedIntColumn get unreadMessages => - _unreadMessages ??= _constructUnreadMessages(); + late final GeneratedIntColumn unreadMessages = _constructUnreadMessages(); GeneratedIntColumn _constructUnreadMessages() { - return GeneratedIntColumn( - 'unread_messages', - $tableName, - true, - ); + return GeneratedIntColumn('unread_messages', $tableName, false, + defaultValue: const Constant(0)); } @override @@ -4718,13 +4615,13 @@ class $ReadsTable extends Reads with TableInfo<$ReadsTable, ReadEntity> { final data = instance.toColumns(true); if (data.containsKey('last_read')) { context.handle(_lastReadMeta, - lastRead.isAcceptableOrUnknown(data['last_read'], _lastReadMeta)); + lastRead.isAcceptableOrUnknown(data['last_read']!, _lastReadMeta)); } else if (isInserting) { context.missing(_lastReadMeta); } if (data.containsKey('user_id')) { context.handle(_userIdMeta, - userId.isAcceptableOrUnknown(data['user_id'], _userIdMeta)); + userId.isAcceptableOrUnknown(data['user_id']!, _userIdMeta)); } else if (isInserting) { context.missing(_userIdMeta); } @@ -4732,7 +4629,7 @@ class $ReadsTable extends Reads with TableInfo<$ReadsTable, ReadEntity> { context.handle( _channelCidMeta, channelCid.isAcceptableOrUnknown( - data['channel_cid'], _channelCidMeta)); + data['channel_cid']!, _channelCidMeta)); } else if (isInserting) { context.missing(_channelCidMeta); } @@ -4740,7 +4637,7 @@ class $ReadsTable extends Reads with TableInfo<$ReadsTable, ReadEntity> { context.handle( _unreadMessagesMeta, unreadMessages.isAcceptableOrUnknown( - data['unread_messages'], _unreadMessagesMeta)); + data['unread_messages']!, _unreadMessagesMeta)); } return context; } @@ -4748,7 +4645,7 @@ class $ReadsTable extends Reads with TableInfo<$ReadsTable, ReadEntity> { @override Set get $primaryKey => {userId, channelCid}; @override - ReadEntity map(Map data, {String tablePrefix}) { + ReadEntity map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : null; return ReadEntity.fromData(data, _db, prefix: effectivePrefix); } @@ -4761,35 +4658,34 @@ class $ReadsTable extends Reads with TableInfo<$ReadsTable, ReadEntity> { class ChannelQueryEntity extends DataClass implements Insertable { + /// The unique hash of this query final String queryHash; + + /// The channel cid of this query final String channelCid; - ChannelQueryEntity({@required this.queryHash, @required this.channelCid}); + ChannelQueryEntity({required this.queryHash, required this.channelCid}); factory ChannelQueryEntity.fromData( Map data, GeneratedDatabase db, - {String prefix}) { + {String? prefix}) { final effectivePrefix = prefix ?? ''; final stringType = db.typeSystem.forDartType(); return ChannelQueryEntity( queryHash: stringType - .mapFromDatabaseResponse(data['${effectivePrefix}query_hash']), + .mapFromDatabaseResponse(data['${effectivePrefix}query_hash'])!, channelCid: stringType - .mapFromDatabaseResponse(data['${effectivePrefix}channel_cid']), + .mapFromDatabaseResponse(data['${effectivePrefix}channel_cid'])!, ); } @override Map toColumns(bool nullToAbsent) { final map = {}; - if (!nullToAbsent || queryHash != null) { - map['query_hash'] = Variable(queryHash); - } - if (!nullToAbsent || channelCid != null) { - map['channel_cid'] = Variable(channelCid); - } + map['query_hash'] = Variable(queryHash); + map['channel_cid'] = Variable(channelCid); return map; } factory ChannelQueryEntity.fromJson(Map json, - {ValueSerializer serializer}) { + {ValueSerializer? serializer}) { serializer ??= moorRuntimeOptions.defaultSerializer; return ChannelQueryEntity( queryHash: serializer.fromJson(json['queryHash']), @@ -4797,7 +4693,7 @@ class ChannelQueryEntity extends DataClass ); } @override - Map toJson({ValueSerializer serializer}) { + Map toJson({ValueSerializer? serializer}) { serializer ??= moorRuntimeOptions.defaultSerializer; return { 'queryHash': serializer.toJson(queryHash), @@ -4805,7 +4701,7 @@ class ChannelQueryEntity extends DataClass }; } - ChannelQueryEntity copyWith({String queryHash, String channelCid}) => + ChannelQueryEntity copyWith({String? queryHash, String? channelCid}) => ChannelQueryEntity( queryHash: queryHash ?? this.queryHash, channelCid: channelCid ?? this.channelCid, @@ -4837,13 +4733,13 @@ class ChannelQueriesCompanion extends UpdateCompanion { this.channelCid = const Value.absent(), }); ChannelQueriesCompanion.insert({ - @required String queryHash, - @required String channelCid, - }) : queryHash = Value(queryHash), + required String queryHash, + required String channelCid, + }) : queryHash = Value(queryHash), channelCid = Value(channelCid); static Insertable custom({ - Expression queryHash, - Expression channelCid, + Expression? queryHash, + Expression? channelCid, }) { return RawValuesInsertable({ if (queryHash != null) 'query_hash': queryHash, @@ -4852,7 +4748,7 @@ class ChannelQueriesCompanion extends UpdateCompanion { } ChannelQueriesCompanion copyWith( - {Value queryHash, Value channelCid}) { + {Value? queryHash, Value? channelCid}) { return ChannelQueriesCompanion( queryHash: queryHash ?? this.queryHash, channelCid: channelCid ?? this.channelCid, @@ -4884,12 +4780,11 @@ class ChannelQueriesCompanion extends UpdateCompanion { class $ChannelQueriesTable extends ChannelQueries with TableInfo<$ChannelQueriesTable, ChannelQueryEntity> { final GeneratedDatabase _db; - final String _alias; + final String? _alias; $ChannelQueriesTable(this._db, [this._alias]); final VerificationMeta _queryHashMeta = const VerificationMeta('queryHash'); - GeneratedTextColumn _queryHash; @override - GeneratedTextColumn get queryHash => _queryHash ??= _constructQueryHash(); + late final GeneratedTextColumn queryHash = _constructQueryHash(); GeneratedTextColumn _constructQueryHash() { return GeneratedTextColumn( 'query_hash', @@ -4899,9 +4794,8 @@ class $ChannelQueriesTable extends ChannelQueries } final VerificationMeta _channelCidMeta = const VerificationMeta('channelCid'); - GeneratedTextColumn _channelCid; @override - GeneratedTextColumn get channelCid => _channelCid ??= _constructChannelCid(); + late final GeneratedTextColumn channelCid = _constructChannelCid(); GeneratedTextColumn _constructChannelCid() { return GeneratedTextColumn( 'channel_cid', @@ -4925,7 +4819,7 @@ class $ChannelQueriesTable extends ChannelQueries final data = instance.toColumns(true); if (data.containsKey('query_hash')) { context.handle(_queryHashMeta, - queryHash.isAcceptableOrUnknown(data['query_hash'], _queryHashMeta)); + queryHash.isAcceptableOrUnknown(data['query_hash']!, _queryHashMeta)); } else if (isInserting) { context.missing(_queryHashMeta); } @@ -4933,7 +4827,7 @@ class $ChannelQueriesTable extends ChannelQueries context.handle( _channelCidMeta, channelCid.isAcceptableOrUnknown( - data['channel_cid'], _channelCidMeta)); + data['channel_cid']!, _channelCidMeta)); } else if (isInserting) { context.missing(_channelCidMeta); } @@ -4943,7 +4837,7 @@ class $ChannelQueriesTable extends ChannelQueries @override Set get $primaryKey => {queryHash, channelCid}; @override - ChannelQueryEntity map(Map data, {String tablePrefix}) { + ChannelQueryEntity map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : null; return ChannelQueryEntity.fromData(data, _db, prefix: effectivePrefix); } @@ -4956,14 +4850,25 @@ class $ChannelQueriesTable extends ChannelQueries class ConnectionEventEntity extends DataClass implements Insertable { + /// event id final int id; - final Map ownUser; - final int totalUnreadCount; - final int unreadChannels; - final DateTime lastEventAt; - final DateTime lastSyncAt; + + /// User object of the current user + final Map? ownUser; + + /// The number of unread messages for current user + final int? totalUnreadCount; + + /// User total unread channels for current user + final int? unreadChannels; + + /// DateTime of the last event + final DateTime? lastEventAt; + + /// DateTime of the last sync + final DateTime? lastSyncAt; ConnectionEventEntity( - {@required this.id, + {required this.id, this.ownUser, this.totalUnreadCount, this.unreadChannels, @@ -4971,13 +4876,13 @@ class ConnectionEventEntity extends DataClass this.lastSyncAt}); factory ConnectionEventEntity.fromData( Map data, GeneratedDatabase db, - {String prefix}) { + {String? prefix}) { final effectivePrefix = prefix ?? ''; final intType = db.typeSystem.forDartType(); final stringType = db.typeSystem.forDartType(); final dateTimeType = db.typeSystem.forDartType(); return ConnectionEventEntity( - id: intType.mapFromDatabaseResponse(data['${effectivePrefix}id']), + id: intType.mapFromDatabaseResponse(data['${effectivePrefix}id'])!, ownUser: $ConnectionEventsTable.$converter0.mapToDart(stringType .mapFromDatabaseResponse(data['${effectivePrefix}own_user'])), totalUnreadCount: intType.mapFromDatabaseResponse( @@ -4993,60 +4898,58 @@ class ConnectionEventEntity extends DataClass @override Map toColumns(bool nullToAbsent) { final map = {}; - if (!nullToAbsent || id != null) { - map['id'] = Variable(id); - } + map['id'] = Variable(id); if (!nullToAbsent || ownUser != null) { final converter = $ConnectionEventsTable.$converter0; - map['own_user'] = Variable(converter.mapToSql(ownUser)); + map['own_user'] = Variable(converter.mapToSql(ownUser)); } if (!nullToAbsent || totalUnreadCount != null) { - map['total_unread_count'] = Variable(totalUnreadCount); + map['total_unread_count'] = Variable(totalUnreadCount); } if (!nullToAbsent || unreadChannels != null) { - map['unread_channels'] = Variable(unreadChannels); + map['unread_channels'] = Variable(unreadChannels); } if (!nullToAbsent || lastEventAt != null) { - map['last_event_at'] = Variable(lastEventAt); + map['last_event_at'] = Variable(lastEventAt); } if (!nullToAbsent || lastSyncAt != null) { - map['last_sync_at'] = Variable(lastSyncAt); + map['last_sync_at'] = Variable(lastSyncAt); } return map; } factory ConnectionEventEntity.fromJson(Map json, - {ValueSerializer serializer}) { + {ValueSerializer? serializer}) { serializer ??= moorRuntimeOptions.defaultSerializer; return ConnectionEventEntity( id: serializer.fromJson(json['id']), - ownUser: serializer.fromJson>(json['ownUser']), - totalUnreadCount: serializer.fromJson(json['totalUnreadCount']), - unreadChannels: serializer.fromJson(json['unreadChannels']), - lastEventAt: serializer.fromJson(json['lastEventAt']), - lastSyncAt: serializer.fromJson(json['lastSyncAt']), + ownUser: serializer.fromJson?>(json['ownUser']), + totalUnreadCount: serializer.fromJson(json['totalUnreadCount']), + unreadChannels: serializer.fromJson(json['unreadChannels']), + lastEventAt: serializer.fromJson(json['lastEventAt']), + lastSyncAt: serializer.fromJson(json['lastSyncAt']), ); } @override - Map toJson({ValueSerializer serializer}) { + Map toJson({ValueSerializer? serializer}) { serializer ??= moorRuntimeOptions.defaultSerializer; return { 'id': serializer.toJson(id), - 'ownUser': serializer.toJson>(ownUser), - 'totalUnreadCount': serializer.toJson(totalUnreadCount), - 'unreadChannels': serializer.toJson(unreadChannels), - 'lastEventAt': serializer.toJson(lastEventAt), - 'lastSyncAt': serializer.toJson(lastSyncAt), + 'ownUser': serializer.toJson?>(ownUser), + 'totalUnreadCount': serializer.toJson(totalUnreadCount), + 'unreadChannels': serializer.toJson(unreadChannels), + 'lastEventAt': serializer.toJson(lastEventAt), + 'lastSyncAt': serializer.toJson(lastSyncAt), }; } ConnectionEventEntity copyWith( - {int id, - Value> ownUser = const Value.absent(), - Value totalUnreadCount = const Value.absent(), - Value unreadChannels = const Value.absent(), - Value lastEventAt = const Value.absent(), - Value lastSyncAt = const Value.absent()}) => + {int? id, + Value?> ownUser = const Value.absent(), + Value totalUnreadCount = const Value.absent(), + Value unreadChannels = const Value.absent(), + Value lastEventAt = const Value.absent(), + Value lastSyncAt = const Value.absent()}) => ConnectionEventEntity( id: id ?? this.id, ownUser: ownUser.present ? ownUser.value : this.ownUser, @@ -5094,11 +4997,11 @@ class ConnectionEventEntity extends DataClass class ConnectionEventsCompanion extends UpdateCompanion { final Value id; - final Value> ownUser; - final Value totalUnreadCount; - final Value unreadChannels; - final Value lastEventAt; - final Value lastSyncAt; + final Value?> ownUser; + final Value totalUnreadCount; + final Value unreadChannels; + final Value lastEventAt; + final Value lastSyncAt; const ConnectionEventsCompanion({ this.id = const Value.absent(), this.ownUser = const Value.absent(), @@ -5116,12 +5019,12 @@ class ConnectionEventsCompanion extends UpdateCompanion { this.lastSyncAt = const Value.absent(), }); static Insertable custom({ - Expression id, - Expression ownUser, - Expression totalUnreadCount, - Expression unreadChannels, - Expression lastEventAt, - Expression lastSyncAt, + Expression? id, + Expression?>? ownUser, + Expression? totalUnreadCount, + Expression? unreadChannels, + Expression? lastEventAt, + Expression? lastSyncAt, }) { return RawValuesInsertable({ if (id != null) 'id': id, @@ -5134,12 +5037,12 @@ class ConnectionEventsCompanion extends UpdateCompanion { } ConnectionEventsCompanion copyWith( - {Value id, - Value> ownUser, - Value totalUnreadCount, - Value unreadChannels, - Value lastEventAt, - Value lastSyncAt}) { + {Value? id, + Value?>? ownUser, + Value? totalUnreadCount, + Value? unreadChannels, + Value? lastEventAt, + Value? lastSyncAt}) { return ConnectionEventsCompanion( id: id ?? this.id, ownUser: ownUser ?? this.ownUser, @@ -5158,19 +5061,19 @@ class ConnectionEventsCompanion extends UpdateCompanion { } if (ownUser.present) { final converter = $ConnectionEventsTable.$converter0; - map['own_user'] = Variable(converter.mapToSql(ownUser.value)); + map['own_user'] = Variable(converter.mapToSql(ownUser.value)); } if (totalUnreadCount.present) { - map['total_unread_count'] = Variable(totalUnreadCount.value); + map['total_unread_count'] = Variable(totalUnreadCount.value); } if (unreadChannels.present) { - map['unread_channels'] = Variable(unreadChannels.value); + map['unread_channels'] = Variable(unreadChannels.value); } if (lastEventAt.present) { - map['last_event_at'] = Variable(lastEventAt.value); + map['last_event_at'] = Variable(lastEventAt.value); } if (lastSyncAt.present) { - map['last_sync_at'] = Variable(lastSyncAt.value); + map['last_sync_at'] = Variable(lastSyncAt.value); } return map; } @@ -5192,12 +5095,11 @@ class ConnectionEventsCompanion extends UpdateCompanion { class $ConnectionEventsTable extends ConnectionEvents with TableInfo<$ConnectionEventsTable, ConnectionEventEntity> { final GeneratedDatabase _db; - final String _alias; + final String? _alias; $ConnectionEventsTable(this._db, [this._alias]); final VerificationMeta _idMeta = const VerificationMeta('id'); - GeneratedIntColumn _id; @override - GeneratedIntColumn get id => _id ??= _constructId(); + late final GeneratedIntColumn id = _constructId(); GeneratedIntColumn _constructId() { return GeneratedIntColumn( 'id', @@ -5207,9 +5109,8 @@ class $ConnectionEventsTable extends ConnectionEvents } final VerificationMeta _ownUserMeta = const VerificationMeta('ownUser'); - GeneratedTextColumn _ownUser; @override - GeneratedTextColumn get ownUser => _ownUser ??= _constructOwnUser(); + late final GeneratedTextColumn ownUser = _constructOwnUser(); GeneratedTextColumn _constructOwnUser() { return GeneratedTextColumn( 'own_user', @@ -5220,10 +5121,8 @@ class $ConnectionEventsTable extends ConnectionEvents final VerificationMeta _totalUnreadCountMeta = const VerificationMeta('totalUnreadCount'); - GeneratedIntColumn _totalUnreadCount; @override - GeneratedIntColumn get totalUnreadCount => - _totalUnreadCount ??= _constructTotalUnreadCount(); + late final GeneratedIntColumn totalUnreadCount = _constructTotalUnreadCount(); GeneratedIntColumn _constructTotalUnreadCount() { return GeneratedIntColumn( 'total_unread_count', @@ -5234,10 +5133,8 @@ class $ConnectionEventsTable extends ConnectionEvents final VerificationMeta _unreadChannelsMeta = const VerificationMeta('unreadChannels'); - GeneratedIntColumn _unreadChannels; @override - GeneratedIntColumn get unreadChannels => - _unreadChannels ??= _constructUnreadChannels(); + late final GeneratedIntColumn unreadChannels = _constructUnreadChannels(); GeneratedIntColumn _constructUnreadChannels() { return GeneratedIntColumn( 'unread_channels', @@ -5248,10 +5145,8 @@ class $ConnectionEventsTable extends ConnectionEvents final VerificationMeta _lastEventAtMeta = const VerificationMeta('lastEventAt'); - GeneratedDateTimeColumn _lastEventAt; @override - GeneratedDateTimeColumn get lastEventAt => - _lastEventAt ??= _constructLastEventAt(); + late final GeneratedDateTimeColumn lastEventAt = _constructLastEventAt(); GeneratedDateTimeColumn _constructLastEventAt() { return GeneratedDateTimeColumn( 'last_event_at', @@ -5261,10 +5156,8 @@ class $ConnectionEventsTable extends ConnectionEvents } final VerificationMeta _lastSyncAtMeta = const VerificationMeta('lastSyncAt'); - GeneratedDateTimeColumn _lastSyncAt; @override - GeneratedDateTimeColumn get lastSyncAt => - _lastSyncAt ??= _constructLastSyncAt(); + late final GeneratedDateTimeColumn lastSyncAt = _constructLastSyncAt(); GeneratedDateTimeColumn _constructLastSyncAt() { return GeneratedDateTimeColumn( 'last_sync_at', @@ -5289,32 +5182,32 @@ class $ConnectionEventsTable extends ConnectionEvents final context = VerificationContext(); final data = instance.toColumns(true); if (data.containsKey('id')) { - context.handle(_idMeta, id.isAcceptableOrUnknown(data['id'], _idMeta)); + context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); } context.handle(_ownUserMeta, const VerificationResult.success()); if (data.containsKey('total_unread_count')) { context.handle( _totalUnreadCountMeta, totalUnreadCount.isAcceptableOrUnknown( - data['total_unread_count'], _totalUnreadCountMeta)); + data['total_unread_count']!, _totalUnreadCountMeta)); } if (data.containsKey('unread_channels')) { context.handle( _unreadChannelsMeta, unreadChannels.isAcceptableOrUnknown( - data['unread_channels'], _unreadChannelsMeta)); + data['unread_channels']!, _unreadChannelsMeta)); } if (data.containsKey('last_event_at')) { context.handle( _lastEventAtMeta, lastEventAt.isAcceptableOrUnknown( - data['last_event_at'], _lastEventAtMeta)); + data['last_event_at']!, _lastEventAtMeta)); } if (data.containsKey('last_sync_at')) { context.handle( _lastSyncAtMeta, lastSyncAt.isAcceptableOrUnknown( - data['last_sync_at'], _lastSyncAtMeta)); + data['last_sync_at']!, _lastSyncAtMeta)); } return context; } @@ -5322,7 +5215,7 @@ class $ConnectionEventsTable extends ConnectionEvents @override Set get $primaryKey => {id}; @override - ConnectionEventEntity map(Map data, {String tablePrefix}) { + ConnectionEventEntity map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : null; return ConnectionEventEntity.fromData(data, _db, prefix: effectivePrefix); } @@ -5332,58 +5225,35 @@ class $ConnectionEventsTable extends ConnectionEvents return $ConnectionEventsTable(_db, alias); } - static TypeConverter, String> $converter0 = - MapConverter(); + static TypeConverter, String> $converter0 = + MapConverter(); } abstract class _$MoorChatDatabase extends GeneratedDatabase { _$MoorChatDatabase(QueryExecutor e) : super(SqlTypeSystem.defaultInstance, e); _$MoorChatDatabase.connect(DatabaseConnection c) : super.connect(c); - $ChannelsTable _channels; - $ChannelsTable get channels => _channels ??= $ChannelsTable(this); - $MessagesTable _messages; - $MessagesTable get messages => _messages ??= $MessagesTable(this); - $PinnedMessagesTable _pinnedMessages; - $PinnedMessagesTable get pinnedMessages => - _pinnedMessages ??= $PinnedMessagesTable(this); - $ReactionsTable _reactions; - $ReactionsTable get reactions => _reactions ??= $ReactionsTable(this); - $UsersTable _users; - $UsersTable get users => _users ??= $UsersTable(this); - $MembersTable _members; - $MembersTable get members => _members ??= $MembersTable(this); - $ReadsTable _reads; - $ReadsTable get reads => _reads ??= $ReadsTable(this); - $ChannelQueriesTable _channelQueries; - $ChannelQueriesTable get channelQueries => - _channelQueries ??= $ChannelQueriesTable(this); - $ConnectionEventsTable _connectionEvents; - $ConnectionEventsTable get connectionEvents => - _connectionEvents ??= $ConnectionEventsTable(this); - UserDao _userDao; - UserDao get userDao => _userDao ??= UserDao(this as MoorChatDatabase); - ChannelDao _channelDao; - ChannelDao get channelDao => - _channelDao ??= ChannelDao(this as MoorChatDatabase); - MessageDao _messageDao; - MessageDao get messageDao => - _messageDao ??= MessageDao(this as MoorChatDatabase); - PinnedMessageDao _pinnedMessageDao; - PinnedMessageDao get pinnedMessageDao => - _pinnedMessageDao ??= PinnedMessageDao(this as MoorChatDatabase); - MemberDao _memberDao; - MemberDao get memberDao => _memberDao ??= MemberDao(this as MoorChatDatabase); - ReactionDao _reactionDao; - ReactionDao get reactionDao => - _reactionDao ??= ReactionDao(this as MoorChatDatabase); - ReadDao _readDao; - ReadDao get readDao => _readDao ??= ReadDao(this as MoorChatDatabase); - ChannelQueryDao _channelQueryDao; - ChannelQueryDao get channelQueryDao => - _channelQueryDao ??= ChannelQueryDao(this as MoorChatDatabase); - ConnectionEventDao _connectionEventDao; - ConnectionEventDao get connectionEventDao => - _connectionEventDao ??= ConnectionEventDao(this as MoorChatDatabase); + late final $ChannelsTable channels = $ChannelsTable(this); + late final $MessagesTable messages = $MessagesTable(this); + late final $PinnedMessagesTable pinnedMessages = $PinnedMessagesTable(this); + late final $ReactionsTable reactions = $ReactionsTable(this); + late final $UsersTable users = $UsersTable(this); + late final $MembersTable members = $MembersTable(this); + late final $ReadsTable reads = $ReadsTable(this); + late final $ChannelQueriesTable channelQueries = $ChannelQueriesTable(this); + late final $ConnectionEventsTable connectionEvents = + $ConnectionEventsTable(this); + late final UserDao userDao = UserDao(this as MoorChatDatabase); + late final ChannelDao channelDao = ChannelDao(this as MoorChatDatabase); + late final MessageDao messageDao = MessageDao(this as MoorChatDatabase); + late final PinnedMessageDao pinnedMessageDao = + PinnedMessageDao(this as MoorChatDatabase); + late final MemberDao memberDao = MemberDao(this as MoorChatDatabase); + late final ReactionDao reactionDao = ReactionDao(this as MoorChatDatabase); + late final ReadDao readDao = ReadDao(this as MoorChatDatabase); + late final ChannelQueryDao channelQueryDao = + ChannelQueryDao(this as MoorChatDatabase); + late final ConnectionEventDao connectionEventDao = + ConnectionEventDao(this as MoorChatDatabase); @override Iterable get allTables => allSchemaEntities.whereType(); @override diff --git a/packages/stream_chat_persistence/lib/src/entity/channels.dart b/packages/stream_chat_persistence/lib/src/entity/channels.dart index 6f60fac3..e6c280ed 100644 --- a/packages/stream_chat_persistence/lib/src/entity/channels.dart +++ b/packages/stream_chat_persistence/lib/src/entity/channels.dart @@ -15,7 +15,7 @@ class Channels extends Table { TextColumn get cid => text()(); /// The channel configuration data - TextColumn get config => text().map(MapConverter())(); + TextColumn get config => text().map(MapConverter())(); /// True if this channel entity is frozen BoolColumn get frozen => boolean().withDefault(const Constant(false))(); @@ -24,16 +24,16 @@ class Channels extends Table { DateTimeColumn get lastMessageAt => dateTime().nullable()(); /// The date of channel creation - DateTimeColumn get createdAt => dateTime().nullable()(); + DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)(); /// The date of the last channel update - DateTimeColumn get updatedAt => dateTime().nullable()(); + DateTimeColumn get updatedAt => dateTime().withDefault(currentDateAndTime)(); /// The date of channel deletion DateTimeColumn get deletedAt => dateTime().nullable()(); /// The count of this channel members - IntColumn get memberCount => integer().nullable()(); + IntColumn get memberCount => integer().withDefault(const Constant(0))(); /// The id of the user that created this channel TextColumn get createdById => text().nullable()(); diff --git a/packages/stream_chat_persistence/lib/src/entity/connection_events.dart b/packages/stream_chat_persistence/lib/src/entity/connection_events.dart index 69538edc..cf0819ae 100644 --- a/packages/stream_chat_persistence/lib/src/entity/connection_events.dart +++ b/packages/stream_chat_persistence/lib/src/entity/connection_events.dart @@ -9,7 +9,7 @@ class ConnectionEvents extends Table { IntColumn get id => integer()(); /// User object of the current user - TextColumn get ownUser => text().nullable().map(MapConverter())(); + TextColumn get ownUser => text().nullable().map(MapConverter())(); /// The number of unread messages for current user IntColumn get totalUnreadCount => integer().nullable()(); diff --git a/packages/stream_chat_persistence/lib/src/entity/members.dart b/packages/stream_chat_persistence/lib/src/entity/members.dart index c7316640..8d3d4a57 100644 --- a/packages/stream_chat_persistence/lib/src/entity/members.dart +++ b/packages/stream_chat_persistence/lib/src/entity/members.dart @@ -21,22 +21,22 @@ class Members extends Table { DateTimeColumn get inviteRejectedAt => dateTime().nullable()(); /// True if the user has been invited to the channel - BoolColumn get invited => boolean().nullable()(); + BoolColumn get invited => boolean().withDefault(const Constant(false))(); /// True if the member is banned from the channel - BoolColumn get banned => boolean().nullable()(); + BoolColumn get banned => boolean().withDefault(const Constant(false))(); /// True if the member is shadow banned from the channel - BoolColumn get shadowBanned => boolean().nullable()(); + BoolColumn get shadowBanned => boolean().withDefault(const Constant(false))(); /// True if the user is a moderator of the channel - BoolColumn get isModerator => boolean().nullable()(); + BoolColumn get isModerator => boolean().withDefault(const Constant(false))(); /// The date of creation - DateTimeColumn get createdAt => dateTime()(); + DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)(); /// The last date of update - DateTimeColumn get updatedAt => dateTime().nullable()(); + DateTimeColumn get updatedAt => dateTime().withDefault(currentDateAndTime)(); @override Set get primaryKey => { diff --git a/packages/stream_chat_persistence/lib/src/entity/messages.dart b/packages/stream_chat_persistence/lib/src/entity/messages.dart index 8840d85f..7cb1326d 100644 --- a/packages/stream_chat_persistence/lib/src/entity/messages.dart +++ b/packages/stream_chat_persistence/lib/src/entity/messages.dart @@ -15,19 +15,18 @@ class Messages extends Table { /// The list of attachments, either provided by the user /// or generated from a command or as a result of URL scraping. - TextColumn get attachments => - text().nullable().map(ListConverter())(); + TextColumn get attachments => text().map(ListConverter())(); /// The status of a sending message - IntColumn get status => - integer().nullable().map(MessageSendingStatusConverter())(); + IntColumn get status => integer() + .withDefault(const Constant(1)) + .map(MessageSendingStatusConverter())(); /// The message type - TextColumn get type => text().nullable()(); + TextColumn get type => text().withDefault(const Constant('regular'))(); /// The list of user mentioned in the message - TextColumn get mentionedUsers => - text().nullable().map(ListConverter())(); + TextColumn get mentionedUsers => text().map(ListConverter())(); /// A map describing the count of number of every reaction TextColumn get reactionCounts => text().nullable().map(MapConverter())(); @@ -48,16 +47,16 @@ class Messages extends Table { BoolColumn get showInChannel => boolean().nullable()(); /// If true the message is shadowed - BoolColumn get shadowed => boolean().nullable()(); + BoolColumn get shadowed => boolean().withDefault(const Constant(false))(); /// A used command name. TextColumn get command => text().nullable()(); /// The DateTime when the message was created. - DateTimeColumn get createdAt => dateTime()(); + DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)(); /// The DateTime when the message was updated last time. - DateTimeColumn get updatedAt => dateTime().nullable()(); + DateTimeColumn get updatedAt => dateTime().withDefault(currentDateAndTime)(); /// The DateTime when the message was deleted. DateTimeColumn get deletedAt => dateTime().nullable()(); diff --git a/packages/stream_chat_persistence/lib/src/entity/reactions.dart b/packages/stream_chat_persistence/lib/src/entity/reactions.dart index 53cbdd11..2b7d12b5 100644 --- a/packages/stream_chat_persistence/lib/src/entity/reactions.dart +++ b/packages/stream_chat_persistence/lib/src/entity/reactions.dart @@ -16,10 +16,10 @@ class Reactions extends Table { TextColumn get type => text()(); /// The DateTime on which the reaction is created - DateTimeColumn get createdAt => dateTime()(); + DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)(); /// The score of the reaction (ie. number of reactions sent) - IntColumn get score => integer().nullable()(); + IntColumn get score => integer().withDefault(const Constant(0))(); /// Reaction custom extraData TextColumn get extraData => text().nullable().map(MapConverter())(); diff --git a/packages/stream_chat_persistence/lib/src/entity/reads.dart b/packages/stream_chat_persistence/lib/src/entity/reads.dart index 7943ed9e..1f59b3c8 100644 --- a/packages/stream_chat_persistence/lib/src/entity/reads.dart +++ b/packages/stream_chat_persistence/lib/src/entity/reads.dart @@ -15,7 +15,7 @@ class Reads extends Table { text().customConstraint('REFERENCES channels(cid) ON DELETE CASCADE')(); /// Number of unread messages - IntColumn get unreadMessages => integer().nullable()(); + IntColumn get unreadMessages => integer().withDefault(const Constant(0))(); @override Set get primaryKey => { diff --git a/packages/stream_chat_persistence/lib/src/entity/users.dart b/packages/stream_chat_persistence/lib/src/entity/users.dart index 7ece9600..49fb4687 100644 --- a/packages/stream_chat_persistence/lib/src/entity/users.dart +++ b/packages/stream_chat_persistence/lib/src/entity/users.dart @@ -12,22 +12,22 @@ class Users extends Table { TextColumn get role => text().nullable()(); /// Date of user creation - DateTimeColumn get createdAt => dateTime().nullable()(); + DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)(); /// Date of last user update - DateTimeColumn get updatedAt => dateTime().nullable()(); + DateTimeColumn get updatedAt => dateTime().withDefault(currentDateAndTime)(); /// Date of last user connection DateTimeColumn get lastActive => dateTime().nullable()(); /// True if user is online - BoolColumn get online => boolean().nullable()(); + BoolColumn get online => boolean().withDefault(const Constant(false))(); /// True if user is banned from the chat - BoolColumn get banned => boolean().nullable()(); + BoolColumn get banned => boolean().withDefault(const Constant(false))(); /// Map of custom user extraData - TextColumn get extraData => text().nullable().map(MapConverter())(); + TextColumn get extraData => text().map(MapConverter())(); @override Set get primaryKey => {id}; diff --git a/packages/stream_chat_persistence/lib/src/mapper/channel_mapper.dart b/packages/stream_chat_persistence/lib/src/mapper/channel_mapper.dart index 65315519..71f5b708 100644 --- a/packages/stream_chat_persistence/lib/src/mapper/channel_mapper.dart +++ b/packages/stream_chat_persistence/lib/src/mapper/channel_mapper.dart @@ -4,8 +4,8 @@ import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; /// Useful mapping functions for [ChannelEntity] extension ChannelEntityX on ChannelEntity { /// Maps a [ChannelEntity] into [ChannelModel] - ChannelModel toChannelModel({User createdBy}) { - final config = ChannelConfig.fromJson(this.config ?? {}); + ChannelModel toChannelModel({User? createdBy}) { + final config = ChannelConfig.fromJson(this.config); return ChannelModel( id: id, config: config, @@ -17,18 +17,18 @@ extension ChannelEntityX on ChannelEntity { cid: cid, lastMessageAt: lastMessageAt, deletedAt: deletedAt, - extraData: extraData, + extraData: extraData ?? {}, createdBy: createdBy, ); } /// Maps a [ChannelEntity] into [ChannelState] ChannelState toChannelState({ - User createdBy, - List members, - List reads, - List messages, - List pinnedMessages, + User? createdBy, + List members = const [], + List reads = const [], + List messages = const [], + List pinnedMessages = const [], }) => ChannelState( members: members, @@ -46,7 +46,7 @@ extension ChannelModelX on ChannelModel { id: id, type: type, cid: cid, - config: config?.toJson(), + config: config.toJson(), frozen: frozen, lastMessageAt: lastMessageAt, createdAt: createdAt, diff --git a/packages/stream_chat_persistence/lib/src/mapper/event_mapper.dart b/packages/stream_chat_persistence/lib/src/mapper/event_mapper.dart index fc2a57ba..c5cc4198 100644 --- a/packages/stream_chat_persistence/lib/src/mapper/event_mapper.dart +++ b/packages/stream_chat_persistence/lib/src/mapper/event_mapper.dart @@ -5,7 +5,7 @@ import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; extension ConnectionEventX on ConnectionEventEntity { /// Maps a [ConnectionEventEntity] into [Event] Event toEvent() => Event( - me: ownUser != null ? OwnUser.fromJson(ownUser) : null, + me: ownUser != null ? OwnUser.fromJson(ownUser!) : null, totalUnreadCount: totalUnreadCount, unreadChannels: unreadChannels, ); diff --git a/packages/stream_chat_persistence/lib/src/mapper/member_mapper.dart b/packages/stream_chat_persistence/lib/src/mapper/member_mapper.dart index cbdcc297..414cab16 100644 --- a/packages/stream_chat_persistence/lib/src/mapper/member_mapper.dart +++ b/packages/stream_chat_persistence/lib/src/mapper/member_mapper.dart @@ -4,7 +4,7 @@ import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; /// Useful mapping functions for [MemberEntity] extension MemberEntityX on MemberEntity { /// Maps a [MemberEntity] into [Member] - Member toMember({User user}) => Member( + Member toMember({User? user}) => Member( user: user, userId: userId, banned: banned, @@ -22,8 +22,8 @@ extension MemberEntityX on MemberEntity { /// Useful mapping functions for [Member] extension MemberX on Member { /// Maps a [Member] into [MemberEntity] - MemberEntity toEntity({String cid}) => MemberEntity( - userId: user?.id, + MemberEntity toEntity({required String cid}) => MemberEntity( + userId: user!.id, banned: banned, shadowBanned: shadowBanned, channelCid: cid, diff --git a/packages/stream_chat_persistence/lib/src/mapper/message_mapper.dart b/packages/stream_chat_persistence/lib/src/mapper/message_mapper.dart index fdf93cc5..c97cc92d 100644 --- a/packages/stream_chat_persistence/lib/src/mapper/message_mapper.dart +++ b/packages/stream_chat_persistence/lib/src/mapper/message_mapper.dart @@ -7,22 +7,22 @@ import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; extension MessageEntityX on MessageEntity { /// Maps a [MessageEntity] into [Message] Message toMessage({ - User user, - User pinnedBy, - List latestReactions, - List ownReactions, - Message quotedMessage, + User? user, + User? pinnedBy, + List? latestReactions, + List? ownReactions, + Message? quotedMessage, }) => Message( shadowed: shadowed, latestReactions: latestReactions, ownReactions: ownReactions, - attachments: attachments?.map((it) { + attachments: attachments.map((it) { final json = jsonDecode(it); return Attachment.fromData(json); - })?.toList(), + }).toList(), createdAt: createdAt, - extraData: extraData, + extraData: extraData ?? {}, updatedAt: updatedAt, id: id, type: type, @@ -48,10 +48,9 @@ extension MessageEntityX on MessageEntity { /// Useful mapping functions for [Message] extension MessageX on Message { /// Maps a [Message] into [MessageEntity] - MessageEntity toEntity({String cid}) => MessageEntity( + MessageEntity toEntity({String? cid}) => MessageEntity( id: id, - attachments: - attachments?.map((it) => jsonEncode(it.toData()))?.toList(), + attachments: attachments.map((it) => jsonEncode(it.toData())).toList(), channelCid: cid, type: type, parentId: parentId, @@ -63,6 +62,7 @@ extension MessageX on Message { replyCount: replyCount, reactionScores: reactionScores, reactionCounts: reactionCounts, + mentionedUsers: mentionedUsers.map(jsonEncode).toList(), status: status, updatedAt: updatedAt, extraData: extraData, diff --git a/packages/stream_chat_persistence/lib/src/mapper/pinned_message_mapper.dart b/packages/stream_chat_persistence/lib/src/mapper/pinned_message_mapper.dart index ec2a0e91..1083e239 100644 --- a/packages/stream_chat_persistence/lib/src/mapper/pinned_message_mapper.dart +++ b/packages/stream_chat_persistence/lib/src/mapper/pinned_message_mapper.dart @@ -7,22 +7,22 @@ import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; extension PinnedMessageEntityX on PinnedMessageEntity { /// Maps a [PinnedMessageEntity] into [Message] Message toMessage({ - User user, - User pinnedBy, - List latestReactions, - List ownReactions, - Message quotedMessage, + User? user, + User? pinnedBy, + List? latestReactions, + List? ownReactions, + Message? quotedMessage, }) => Message( shadowed: shadowed, latestReactions: latestReactions, ownReactions: ownReactions, - attachments: attachments?.map((it) { + attachments: attachments.map((it) { final json = jsonDecode(it); return Attachment.fromData(json); - })?.toList(), + }).toList(), createdAt: createdAt, - extraData: extraData, + extraData: extraData ?? {}, updatedAt: updatedAt, id: id, type: type, @@ -48,10 +48,9 @@ extension PinnedMessageEntityX on PinnedMessageEntity { /// Useful mapping functions for [Message] extension PMessageX on Message { /// Maps a [Message] into [PinnedMessageEntity] - PinnedMessageEntity toPinnedEntity({String cid}) => PinnedMessageEntity( + PinnedMessageEntity toPinnedEntity({String? cid}) => PinnedMessageEntity( id: id, - attachments: - attachments?.map((it) => jsonEncode(it.toData()))?.toList(), + attachments: attachments.map((it) => jsonEncode(it.toData())).toList(), channelCid: cid, type: type, parentId: parentId, @@ -63,6 +62,7 @@ extension PMessageX on Message { replyCount: replyCount, reactionScores: reactionScores, reactionCounts: reactionCounts, + mentionedUsers: mentionedUsers.map(jsonEncode).toList(), status: status, updatedAt: updatedAt, extraData: extraData, diff --git a/packages/stream_chat_persistence/lib/src/mapper/reaction_mapper.dart b/packages/stream_chat_persistence/lib/src/mapper/reaction_mapper.dart index 20842307..003802b9 100644 --- a/packages/stream_chat_persistence/lib/src/mapper/reaction_mapper.dart +++ b/packages/stream_chat_persistence/lib/src/mapper/reaction_mapper.dart @@ -4,7 +4,7 @@ import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; /// Useful mapping functions for [ReactionEntity] extension ReactionEntityX on ReactionEntity { /// Maps a [ReactionEntity] into [Reaction] - Reaction toReaction({User user}) => Reaction( + Reaction toReaction({User? user}) => Reaction( extraData: extraData, type: type, createdAt: createdAt, @@ -22,8 +22,8 @@ extension ReactionX on Reaction { extraData: extraData, type: type, createdAt: createdAt, - userId: userId, - messageId: messageId, + userId: userId!, + messageId: messageId!, score: score, ); } diff --git a/packages/stream_chat_persistence/lib/src/mapper/read_mapper.dart b/packages/stream_chat_persistence/lib/src/mapper/read_mapper.dart index 9664030e..889776e2 100644 --- a/packages/stream_chat_persistence/lib/src/mapper/read_mapper.dart +++ b/packages/stream_chat_persistence/lib/src/mapper/read_mapper.dart @@ -4,7 +4,7 @@ import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; /// Useful mapping functions for [ReadEntity] extension ReadEntityX on ReadEntity { /// Maps a [ReadEntity] into [Read] - Read toRead({User user}) => Read( + Read toRead({required User user}) => Read( user: user, lastRead: lastRead, unreadMessages: unreadMessages, @@ -14,9 +14,9 @@ extension ReadEntityX on ReadEntity { /// Useful mapping functions for [Read] extension ReadX on Read { /// Maps a [Read] into [ReadEntity] - ReadEntity toEntity({String cid}) => ReadEntity( + ReadEntity toEntity({required String cid}) => ReadEntity( lastRead: lastRead, - userId: user?.id, + userId: user.id, channelCid: cid, unreadMessages: unreadMessages, ); diff --git a/packages/stream_chat_persistence/lib/src/stream_chat_persistence_client.dart b/packages/stream_chat_persistence/lib/src/stream_chat_persistence_client.dart index 2c9d28fb..20c4e327 100644 --- a/packages/stream_chat_persistence/lib/src/stream_chat_persistence_client.dart +++ b/packages/stream_chat_persistence/lib/src/stream_chat_persistence_client.dart @@ -1,3 +1,4 @@ +import 'package:flutter/foundation.dart'; import 'package:logging/logging.dart' show LogRecord; import 'package:meta/meta.dart'; import 'package:mutex/mutex.dart'; @@ -30,17 +31,15 @@ class StreamChatPersistenceClient extends ChatPersistenceClient { /// Connection mode on which the client will work ConnectionMode connectionMode = ConnectionMode.regular, Level logLevel = Level.WARNING, - LogHandlerFunction logHandlerFunction, - }) : assert(connectionMode != null, 'ConnectionMode cannot be null'), - assert(logLevel != null, 'LogLevel cannot be null'), - _connectionMode = connectionMode, + LogHandlerFunction? logHandlerFunction, + }) : _connectionMode = connectionMode, _logger = Logger.detached('💽')..level = logLevel { _logger.onRecord.listen(logHandlerFunction ?? _defaultLogHandler); } /// [MoorChatDatabase] instance used by this client. @visibleForTesting - MoorChatDatabase db; + MoorChatDatabase? db; final Logger _logger; final ConnectionMode _connectionMode; @@ -55,15 +54,20 @@ class StreamChatPersistenceClient extends ChatPersistenceClient { if (record.stackTrace != null) print(record.stackTrace); } - Future _readProtected(Future Function() f) async { - T ret; - await _mutex.protectRead(() async { + Future _readProtected(AsyncValueGetter func) => + _mutex.protectRead(func); + + bool get _debugIsConnected { + assert(() { if (db == null) { - return; + throw StateError(''' + $runtimeType hasn't been connected yet or used after `disconnect` + was called. Consider calling `connect` to create a connection. + '''); } - ret = await f(); - }); - return ret; + return true; + }(), ''); + return true; } MoorChatDatabase _defaultDatabaseProvider( @@ -75,7 +79,7 @@ class StreamChatPersistenceClient extends ChatPersistenceClient { @override Future connect( String userId, { - DatabaseProvider databaseProvider, // Used only for testing + DatabaseProvider? databaseProvider, // Used only for testing }) async { if (db != null) { throw Exception( @@ -88,239 +92,281 @@ class StreamChatPersistenceClient extends ChatPersistenceClient { } @override - Future getConnectionInfo() => _readProtected(() { - _logger.info('getConnectionInfo'); - return db.connectionEventDao.connectionEvent; - }); + Future getConnectionInfo() { + assert(_debugIsConnected, ''); + _logger.info('getConnectionInfo'); + return _readProtected(() => db!.connectionEventDao.connectionEvent); + } @override - Future updateConnectionInfo(Event event) => _readProtected(() { - _logger.info('updateConnectionInfo'); - return db.connectionEventDao.updateConnectionEvent(event); - }); + Future updateConnectionInfo(Event event) { + assert(_debugIsConnected, ''); + _logger.info('updateConnectionInfo'); + return _readProtected( + () => db!.connectionEventDao.updateConnectionEvent(event), + ); + } @override - Future updateLastSyncAt(DateTime lastSyncAt) => _readProtected(() { - _logger.info('updateLastSyncAt'); - return db.connectionEventDao.updateLastSyncAt(lastSyncAt); - }); + Future updateLastSyncAt(DateTime lastSyncAt) { + assert(_debugIsConnected, ''); + _logger.info('updateLastSyncAt'); + return _readProtected( + () => db!.connectionEventDao.updateLastSyncAt(lastSyncAt), + ); + } @override - Future getLastSyncAt() => _readProtected(() { - _logger.info('getLastSyncAt'); - return db.connectionEventDao.lastSyncAt; - }); + Future getLastSyncAt() { + assert(_debugIsConnected, ''); + _logger.info('getLastSyncAt'); + return _readProtected(() => db!.connectionEventDao.lastSyncAt); + } @override - Future deleteChannels(List cids) => _readProtected(() { - _logger.info('deleteChannels'); - return db.channelDao.deleteChannelByCids(cids); - }); + Future deleteChannels(List cids) { + assert(_debugIsConnected, ''); + _logger.info('deleteChannels'); + return _readProtected(() => db!.channelDao.deleteChannelByCids(cids)); + } @override - Future> getChannelCids() => _readProtected(() { - _logger.info('getChannelCids'); - return db.channelDao.cids; - }); + Future> getChannelCids() { + assert(_debugIsConnected, ''); + _logger.info('getChannelCids'); + return _readProtected(() => db!.channelDao.cids); + } @override - Future deleteMessageByIds(List messageIds) => - _readProtected(() { - _logger.info('deleteMessageByIds'); - return db.messageDao.deleteMessageByIds(messageIds); - }); + Future deleteMessageByIds(List messageIds) { + assert(_debugIsConnected, ''); + _logger.info('deleteMessageByIds'); + return _readProtected(() => db!.messageDao.deleteMessageByIds(messageIds)); + } @override - Future deletePinnedMessageByIds(List messageIds) => - _readProtected(() { - _logger.info('deletePinnedMessageByIds'); - return db.pinnedMessageDao.deleteMessageByIds(messageIds); - }); + Future deletePinnedMessageByIds(List messageIds) { + assert(_debugIsConnected, ''); + _logger.info('deletePinnedMessageByIds'); + return _readProtected( + () => db!.pinnedMessageDao.deleteMessageByIds(messageIds), + ); + } @override - Future deleteMessageByCids(List cids) => _readProtected(() { - _logger.info('deleteMessageByCids'); - return db.messageDao.deleteMessageByCids(cids); - }); + Future deleteMessageByCids(List cids) { + assert(_debugIsConnected, ''); + _logger.info('deleteMessageByCids'); + return _readProtected(() => db!.messageDao.deleteMessageByCids(cids)); + } @override - Future deletePinnedMessageByCids(List cids) => - _readProtected(() { - _logger.info('deletePinnedMessageByCids'); - return db.pinnedMessageDao.deleteMessageByCids(cids); - }); + Future deletePinnedMessageByCids(List cids) { + assert(_debugIsConnected, ''); + _logger.info('deletePinnedMessageByCids'); + return _readProtected(() => db!.pinnedMessageDao.deleteMessageByCids(cids)); + } @override - Future> getMembersByCid(String cid) => _readProtected(() { - _logger.info('getMembersByCid'); - return db.memberDao.getMembersByCid(cid); - }); + Future> getMembersByCid(String cid) { + assert(_debugIsConnected, ''); + _logger.info('getMembersByCid'); + return _readProtected(() => db!.memberDao.getMembersByCid(cid)); + } @override - Future getChannelByCid(String cid) => _readProtected(() { - _logger.info('getChannelByCid'); - return db.channelDao.getChannelByCid(cid); - }); + Future getChannelByCid(String cid) { + assert(_debugIsConnected, ''); + _logger.info('getChannelByCid'); + return _readProtected(() => db!.channelDao.getChannelByCid(cid)); + } @override Future> getMessagesByCid( String cid, { - PaginationParams messagePagination, - }) => - _readProtected(() { - _logger.info('getMessagesByCid'); - return db.messageDao.getMessagesByCid( - cid, - messagePagination: messagePagination, - ); - }); + PaginationParams? messagePagination, + }) { + assert(_debugIsConnected, ''); + _logger.info('getMessagesByCid'); + return _readProtected( + () => db!.messageDao.getMessagesByCid( + cid, + messagePagination: messagePagination, + ), + ); + } @override Future> getPinnedMessagesByCid( String cid, { - PaginationParams messagePagination, - }) => - _readProtected(() { - _logger.info('getPinnedMessagesByCid'); - return db.pinnedMessageDao.getMessagesByCid( - cid, - messagePagination: messagePagination, - ); - }); + PaginationParams? messagePagination, + }) { + assert(_debugIsConnected, ''); + _logger.info('getPinnedMessagesByCid'); + return _readProtected( + () => db!.pinnedMessageDao.getMessagesByCid( + cid, + messagePagination: messagePagination, + ), + ); + } @override - Future> getReadsByCid(String cid) => _readProtected(() { - _logger.info('getReadsByCid'); - return db.readDao.getReadsByCid(cid); - }); + Future> getReadsByCid(String cid) { + assert(_debugIsConnected, ''); + _logger.info('getReadsByCid'); + return _readProtected(() => db!.readDao.getReadsByCid(cid)); + } @override - Future>> getChannelThreads(String cid) async => - _readProtected(() async { - _logger.info('getChannelThreads'); - final messages = await db.messageDao.getThreadMessages(cid); - final messageByParentIdDictionary = >{}; - for (final message in messages) { - final parentId = message.parentId; - messageByParentIdDictionary[parentId] = [ - ...messageByParentIdDictionary[parentId] ?? [], - message - ]; - } - return messageByParentIdDictionary; - }); + Future>> getChannelThreads(String cid) { + assert(_debugIsConnected, ''); + _logger.info('getChannelThreads'); + return _readProtected(() async { + final messages = await db!.messageDao.getThreadMessages(cid); + final messageByParentIdDictionary = >{}; + for (final message in messages) { + final parentId = message.parentId!; + messageByParentIdDictionary[parentId] = [ + ...messageByParentIdDictionary[parentId] ?? [], + message + ]; + } + return messageByParentIdDictionary; + }); + } @override Future> getReplies( String parentId, { - PaginationParams options, - }) => - _readProtected(() async { - _logger.info('getReplies'); - return db.messageDao.getThreadMessagesByParentId( - parentId, - options: options, - ); - }); + PaginationParams? options, + }) { + assert(_debugIsConnected, ''); + _logger.info('getReplies'); + return _readProtected( + () => db!.messageDao.getThreadMessagesByParentId( + parentId, + options: options, + ), + ); + } @override Future> getChannelStates({ - Map filter, - List> sort = const [], - PaginationParams paginationParams, - }) async => - _readProtected(() async { - _logger.info('getChannelStates'); - final channels = await db.channelQueryDao.getChannels( + Filter? filter, + List>? sort, + PaginationParams? paginationParams, + }) { + assert(_debugIsConnected, ''); + _logger.info('getChannelStates'); + return _readProtected( + () async { + final channels = await db!.channelQueryDao.getChannels( filter: filter, sort: sort, paginationParams: paginationParams, ); return Future.wait(channels.map((e) => getChannelStateByCid(e.cid))); - }); + }, + ); + } @override Future updateChannelQueries( - Map filter, + Filter? filter, List cids, { bool clearQueryCache = false, - }) => - _readProtected(() async { - _logger.info('updateChannelQueries'); - return db.channelQueryDao.updateChannelQueries( - filter, - cids, - clearQueryCache: clearQueryCache, - ); - }); + }) { + assert(_debugIsConnected, ''); + _logger.info('updateChannelQueries'); + return _readProtected( + () => db!.channelQueryDao.updateChannelQueries( + filter, + cids, + clearQueryCache: clearQueryCache, + ), + ); + } @override - Future updateChannels(List channels) => - _readProtected(() async { - _logger.info('updateChannels'); - return db.channelDao.updateChannels(channels); - }); + Future updateChannels(List channels) { + assert(_debugIsConnected, ''); + _logger.info('updateChannels'); + return _readProtected(() => db!.channelDao.updateChannels(channels)); + } @override - Future updateMembers(String cid, List members) => - _readProtected(() async { - _logger.info('updateMembers'); - return db.memberDao.updateMembers(cid, members); - }); + Future updateMembers(String cid, List members) { + assert(_debugIsConnected, ''); + _logger.info('updateMembers'); + return _readProtected(() => db!.memberDao.updateMembers(cid, members)); + } @override - Future updateMessages(String cid, List messages) => - _readProtected(() async { - _logger.info('updateMessages'); - return db.messageDao.updateMessages(cid, messages); - }); + Future updateMessages(String cid, List messages) { + assert(_debugIsConnected, ''); + _logger.info('updateMessages'); + return _readProtected(() => db!.messageDao.updateMessages(cid, messages)); + } @override - Future updatePinnedMessages(String cid, List messages) => - _readProtected(() async { - _logger.info('updatePinnedMessages'); - return db.pinnedMessageDao.updateMessages(cid, messages); - }); + Future updatePinnedMessages(String cid, List messages) { + assert(_debugIsConnected, ''); + _logger.info('updatePinnedMessages'); + return _readProtected( + () => db!.pinnedMessageDao.updateMessages(cid, messages), + ); + } @override - Future updateReactions(List reactions) => - _readProtected(() async { - _logger.info('updateReactions'); - return db.reactionDao.updateReactions(reactions); - }); + Future updateReactions(List reactions) { + assert(_debugIsConnected, ''); + _logger.info('updateReactions'); + return _readProtected(() => db!.reactionDao.updateReactions(reactions)); + } @override - Future updateReads(String cid, List reads) => - _readProtected(() async { - _logger.info('updateReads'); - return db.readDao.updateReads(cid, reads); - }); + Future updateReads(String cid, List reads) { + assert(_debugIsConnected, ''); + _logger.info('updateReads'); + return _readProtected(() => db!.readDao.updateReads(cid, reads)); + } @override - Future updateUsers(List users) => _readProtected(() async { - _logger.info('updateUsers'); - return db.userDao.updateUsers(users); - }); + Future updateUsers(List users) { + assert(_debugIsConnected, ''); + _logger.info('updateUsers'); + return _readProtected(() => db!.userDao.updateUsers(users)); + } @override - Future deleteReactionsByMessageId(List messageIds) => - _readProtected(() async { - _logger.info('deleteReactionsByMessageId'); - return db.reactionDao.deleteReactionsByMessageIds(messageIds); - }); + Future deleteReactionsByMessageId(List messageIds) { + assert(_debugIsConnected, ''); + _logger.info('deleteReactionsByMessageId'); + return _readProtected( + () => db!.reactionDao.deleteReactionsByMessageIds(messageIds), + ); + } @override - Future deleteMembersByCids(List cids) => - _readProtected(() async { - _logger.info('deleteMembersByCids'); - return db.memberDao.deleteMemberByCids(cids); - }); + Future deleteMembersByCids(List cids) { + assert(_debugIsConnected, ''); + _logger.info('deleteMembersByCids'); + return _readProtected(() => db!.memberDao.deleteMemberByCids(cids)); + } @override - Future updateChannelStates(List channelStates) => - _readProtected(() async => db.transaction(() async { - await super.updateChannelStates(channelStates); - })); + Future updateChannelStates(List channelStates) { + assert(_debugIsConnected, ''); + _logger.info('updateChannelStates'); + return _readProtected( + () async => db!.transaction( + () async { + await super.updateChannelStates(channelStates); + }, + ), + ); + } @override Future disconnect({bool flush = false}) async => @@ -330,13 +376,9 @@ class StreamChatPersistenceClient extends ChatPersistenceClient { _logger.info('Disconnecting'); if (flush) { _logger.info('Flushing'); - await db.batch((batch) { - db.allTables.forEach((table) { - db.delete(table).go(); - }); - }); + await db!.flush(); } - await db.disconnect(); + await db!.disconnect(); db = null; } }); diff --git a/packages/stream_chat_persistence/pubspec.yaml b/packages/stream_chat_persistence/pubspec.yaml index 958a35b9..f3185581 100644 --- a/packages/stream_chat_persistence/pubspec.yaml +++ b/packages/stream_chat_persistence/pubspec.yaml @@ -1,31 +1,28 @@ name: stream_chat_persistence homepage: https://github.com/GetStream/stream-chat-flutter description: Official Stream Chat Persistence library. Build your own chat experience using Dart and Flutter. -version: 1.5.2 +version: 2.0.0-nullsafety.0 repository: https://github.com/GetStream/stream-chat-flutter issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues environment: - sdk: ">=2.7.0 <3.0.0" + sdk: ">=2.12.0 <3.0.0" dependencies: flutter: sdk: flutter - logging: ^0.11.4 - meta: ^1.2.4 - moor: ^3.4.0 - mutex: ^2.0.0 - path: ^1.7.0 - path_provider: ^1.6.27 - sqlite3_flutter_libs: ^0.4.0+1 + logging: ^1.0.1 + meta: ^1.3.0 + moor: ^4.2.1 + mutex: ^3.0.0 + path: ^1.8.0 + path_provider: ^2.0.1 + sqlite3_flutter_libs: ^0.4.1 stream_chat: ^1.5.3 -dependency_overrides: - stream_chat: - path: ../stream_chat - dev_dependencies: - build_runner: ^1.11.0 - mockito: ^4.1.3 - moor_generator: ^3.4.1 - test: ^1.15.7 + build_runner: ^2.0.1 + mocktail: ^0.1.1 + moor_generator: ^4.2.1 + pedantic: ^1.11.0 + test: ^1.16.8 diff --git a/packages/stream_chat_persistence/test/mock_chat_database.dart b/packages/stream_chat_persistence/test/mock_chat_database.dart index a60bd39b..b421fad1 100644 --- a/packages/stream_chat_persistence/test/mock_chat_database.dart +++ b/packages/stream_chat_persistence/test/mock_chat_database.dart @@ -1,55 +1,61 @@ -import 'package:mockito/mockito.dart'; +import 'package:mocktail/mocktail.dart'; import 'package:stream_chat_persistence/src/dao/dao.dart'; import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; class MockChatDatabase extends Mock implements MoorChatDatabase { - UserDao _userDao; + UserDao? _userDao; @override UserDao get userDao => _userDao ??= MockUserDao(); - ChannelDao _channelDao; + ChannelDao? _channelDao; @override ChannelDao get channelDao => _channelDao ??= MockChannelDao(); - MessageDao _messageDao; + MessageDao? _messageDao; @override MessageDao get messageDao => _messageDao ??= MockMessageDao(); - PinnedMessageDao _pinnedMessageDao; + PinnedMessageDao? _pinnedMessageDao; @override PinnedMessageDao get pinnedMessageDao => _pinnedMessageDao ??= MockPinnedMessageDao(); - MemberDao _memberDao; + MemberDao? _memberDao; @override MemberDao get memberDao => _memberDao ??= MockMemberDao(); - ReactionDao _reactionDao; + ReactionDao? _reactionDao; @override ReactionDao get reactionDao => _reactionDao ??= MockReactionDao(); - ReadDao _readDao; + ReadDao? _readDao; @override ReadDao get readDao => _readDao ??= MockReadDao(); - ChannelQueryDao _channelQueryDao; + ChannelQueryDao? _channelQueryDao; @override ChannelQueryDao get channelQueryDao => _channelQueryDao ??= MockChannelQueryDao(); - ConnectionEventDao _connectionEventDao; + ConnectionEventDao? _connectionEventDao; @override ConnectionEventDao get connectionEventDao => _connectionEventDao ??= MockConnectionEventDao(); + + @override + Future flush() => Future.value(); + + @override + Future disconnect() => Future.value(); } class MockUserDao extends Mock implements UserDao {} diff --git a/packages/stream_chat_persistence/test/src/converter/list_coverter_test.dart b/packages/stream_chat_persistence/test/src/converter/list_coverter_test.dart index a5bedd40..73ce2452 100644 --- a/packages/stream_chat_persistence/test/src/converter/list_coverter_test.dart +++ b/packages/stream_chat_persistence/test/src/converter/list_coverter_test.dart @@ -32,7 +32,7 @@ void main() { test('should return list of String if json data list is provided', () { final data = ['data1', 'data2', 'data3']; final res = listConverter.mapToDart(jsonEncode(data)); - expect(res.length, data.length); + expect(res!.length, data.length); }); }); diff --git a/packages/stream_chat_persistence/test/src/dao/channel_dao_test.dart b/packages/stream_chat_persistence/test/src/dao/channel_dao_test.dart index 47c655a8..d6ac8123 100644 --- a/packages/stream_chat_persistence/test/src/dao/channel_dao_test.dart +++ b/packages/stream_chat_persistence/test/src/dao/channel_dao_test.dart @@ -4,8 +4,8 @@ import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; import 'package:test/test.dart'; void main() { - ChannelDao channelDao; - MoorChatDatabase database; + late ChannelDao channelDao; + late MoorChatDatabase database; setUp(() { database = MoorChatDatabase.testable('testUserId'); @@ -32,7 +32,8 @@ void main() { // Should match the dummy channel final updatedChannel = await channelDao.getChannelByCid(cid); - expect(updatedChannel.id, id); + expect(updatedChannel, isNotNull); + expect(updatedChannel!.id, id); expect(updatedChannel.cid, cid); expect(updatedChannel.type, type); }); @@ -53,7 +54,8 @@ void main() { // Should match the dummy channel final updatedChannel = await channelDao.getChannelByCid(cid); - expect(updatedChannel.id, id); + expect(updatedChannel, isNotNull); + expect(updatedChannel!.id, id); expect(updatedChannel.cid, cid); expect(updatedChannel.type, type); @@ -108,7 +110,8 @@ void main() { // Should match the dummy channel final updatedChannel = await channelDao.getChannelByCid(cid); - expect(updatedChannel.id, id); + expect(updatedChannel, isNotNull); + expect(updatedChannel!.id, id); expect(updatedChannel.cid, cid); expect(updatedChannel.type, type); @@ -119,7 +122,8 @@ void main() { // Should match the new channel final newUpdatedChannel = await channelDao.getChannelByCid(cid); - expect(newUpdatedChannel.id, id); + expect(newUpdatedChannel, isNotNull); + expect(newUpdatedChannel!.id, id); expect(newUpdatedChannel.cid, cid); expect(newUpdatedChannel.type, newType); }); diff --git a/packages/stream_chat_persistence/test/src/dao/channel_query_dao_test.dart b/packages/stream_chat_persistence/test/src/dao/channel_query_dao_test.dart index 4566ca94..9b5e3470 100644 --- a/packages/stream_chat_persistence/test/src/dao/channel_query_dao_test.dart +++ b/packages/stream_chat_persistence/test/src/dao/channel_query_dao_test.dart @@ -8,8 +8,8 @@ import 'package:test/test.dart'; import '../utils/date_matcher.dart'; void main() { - MoorChatDatabase database; - ChannelQueryDao channelQueryDao; + late MoorChatDatabase database; + late ChannelQueryDao channelQueryDao; setUp(() { database = MoorChatDatabase.testable('testUserId'); @@ -17,11 +17,7 @@ void main() { }); test('updateChannelQueries', () async { - const filter = { - 'members': { - r'$in': ['testUserId'], - }, - }; + final filter = Filter.in_('members', const ['testUserId']); const cids = ['testCid1', 'testCid2', 'testCid3']; @@ -36,11 +32,7 @@ void main() { }); test('clear queryCache before updateChannelQueries', () async { - const filter = { - 'members': { - r'$in': ['testUserId'], - }, - }; + final filter = Filter.in_('members', const ['testUserId']); const cids = ['testCid1', 'testCid2', 'testCid3']; @@ -59,11 +51,7 @@ void main() { }); test('getCachedChannelCids', () async { - const filter = { - 'members': { - r'$in': ['testUserId'], - }, - }; + final filter = Filter.in_('members', const ['testUserId']); const cids = ['testCid1', 'testCid2', 'testCid3']; @@ -78,7 +66,7 @@ void main() { }); Future> _insertTestDataForGetChannel( - Map filter, { + Filter filter, { int count = 3, }) async { final now = DateTime.now(); @@ -110,11 +98,7 @@ void main() { } group('getChannels', () { - const filter = { - 'members': { - r'$in': ['testUserId'], - }, - }; + final filter = Filter.in_('members', const ['testUserId']); test('should return empty list of channels', () async { final channels = await channelQueryDao.getChannels(filter: filter); @@ -147,7 +131,7 @@ void main() { // Should match lastMessageAt date expect( updatedChannel.lastMessageAt, - isSameDateAs(insertedChannel.lastMessageAt), + isSameDateAs(insertedChannel.lastMessageAt!), ); } }); @@ -160,10 +144,7 @@ void main() { const pagination = PaginationParams(offset: offset, limit: limit); // Inserting test data for get channels - final insertedChannels = await _insertTestDataForGetChannel( - filter, - count: 30, - ); + await _insertTestDataForGetChannel(filter, count: 30); // Should match with the inserted channels final updatedChannels = await channelQueryDao.getChannels( @@ -187,7 +168,12 @@ void main() { // Should match with the inserted channels final updatedChannels = await channelQueryDao.getChannels( filter: filter, - sort: [SortOption('member_count', comparator: sortComparator)], + sort: [ + SortOption( + 'member_count', + comparator: sortComparator, + ) + ], ); expect(updatedChannels.length, insertedChannels.length); @@ -210,7 +196,7 @@ void main() { // Should match lastMessageAt date expect( updatedChannel.lastMessageAt, - isSameDateAs(insertedChannel.lastMessageAt), + isSameDateAs(insertedChannel.lastMessageAt!), ); } }); @@ -261,7 +247,7 @@ void main() { // Should match lastMessageAt date expect( updatedChannel.lastMessageAt, - isSameDateAs(insertedChannel.lastMessageAt), + isSameDateAs(insertedChannel.lastMessageAt!), ); } }); diff --git a/packages/stream_chat_persistence/test/src/dao/connection_event_dao_test.dart b/packages/stream_chat_persistence/test/src/dao/connection_event_dao_test.dart index 2969751b..536fe3bc 100644 --- a/packages/stream_chat_persistence/test/src/dao/connection_event_dao_test.dart +++ b/packages/stream_chat_persistence/test/src/dao/connection_event_dao_test.dart @@ -6,8 +6,8 @@ import 'package:test/test.dart'; import '../utils/date_matcher.dart'; void main() { - ConnectionEventDao eventDao; - MoorChatDatabase database; + late ConnectionEventDao eventDao; + late MoorChatDatabase database; setUp(() { database = MoorChatDatabase.testable('testUserId'); @@ -30,7 +30,8 @@ void main() { // Should match the added event final updatedEvent = await eventDao.connectionEvent; - expect(updatedEvent.me.id, newEvent.me.id); + expect(updatedEvent, isNotNull); + expect(updatedEvent!.me!.id, newEvent.me!.id); expect(updatedEvent.totalUnreadCount, newEvent.totalUnreadCount); expect(updatedEvent.unreadChannels, newEvent.unreadChannels); }); @@ -70,7 +71,8 @@ void main() { // Should match the previously added event final fetchedEvent = await eventDao.connectionEvent; - expect(fetchedEvent.me.id, event.me.id); + expect(fetchedEvent, isNotNull); + expect(fetchedEvent!.me!.id, event.me!.id); expect(fetchedEvent.totalUnreadCount, event.totalUnreadCount); expect(fetchedEvent.unreadChannels, event.unreadChannels); @@ -80,7 +82,8 @@ void main() { // Should match the updated event final fetchedNewEvent = await eventDao.connectionEvent; - expect(fetchedNewEvent.me.id, event.me.id); + expect(fetchedNewEvent, isNotNull); + expect(fetchedNewEvent!.me!.id, event.me!.id); expect(fetchedNewEvent.totalUnreadCount, event.totalUnreadCount); expect(fetchedNewEvent.unreadChannels, newEvent.unreadChannels); }); diff --git a/packages/stream_chat_persistence/test/src/dao/member_dao_test.dart b/packages/stream_chat_persistence/test/src/dao/member_dao_test.dart index dbb8c73c..6654de4c 100644 --- a/packages/stream_chat_persistence/test/src/dao/member_dao_test.dart +++ b/packages/stream_chat_persistence/test/src/dao/member_dao_test.dart @@ -8,8 +8,8 @@ import 'package:test/test.dart'; import '../utils/date_matcher.dart'; void main() { - MemberDao memberDao; - MoorChatDatabase database; + late MemberDao memberDao; + late MoorChatDatabase database; setUp(() { database = MoorChatDatabase.testable('testUserId'); @@ -53,7 +53,7 @@ void main() { for (var i = 0; i < fetchedMembers.length; i++) { final member = memberList[i]; final fetchedMember = fetchedMembers[i]; - expect(fetchedMember.user.id, member.user.id); + expect(fetchedMember.user!.id, member.user!.id); expect(fetchedMember.banned, member.banned); expect(fetchedMember.shadowBanned, member.shadowBanned); expect(fetchedMember.createdAt, isSameDateAs(member.createdAt)); @@ -63,7 +63,7 @@ void main() { expect(fetchedMember.updatedAt, isSameDateAs(member.updatedAt)); expect( fetchedMember.inviteAcceptedAt, - isSameDateAs(member.inviteAcceptedAt), + isSameDateAs(member.inviteAcceptedAt!), ); } }); @@ -80,7 +80,7 @@ void main() { for (var i = 0; i < fetchedMembers.length; i++) { final member = memberList[i]; final fetchedMember = fetchedMembers[i]; - expect(fetchedMember.user.id, member.user.id); + expect(fetchedMember.user!.id, member.user!.id); expect(fetchedMember.banned, member.banned); expect(fetchedMember.shadowBanned, member.shadowBanned); expect(fetchedMember.createdAt, isSameDateAs(member.createdAt)); @@ -90,7 +90,7 @@ void main() { expect(fetchedMember.updatedAt, isSameDateAs(member.updatedAt)); expect( fetchedMember.inviteAcceptedAt, - isSameDateAs(member.inviteAcceptedAt), + isSameDateAs(member.inviteAcceptedAt!), ); } @@ -118,13 +118,13 @@ void main() { expect(newFetchedMembers.length, fetchedMembers.length + 1); expect( newFetchedMembers - .firstWhere((it) => it.user.id == copyMember.user.id) + .firstWhere((it) => it.user!.id == copyMember.user!.id) .banned, true, ); expect( newFetchedMembers - .where((it) => it.user.id == newMember.user.id) + .where((it) => it.user!.id == newMember.user!.id) .isNotEmpty, true, ); diff --git a/packages/stream_chat_persistence/test/src/dao/message_dao_test.dart b/packages/stream_chat_persistence/test/src/dao/message_dao_test.dart index f2440ae1..0d986590 100644 --- a/packages/stream_chat_persistence/test/src/dao/message_dao_test.dart +++ b/packages/stream_chat_persistence/test/src/dao/message_dao_test.dart @@ -6,8 +6,8 @@ import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; import 'package:test/test.dart'; void main() { - MessageDao messageDao; - MoorChatDatabase database; + late MessageDao messageDao; + late MoorChatDatabase database; setUp(() { database = MoorChatDatabase.testable('testUserId'); @@ -32,7 +32,7 @@ void main() { shadowed: math.Random().nextBool(), replyCount: index, updatedAt: DateTime.now(), - extraData: {'extra_test_field': 'extraTestData'}, + extraData: const {'extra_test_field': 'extraTestData'}, text: 'Dummy text #$index', pinned: math.Random().nextBool(), pinnedAt: DateTime.now(), @@ -49,7 +49,7 @@ void main() { shadowed: math.Random().nextBool(), replyCount: index, updatedAt: DateTime.now(), - extraData: {'extra_test_field': 'extraTestData'}, + extraData: const {'extra_test_field': 'extraTestData'}, text: 'Dummy text #$index', quotedMessageId: messages[index].id, pinned: math.Random().nextBool(), @@ -69,7 +69,7 @@ void main() { shadowed: math.Random().nextBool(), replyCount: index, updatedAt: DateTime.now(), - extraData: {'extra_test_field': 'extraTestData'}, + extraData: const {'extra_test_field': 'extraTestData'}, text: 'Dummy text #$index', pinned: math.Random().nextBool(), pinnedAt: DateTime.now(), @@ -168,7 +168,8 @@ void main() { // Fetched message id should match the inserted message id final fetchedMessage = await messageDao.getMessageById(id); - expect(fetchedMessage.id, insertedMessages.first.id); + expect(fetchedMessage, isNotNull); + expect(fetchedMessage!.id, insertedMessages.first.id); }); test('getThreadMessages', () async { @@ -332,7 +333,7 @@ void main() { showInChannel: math.Random().nextBool(), replyCount: 4, updatedAt: DateTime.now(), - extraData: {'extra_test_field': 'extraTestData'}, + extraData: const {'extra_test_field': 'extraTestData'}, text: 'Dummy text #4', pinned: math.Random().nextBool(), pinnedAt: DateTime.now(), diff --git a/packages/stream_chat_persistence/test/src/dao/pinned_message_dao_test.dart b/packages/stream_chat_persistence/test/src/dao/pinned_message_dao_test.dart index de7c8976..33bf96b1 100644 --- a/packages/stream_chat_persistence/test/src/dao/pinned_message_dao_test.dart +++ b/packages/stream_chat_persistence/test/src/dao/pinned_message_dao_test.dart @@ -6,8 +6,8 @@ import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; import 'package:test/test.dart'; void main() { - PinnedMessageDao pinnedMessageDao; - MoorChatDatabase database; + late PinnedMessageDao pinnedMessageDao; + late MoorChatDatabase database; setUp(() { database = MoorChatDatabase.testable('testUserId'); @@ -32,7 +32,7 @@ void main() { shadowed: math.Random().nextBool(), replyCount: index, updatedAt: DateTime.now(), - extraData: {'extra_test_field': 'extraTestData'}, + extraData: const {'extra_test_field': 'extraTestData'}, text: 'Dummy text #$index', pinned: math.Random().nextBool(), pinnedAt: DateTime.now(), @@ -49,7 +49,7 @@ void main() { shadowed: math.Random().nextBool(), replyCount: index, updatedAt: DateTime.now(), - extraData: {'extra_test_field': 'extraTestData'}, + extraData: const {'extra_test_field': 'extraTestData'}, text: 'Dummy text #$index', quotedMessageId: messages[index].id, pinned: math.Random().nextBool(), @@ -69,7 +69,7 @@ void main() { shadowed: math.Random().nextBool(), replyCount: index, updatedAt: DateTime.now(), - extraData: {'extra_test_field': 'extraTestData'}, + extraData: const {'extra_test_field': 'extraTestData'}, text: 'Dummy text #$index', pinned: math.Random().nextBool(), pinnedAt: DateTime.now(), @@ -168,7 +168,8 @@ void main() { // Fetched message id should match the inserted message id final fetchedMessage = await pinnedMessageDao.getMessageById(id); - expect(fetchedMessage.id, insertedMessages.first.id); + expect(fetchedMessage, isNotNull); + expect(fetchedMessage!.id, insertedMessages.first.id); }); test('getThreadMessages', () async { @@ -333,7 +334,7 @@ void main() { showInChannel: math.Random().nextBool(), replyCount: 4, updatedAt: DateTime.now(), - extraData: {'extra_test_field': 'extraTestData'}, + extraData: const {'extra_test_field': 'extraTestData'}, text: 'Dummy text #4', pinned: math.Random().nextBool(), pinnedAt: DateTime.now(), diff --git a/packages/stream_chat_persistence/test/src/dao/reaction_dao_test.dart b/packages/stream_chat_persistence/test/src/dao/reaction_dao_test.dart index 94f8ebdd..73ff63fe 100644 --- a/packages/stream_chat_persistence/test/src/dao/reaction_dao_test.dart +++ b/packages/stream_chat_persistence/test/src/dao/reaction_dao_test.dart @@ -6,8 +6,8 @@ import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; import 'package:test/test.dart'; void main() { - ReactionDao reactionDao; - MoorChatDatabase database; + late ReactionDao reactionDao; + late MoorChatDatabase database; setUp(() { database = MoorChatDatabase.testable('testUserId'); @@ -16,7 +16,7 @@ void main() { Future> _prepareReactionData( String messageId, { - String userId, + String? userId, int count = 3, }) async { final users = List.generate(count, (index) => User(id: 'testUserId$index')); @@ -29,7 +29,7 @@ void main() { showInChannel: math.Random().nextBool(), replyCount: 3, updatedAt: DateTime.now(), - extraData: {'extra_test_field': 'extraTestData'}, + extraData: const {'extra_test_field': 'extraTestData'}, text: 'Dummy text', pinned: math.Random().nextBool(), pinnedAt: DateTime.now(), diff --git a/packages/stream_chat_persistence/test/src/dao/read_dao_test.dart b/packages/stream_chat_persistence/test/src/dao/read_dao_test.dart index 52207f68..77b7a7f9 100644 --- a/packages/stream_chat_persistence/test/src/dao/read_dao_test.dart +++ b/packages/stream_chat_persistence/test/src/dao/read_dao_test.dart @@ -6,8 +6,8 @@ import 'package:test/test.dart'; import '../utils/date_matcher.dart'; void main() { - ReadDao readDao; - MoorChatDatabase database; + late ReadDao readDao; + late MoorChatDatabase database; setUp(() { database = MoorChatDatabase.testable('testUserId'); diff --git a/packages/stream_chat_persistence/test/src/dao/user_dao_test.dart b/packages/stream_chat_persistence/test/src/dao/user_dao_test.dart index 8c5898a0..5332a298 100644 --- a/packages/stream_chat_persistence/test/src/dao/user_dao_test.dart +++ b/packages/stream_chat_persistence/test/src/dao/user_dao_test.dart @@ -6,8 +6,8 @@ import 'package:test/test.dart'; import 'package:stream_chat/stream_chat.dart'; void main() { - UserDao userDao; - MoorChatDatabase database; + late UserDao userDao; + late MoorChatDatabase database; setUp(() { database = MoorChatDatabase.testable('testUserId'); diff --git a/packages/stream_chat_persistence/test/src/mapper/channel_mapper_test.dart b/packages/stream_chat_persistence/test/src/mapper/channel_mapper_test.dart index dc02ee4b..93789cc7 100644 --- a/packages/stream_chat_persistence/test/src/mapper/channel_mapper_test.dart +++ b/packages/stream_chat_persistence/test/src/mapper/channel_mapper_test.dart @@ -34,15 +34,21 @@ void main() { expect(channelModel.updatedAt, isSameDateAs(entity.updatedAt)); expect(channelModel.memberCount, entity.memberCount); expect(channelModel.cid, entity.cid); - expect(channelModel.lastMessageAt, isSameDateAs(entity.lastMessageAt)); - expect(channelModel.deletedAt, isSameDateAs(entity.deletedAt)); + expect(channelModel.lastMessageAt, isSameDateAs(entity.lastMessageAt!)); + expect(channelModel.deletedAt, isSameDateAs(entity.deletedAt!)); expect(channelModel.extraData, entity.extraData); - expect(channelModel.createdBy.id, entity.createdById); + expect(channelModel.createdBy!.id, entity.createdById); }); test('toChannelState should map entity into ChannelState ', () { final members = List.generate(3, (index) => Member()); - final reads = List.generate(3, (index) => Read()); + final reads = List.generate( + 3, + (index) => Read( + user: User(id: 'testUserId$index'), + lastRead: DateTime.now(), + ), + ); final messages = List.generate(3, (index) => Message()); final channelState = entity.toChannelState( @@ -59,7 +65,7 @@ void main() { expect(channelState.messages.length, messages.length); expect(channelState.pinnedMessages.length, messages.length); - final channelModel = channelState.channel; + final channelModel = channelState.channel!; expect(channelModel.id, entity.id); expect(channelModel.config.toJson()['max_message_length'], 33); expect(channelModel.frozen, entity.frozen); @@ -67,10 +73,10 @@ void main() { expect(channelModel.updatedAt, isSameDateAs(entity.updatedAt)); expect(channelModel.memberCount, entity.memberCount); expect(channelModel.cid, entity.cid); - expect(channelModel.lastMessageAt, isSameDateAs(entity.lastMessageAt)); - expect(channelModel.deletedAt, isSameDateAs(entity.deletedAt)); + expect(channelModel.lastMessageAt, isSameDateAs(entity.lastMessageAt!)); + expect(channelModel.deletedAt, isSameDateAs(entity.deletedAt!)); expect(channelModel.extraData, entity.extraData); - expect(channelModel.createdBy.id, entity.createdById); + expect(channelModel.createdBy!.id, entity.createdById); }); }); @@ -103,9 +109,9 @@ void main() { expect(channelEntity.updatedAt, isSameDateAs(model.updatedAt)); expect(channelEntity.memberCount, model.memberCount); expect(channelEntity.cid, model.cid); - expect(channelEntity.lastMessageAt, isSameDateAs(model.lastMessageAt)); - expect(channelEntity.deletedAt, isSameDateAs(model.deletedAt)); + expect(channelEntity.lastMessageAt, isSameDateAs(model.lastMessageAt!)); + expect(channelEntity.deletedAt, isSameDateAs(model.deletedAt!)); expect(channelEntity.extraData, model.extraData); - expect(channelEntity.createdById, model.createdBy.id); + expect(channelEntity.createdById, model.createdBy!.id); }); } diff --git a/packages/stream_chat_persistence/test/src/mapper/event_mapper_test.dart b/packages/stream_chat_persistence/test/src/mapper/event_mapper_test.dart index 240ce1a7..cb977aa5 100644 --- a/packages/stream_chat_persistence/test/src/mapper/event_mapper_test.dart +++ b/packages/stream_chat_persistence/test/src/mapper/event_mapper_test.dart @@ -16,7 +16,7 @@ void main() { ); final event = entity.toEvent(); expect(event, isA()); - expect(event.me.id, ownUser.id); + expect(event.me!.id, ownUser.id); expect(event.totalUnreadCount, entity.totalUnreadCount); expect(event.unreadChannels, entity.unreadChannels); }); diff --git a/packages/stream_chat_persistence/test/src/mapper/member_mapper_test.dart b/packages/stream_chat_persistence/test/src/mapper/member_mapper_test.dart index 0f7d4f11..feca753b 100644 --- a/packages/stream_chat_persistence/test/src/mapper/member_mapper_test.dart +++ b/packages/stream_chat_persistence/test/src/mapper/member_mapper_test.dart @@ -25,12 +25,12 @@ void main() { ); final member = entity.toMember(user: user); expect(member, isA()); - expect(member.user.id, entity.userId); + expect(member.user!.id, entity.userId); expect(member.createdAt, isSameDateAs(entity.createdAt)); expect(member.updatedAt, isSameDateAs(entity.updatedAt)); expect(member.role, entity.role); - expect(member.inviteAcceptedAt, isSameDateAs(entity.inviteAcceptedAt)); - expect(member.inviteRejectedAt, isSameDateAs(entity.inviteRejectedAt)); + expect(member.inviteAcceptedAt, isSameDateAs(entity.inviteAcceptedAt!)); + expect(member.inviteRejectedAt, isSameDateAs(entity.inviteRejectedAt!)); expect(member.invited, entity.invited); expect(member.banned, entity.banned); expect(member.shadowBanned, entity.shadowBanned); @@ -55,12 +55,12 @@ void main() { final entity = member.toEntity(cid: cid); expect(entity, isA()); expect(entity.channelCid, cid); - expect(entity.userId, member.user.id); + expect(entity.userId, member.user!.id); expect(entity.createdAt, isSameDateAs(member.createdAt)); expect(entity.updatedAt, isSameDateAs(member.updatedAt)); expect(entity.role, member.role); - expect(entity.inviteAcceptedAt, isSameDateAs(member.inviteAcceptedAt)); - expect(entity.inviteRejectedAt, isSameDateAs(member.inviteRejectedAt)); + expect(entity.inviteAcceptedAt, isSameDateAs(member.inviteAcceptedAt!)); + expect(entity.inviteRejectedAt, isSameDateAs(member.inviteRejectedAt!)); expect(entity.invited, member.invited); expect(entity.banned, member.banned); expect(entity.shadowBanned, member.shadowBanned); diff --git a/packages/stream_chat_persistence/test/src/mapper/message_mapper_test.dart b/packages/stream_chat_persistence/test/src/mapper/message_mapper_test.dart index 21a05da8..f73102f8 100644 --- a/packages/stream_chat_persistence/test/src/mapper/message_mapper_test.dart +++ b/packages/stream_chat_persistence/test/src/mapper/message_mapper_test.dart @@ -32,7 +32,7 @@ void main() { ); final entity = MessageEntity( id: 'testMessageId', - attachments: attachments?.map((it) => jsonEncode(it.toData()))?.toList(), + attachments: attachments.map((it) => jsonEncode(it.toData())).toList(), channelCid: 'testCid', type: 'testType', parentId: 'testParentId', @@ -46,8 +46,9 @@ void main() { reactionCounts: reactions.fold( {}, (prev, curr) => - prev..update(curr.type, (value) => value + 1, ifAbsent: () => 1), + prev?..update(curr.type, (value) => value + 1, ifAbsent: () => 1), ), + mentionedUsers: const [], status: MessageSendingStatus.sent, updatedAt: DateTime.now(), extraData: {'extra_test_data': 'extraData'}, @@ -82,13 +83,13 @@ void main() { expect(message.status, entity.status); expect(message.updatedAt, isSameDateAs(entity.updatedAt)); expect(message.extraData, entity.extraData); - expect(message.user.id, entity.userId); - expect(message.deletedAt, isSameDateAs(entity.deletedAt)); + expect(message.user!.id, entity.userId); + expect(message.deletedAt, isSameDateAs(entity.deletedAt!)); expect(message.text, entity.messageText); expect(message.pinned, entity.pinned); - expect(message.pinExpires, isSameDateAs(entity.pinExpires)); - expect(message.pinnedAt, isSameDateAs(entity.pinnedAt)); - expect(message.pinnedBy.id, entity.pinnedByUserId); + expect(message.pinExpires, isSameDateAs(entity.pinExpires!)); + expect(message.pinnedAt, isSameDateAs(entity.pinnedAt!)); + expect(message.pinnedBy!.id, entity.pinnedByUserId); expect(message.reactionCounts, entity.reactionCounts); expect(message.reactionScores, entity.reactionScores); for (var i = 0; i < message.attachments.length; i++) { @@ -138,11 +139,11 @@ void main() { reactionCounts: reactions.fold( {}, (prev, curr) => - prev..update(curr.type, (value) => value + 1, ifAbsent: () => 1), + prev?..update(curr.type, (value) => value + 1, ifAbsent: () => 1), ), status: MessageSendingStatus.sending, updatedAt: DateTime.now(), - extraData: {'extra_test_data': 'extraData'}, + extraData: const {'extra_test_data': 'extraData'}, user: user, deletedAt: DateTime.now(), text: 'dummy text', @@ -167,18 +168,18 @@ void main() { expect(entity.status, message.status); expect(entity.updatedAt, isSameDateAs(message.updatedAt)); expect(entity.extraData, message.extraData); - expect(entity.userId, message.user.id); - expect(entity.deletedAt, isSameDateAs(message.deletedAt)); + expect(entity.userId, message.user!.id); + expect(entity.deletedAt, isSameDateAs(message.deletedAt!)); expect(entity.messageText, message.text); expect(entity.pinned, message.pinned); - expect(entity.pinExpires, isSameDateAs(message.pinExpires)); - expect(entity.pinnedAt, isSameDateAs(message.pinnedAt)); - expect(entity.pinnedByUserId, message.pinnedBy.id); + expect(entity.pinExpires, isSameDateAs(message.pinExpires!)); + expect(entity.pinnedAt, isSameDateAs(message.pinnedAt!)); + expect(entity.pinnedByUserId, message.pinnedBy!.id); expect(entity.reactionCounts, message.reactionCounts); expect(entity.reactionScores, message.reactionScores); expect( entity.attachments, - message.attachments?.map((it) => jsonEncode(it.toData()))?.toList(), + message.attachments.map((it) => jsonEncode(it.toData())).toList(), ); }); } diff --git a/packages/stream_chat_persistence/test/src/mapper/pinned_message_mapper_test.dart b/packages/stream_chat_persistence/test/src/mapper/pinned_message_mapper_test.dart index 0aadf08a..8b5f4db4 100644 --- a/packages/stream_chat_persistence/test/src/mapper/pinned_message_mapper_test.dart +++ b/packages/stream_chat_persistence/test/src/mapper/pinned_message_mapper_test.dart @@ -32,7 +32,7 @@ void main() { ); final entity = PinnedMessageEntity( id: 'testMessageId', - attachments: attachments?.map((it) => jsonEncode(it.toData()))?.toList(), + attachments: attachments.map((it) => jsonEncode(it.toData())).toList(), channelCid: 'testCid', type: 'testType', parentId: 'testParentId', @@ -46,8 +46,9 @@ void main() { reactionCounts: reactions.fold( {}, (prev, curr) => - prev..update(curr.type, (value) => value + 1, ifAbsent: () => 1), + prev?..update(curr.type, (value) => value + 1, ifAbsent: () => 1), ), + mentionedUsers: [], status: MessageSendingStatus.sent, updatedAt: DateTime.now(), extraData: {'extra_test_data': 'extraData'}, @@ -82,13 +83,13 @@ void main() { expect(message.status, entity.status); expect(message.updatedAt, isSameDateAs(entity.updatedAt)); expect(message.extraData, entity.extraData); - expect(message.user.id, entity.userId); - expect(message.deletedAt, isSameDateAs(entity.deletedAt)); + expect(message.user!.id, entity.userId); + expect(message.deletedAt, isSameDateAs(entity.deletedAt!)); expect(message.text, entity.messageText); expect(message.pinned, entity.pinned); - expect(message.pinExpires, isSameDateAs(entity.pinExpires)); - expect(message.pinnedAt, isSameDateAs(entity.pinnedAt)); - expect(message.pinnedBy.id, entity.pinnedByUserId); + expect(message.pinExpires, isSameDateAs(entity.pinExpires!)); + expect(message.pinnedAt, isSameDateAs(entity.pinnedAt!)); + expect(message.pinnedBy!.id, entity.pinnedByUserId); expect(message.reactionCounts, entity.reactionCounts); expect(message.reactionScores, entity.reactionScores); for (var i = 0; i < message.attachments.length; i++) { @@ -138,11 +139,11 @@ void main() { reactionCounts: reactions.fold( {}, (prev, curr) => - prev..update(curr.type, (value) => value + 1, ifAbsent: () => 1), + prev?..update(curr.type, (value) => value + 1, ifAbsent: () => 1), ), status: MessageSendingStatus.sending, updatedAt: DateTime.now(), - extraData: {'extra_test_data': 'extraData'}, + extraData: const {'extra_test_data': 'extraData'}, user: user, deletedAt: DateTime.now(), text: 'dummy text', @@ -167,18 +168,18 @@ void main() { expect(entity.status, message.status); expect(entity.updatedAt, isSameDateAs(message.updatedAt)); expect(entity.extraData, message.extraData); - expect(entity.userId, message.user.id); - expect(entity.deletedAt, isSameDateAs(message.deletedAt)); + expect(entity.userId, message.user!.id); + expect(entity.deletedAt, isSameDateAs(message.deletedAt!)); expect(entity.messageText, message.text); expect(entity.pinned, message.pinned); - expect(entity.pinExpires, isSameDateAs(message.pinExpires)); - expect(entity.pinnedAt, isSameDateAs(message.pinnedAt)); - expect(entity.pinnedByUserId, message.pinnedBy.id); + expect(entity.pinExpires, isSameDateAs(message.pinExpires!)); + expect(entity.pinnedAt, isSameDateAs(message.pinnedAt!)); + expect(entity.pinnedByUserId, message.pinnedBy!.id); expect(entity.reactionCounts, message.reactionCounts); expect(entity.reactionScores, message.reactionScores); expect( entity.attachments, - message.attachments?.map((it) => jsonEncode(it.toData()))?.toList(), + message.attachments.map((it) => jsonEncode(it.toData())).toList(), ); }); } diff --git a/packages/stream_chat_persistence/test/src/mapper/user_mapper_test.dart b/packages/stream_chat_persistence/test/src/mapper/user_mapper_test.dart index 5e40f237..adc05c58 100644 --- a/packages/stream_chat_persistence/test/src/mapper/user_mapper_test.dart +++ b/packages/stream_chat_persistence/test/src/mapper/user_mapper_test.dart @@ -24,7 +24,7 @@ void main() { expect(user.role, entity.role); expect(user.createdAt, isSameDateAs(entity.createdAt)); expect(user.updatedAt, isSameDateAs(entity.updatedAt)); - expect(user.lastActive, isSameDateAs(entity.lastActive)); + expect(user.lastActive, isSameDateAs(entity.lastActive!)); expect(user.online, entity.online); expect(user.banned, entity.banned); expect(user.extraData, entity.extraData); @@ -47,7 +47,7 @@ void main() { expect(entity.role, user.role); expect(entity.createdAt, isSameDateAs(user.createdAt)); expect(entity.updatedAt, isSameDateAs(user.updatedAt)); - expect(entity.lastActive, isSameDateAs(user.lastActive)); + expect(entity.lastActive, isSameDateAs(user.lastActive!)); expect(entity.online, user.online); expect(entity.banned, user.banned); expect(entity.extraData, user.extraData); diff --git a/packages/stream_chat_persistence/test/src/utils/date_matcher.dart b/packages/stream_chat_persistence/test/src/utils/date_matcher.dart index 909e58b0..dec68db0 100644 --- a/packages/stream_chat_persistence/test/src/utils/date_matcher.dart +++ b/packages/stream_chat_persistence/test/src/utils/date_matcher.dart @@ -1,13 +1,10 @@ import 'package:test/test.dart'; -import 'package:meta/meta.dart'; Matcher isSameDateAs(DateTime targetDate) => _IsSameDateAs(targetDate: targetDate); class _IsSameDateAs extends Matcher { - const _IsSameDateAs({ - @required this.targetDate, - }) : assert(targetDate != null, ''); + const _IsSameDateAs({required this.targetDate}); final DateTime targetDate; diff --git a/packages/stream_chat_persistence/test/stream_chat_persistence_client_test.dart b/packages/stream_chat_persistence/test/stream_chat_persistence_client_test.dart index 55a5113a..8a275201 100644 --- a/packages/stream_chat_persistence/test/stream_chat_persistence_client_test.dart +++ b/packages/stream_chat_persistence/test/stream_chat_persistence_client_test.dart @@ -1,4 +1,4 @@ -import 'package:mockito/mockito.dart'; +import 'package:mocktail/mocktail.dart'; import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat_persistence/src/db/moor_chat_database.dart'; import 'package:stream_chat_persistence/src/stream_chat_persistence_client.dart'; @@ -10,22 +10,6 @@ MoorChatDatabase _testDatabaseProvider(String userId, ConnectionMode mode) => MoorChatDatabase.testable(userId); void main() { - group('client constructor', () { - test('throws assertion error if null connectionMode is provided', () { - expect( - () => StreamChatPersistenceClient(connectionMode: null), - throwsA(isA()), - ); - }); - - test('throws assertion error if null logLevel is provided', () { - expect( - () => StreamChatPersistenceClient(logLevel: null), - throwsA(isA()), - ); - }); - }); - group('connect', () { const userId = 'testUserId'; test('successfully connects with the Database', () async { @@ -34,7 +18,7 @@ void main() { await client.connect(userId, databaseProvider: _testDatabaseProvider); expect(client.db, isNotNull); expect(client.db, isA()); - expect(client.db.userId, userId); + expect(client.db!.userId, userId); addTearDown(() async { await client.disconnect(); @@ -48,7 +32,7 @@ void main() { expect(client.db, isNotNull); expect(client.db, isNotNull); expect(client.db, isA()); - expect(client.db.userId, userId); + expect(client.db!.userId, userId); expect( () => client.connect(userId, databaseProvider: _testDatabaseProvider), throwsException, @@ -73,7 +57,7 @@ void main() { const userId = 'testUserId'; final mockDatabase = MockChatDatabase(); MoorChatDatabase _mockDatabaseProvider(_, __) => mockDatabase; - StreamChatPersistenceClient client; + late StreamChatPersistenceClient client; setUp(() async { client = StreamChatPersistenceClient(logLevel: Level.ALL); @@ -84,139 +68,152 @@ void main() { const parentId = 'testParentId'; final replies = List.generate(3, (index) => Message(id: 'testId$index')); - when(mockDatabase.messageDao.getThreadMessagesByParentId(parentId)) + when(() => mockDatabase.messageDao.getThreadMessagesByParentId(parentId)) .thenAnswer((_) async => replies); final fetchedReplies = await client.getReplies(parentId); expect(fetchedReplies.length, replies.length); - verify(mockDatabase.messageDao.getThreadMessagesByParentId(parentId)) + verify(() => + mockDatabase.messageDao.getThreadMessagesByParentId(parentId)) .called(1); }); test('getConnectionInfo', () async { - final event = Event(type: 'testEvent'); - when(mockDatabase.connectionEventDao.connectionEvent) + const event = Event(type: 'testEvent'); + when(() => mockDatabase.connectionEventDao.connectionEvent) .thenAnswer((_) async => event); final fetchedEvent = await client.getConnectionInfo(); - expect(fetchedEvent.type, event.type); - verify(mockDatabase.connectionEventDao.connectionEvent).called(1); + expect(fetchedEvent, isNotNull); + expect(fetchedEvent!.type, event.type); + verify(() => mockDatabase.connectionEventDao.connectionEvent).called(1); }); test('getLastSyncAt', () async { final lastSync = DateTime.now(); - when(mockDatabase.connectionEventDao.lastSyncAt) + when(() => mockDatabase.connectionEventDao.lastSyncAt) .thenAnswer((_) async => lastSync); final fetchedLastSync = await client.getLastSyncAt(); expect(fetchedLastSync, isSameDateAs(lastSync)); - verify(mockDatabase.connectionEventDao.lastSyncAt).called(1); + verify(() => mockDatabase.connectionEventDao.lastSyncAt).called(1); }); test('updateConnectionInfo', () async { - final event = Event(type: 'testEvent'); - when(mockDatabase.connectionEventDao.updateConnectionEvent(event)) - .thenAnswer((_) async { - return; - }); + const event = Event(type: 'testEvent'); + when(() => mockDatabase.connectionEventDao.updateConnectionEvent(event)) + .thenAnswer((_) async => 1); await client.updateConnectionInfo(event); - verify(mockDatabase.connectionEventDao.updateConnectionEvent(event)) + verify(() => mockDatabase.connectionEventDao.updateConnectionEvent(event)) .called(1); }); test('updateLastSyncAt', () async { final lastSync = DateTime.now(); - when(mockDatabase.connectionEventDao.updateLastSyncAt(lastSync)) - .thenAnswer((_) { - return; - }); + when(() => mockDatabase.connectionEventDao.updateLastSyncAt(lastSync)) + .thenAnswer((_) async => 1); await client.updateLastSyncAt(lastSync); - verify(mockDatabase.connectionEventDao.updateLastSyncAt(lastSync)) + verify(() => mockDatabase.connectionEventDao.updateLastSyncAt(lastSync)) .called(1); }); test('getChannelCids', () async { final channelCids = List.generate(3, (index) => 'testCid$index'); - when(mockDatabase.channelDao.cids).thenAnswer((_) async => channelCids); + when(() => mockDatabase.channelDao.cids) + .thenAnswer((_) async => channelCids); final fetchedChannelCids = await client.getChannelCids(); expect(fetchedChannelCids.length, channelCids.length); - verify(mockDatabase.channelDao.cids).called(1); + verify(() => mockDatabase.channelDao.cids).called(1); }); test('getChannelByCid', () async { - const cid = 'testCid'; + const cid = 'testType:testId'; final channelModel = ChannelModel(cid: cid); - when(mockDatabase.channelDao.getChannelByCid(cid)) + when(() => mockDatabase.channelDao.getChannelByCid(cid)) .thenAnswer((_) async => channelModel); final fetchedChannelModel = await client.getChannelByCid(cid); - expect(fetchedChannelModel.cid, channelModel.cid); - verify(mockDatabase.channelDao.getChannelByCid(cid)).called(1); + expect(fetchedChannelModel, isNotNull); + expect(fetchedChannelModel!.cid, channelModel.cid); + verify(() => mockDatabase.channelDao.getChannelByCid(cid)).called(1); }); test('getMembersByCid', () async { const cid = 'testCid'; final members = List.generate(3, (index) => Member()); - when(mockDatabase.memberDao.getMembersByCid(cid)) + when(() => mockDatabase.memberDao.getMembersByCid(cid)) .thenAnswer((_) async => members); final fetchedMembers = await client.getMembersByCid(cid); expect(fetchedMembers.length, members.length); - verify(mockDatabase.memberDao.getMembersByCid(cid)).called(1); + verify(() => mockDatabase.memberDao.getMembersByCid(cid)).called(1); }); test('getReadsByCid', () async { const cid = 'testCid'; - final reads = List.generate(3, (index) => Read()); - when(mockDatabase.readDao.getReadsByCid(cid)) + final reads = List.generate( + 3, + (index) => Read( + user: User(id: 'testUserId$index'), + lastRead: DateTime.now(), + ), + ); + when(() => mockDatabase.readDao.getReadsByCid(cid)) .thenAnswer((_) async => reads); final fetchedReads = await client.getReadsByCid(cid); expect(fetchedReads.length, reads.length); - verify(mockDatabase.readDao.getReadsByCid(cid)).called(1); + verify(() => mockDatabase.readDao.getReadsByCid(cid)).called(1); }); test('getMessagesByCid', () async { const cid = 'testCid'; final messages = List.generate(3, (index) => Message()); - when(mockDatabase.messageDao.getMessagesByCid(cid)) + when(() => mockDatabase.messageDao.getMessagesByCid(cid)) .thenAnswer((_) async => messages); final fetchedMessages = await client.getMessagesByCid(cid); expect(fetchedMessages.length, messages.length); - verify(mockDatabase.messageDao.getMessagesByCid(cid)).called(1); + verify(() => mockDatabase.messageDao.getMessagesByCid(cid)).called(1); }); test('getPinnedMessagesByCid', () async { const cid = 'testCid'; final messages = List.generate(3, (index) => Message()); - when(mockDatabase.pinnedMessageDao.getMessagesByCid(cid)) + when(() => mockDatabase.pinnedMessageDao.getMessagesByCid(cid)) .thenAnswer((_) async => messages); final fetchedMessages = await client.getPinnedMessagesByCid(cid); expect(fetchedMessages.length, messages.length); - verify(mockDatabase.pinnedMessageDao.getMessagesByCid(cid)).called(1); + verify(() => mockDatabase.pinnedMessageDao.getMessagesByCid(cid)) + .called(1); }); test('getChannelStateByCid', () async { - const cid = 'testCid'; + const cid = 'testType:testId'; final messages = List.generate(3, (index) => Message()); final members = List.generate(3, (index) => Member()); - final reads = List.generate(3, (index) => Read()); + final reads = List.generate( + 3, + (index) => Read( + user: User(id: 'testUserId$index'), + lastRead: DateTime.now(), + ), + ); final channel = ChannelModel(cid: cid); - when(mockDatabase.memberDao.getMembersByCid(cid)) + when(() => mockDatabase.memberDao.getMembersByCid(cid)) .thenAnswer((_) async => members); - when(mockDatabase.readDao.getReadsByCid(cid)) + when(() => mockDatabase.readDao.getReadsByCid(cid)) .thenAnswer((_) async => reads); - when(mockDatabase.channelDao.getChannelByCid(cid)) + when(() => mockDatabase.channelDao.getChannelByCid(cid)) .thenAnswer((_) async => channel); - when(mockDatabase.messageDao.getMessagesByCid(cid)) + when(() => mockDatabase.messageDao.getMessagesByCid(cid)) .thenAnswer((_) async => messages); - when(mockDatabase.pinnedMessageDao.getMessagesByCid(cid)) + when(() => mockDatabase.pinnedMessageDao.getMessagesByCid(cid)) .thenAnswer((_) async => messages); final fetchedChannelState = await client.getChannelStateByCid(cid); @@ -224,21 +221,28 @@ void main() { expect(fetchedChannelState.pinnedMessages.length, messages.length); expect(fetchedChannelState.members.length, members.length); expect(fetchedChannelState.read.length, reads.length); - expect(fetchedChannelState.channel.cid, channel.cid); + expect(fetchedChannelState.channel!.cid, channel.cid); - verify(mockDatabase.memberDao.getMembersByCid(cid)).called(1); - verify(mockDatabase.readDao.getReadsByCid(cid)).called(1); - verify(mockDatabase.channelDao.getChannelByCid(cid)).called(1); - verify(mockDatabase.messageDao.getMessagesByCid(cid)).called(1); - verify(mockDatabase.pinnedMessageDao.getMessagesByCid(cid)).called(1); + verify(() => mockDatabase.memberDao.getMembersByCid(cid)).called(1); + verify(() => mockDatabase.readDao.getReadsByCid(cid)).called(1); + verify(() => mockDatabase.channelDao.getChannelByCid(cid)).called(1); + verify(() => mockDatabase.messageDao.getMessagesByCid(cid)).called(1); + verify(() => mockDatabase.pinnedMessageDao.getMessagesByCid(cid)) + .called(1); }); test('getChannelStates', () async { - const cid = 'testCid'; + const cid = 'testType:testId'; final channels = List.generate(3, (index) => ChannelModel(cid: cid)); final messages = List.generate(3, (index) => Message()); final members = List.generate(3, (index) => Member()); - final reads = List.generate(3, (index) => Read()); + final reads = List.generate( + 3, + (index) => Read( + user: User(id: 'testUserId$index'), + lastRead: DateTime.now(), + ), + ); final channel = ChannelModel(cid: cid); final channelStates = channels .map( @@ -252,17 +256,17 @@ void main() { ) .toList(growable: false); - when(mockDatabase.channelQueryDao.getChannels()) + when(() => mockDatabase.channelQueryDao.getChannels()) .thenAnswer((_) async => channels); - when(mockDatabase.memberDao.getMembersByCid(cid)) + when(() => mockDatabase.memberDao.getMembersByCid(cid)) .thenAnswer((_) async => members); - when(mockDatabase.readDao.getReadsByCid(cid)) + when(() => mockDatabase.readDao.getReadsByCid(cid)) .thenAnswer((_) async => reads); - when(mockDatabase.channelDao.getChannelByCid(cid)) + when(() => mockDatabase.channelDao.getChannelByCid(cid)) .thenAnswer((_) async => channel); - when(mockDatabase.messageDao.getMessagesByCid(cid)) + when(() => mockDatabase.messageDao.getMessagesByCid(cid)) .thenAnswer((_) async => messages); - when(mockDatabase.pinnedMessageDao.getMessagesByCid(cid)) + when(() => mockDatabase.pinnedMessageDao.getMessagesByCid(cid)) .thenAnswer((_) async => messages); final fetchedChannelStates = await client.getChannelStates(); @@ -275,154 +279,139 @@ void main() { expect(fetched.messages.length, original.messages.length); expect(fetched.pinnedMessages.length, original.pinnedMessages.length); expect(fetched.read.length, original.read.length); - expect(fetched.channel.cid, original.channel.cid); + expect(fetched.channel!.cid, original.channel!.cid); } - verify(mockDatabase.channelQueryDao.getChannels()).called(1); - verify(mockDatabase.memberDao.getMembersByCid(cid)).called(3); - verify(mockDatabase.readDao.getReadsByCid(cid)).called(3); - verify(mockDatabase.channelDao.getChannelByCid(cid)).called(3); - verify(mockDatabase.messageDao.getMessagesByCid(cid)).called(3); - verify(mockDatabase.pinnedMessageDao.getMessagesByCid(cid)).called(3); + verify(() => mockDatabase.channelQueryDao.getChannels()).called(1); + verify(() => mockDatabase.memberDao.getMembersByCid(cid)).called(3); + verify(() => mockDatabase.readDao.getReadsByCid(cid)).called(3); + verify(() => mockDatabase.channelDao.getChannelByCid(cid)).called(3); + verify(() => mockDatabase.messageDao.getMessagesByCid(cid)).called(3); + verify(() => mockDatabase.pinnedMessageDao.getMessagesByCid(cid)) + .called(3); }); test('updateChannelQueries', () async { - const filter = {}; + final filter = Filter.in_('members', const ['testUserId']); const cids = []; - when(mockDatabase.channelQueryDao.updateChannelQueries(filter, cids)) - .thenAnswer((realInvocation) async { - return; - }); + when(() => + mockDatabase.channelQueryDao.updateChannelQueries(filter, cids)) + .thenAnswer((_) => Future.value()); await client.updateChannelQueries(filter, cids); - verify(mockDatabase.channelQueryDao.updateChannelQueries(filter, cids)) + verify(() => + mockDatabase.channelQueryDao.updateChannelQueries(filter, cids)) .called(1); }); test('deleteMessageById', () async { const messageId = 'testMessageId'; - when(mockDatabase.messageDao.deleteMessageByIds([messageId])) - .thenAnswer((_) async { - return; - }); + when(() => mockDatabase.messageDao.deleteMessageByIds([messageId])) + .thenAnswer((_) async => 1); await client.deleteMessageById(messageId); - verify(mockDatabase.messageDao.deleteMessageByIds([messageId])).called(1); + verify(() => mockDatabase.messageDao.deleteMessageByIds([messageId])) + .called(1); }); test('deletePinnedMessageById', () async { const messageId = 'testMessageId'; - when(mockDatabase.pinnedMessageDao.deleteMessageByIds([messageId])) - .thenAnswer((_) async { - return; - }); + when(() => mockDatabase.pinnedMessageDao.deleteMessageByIds([messageId])) + .thenAnswer((_) async => 1); await client.deletePinnedMessageById(messageId); - verify(mockDatabase.pinnedMessageDao.deleteMessageByIds([messageId])) + verify(() => + mockDatabase.pinnedMessageDao.deleteMessageByIds([messageId])) .called(1); }); test('deleteMessageByIds', () async { const messageIds = []; - when(mockDatabase.messageDao.deleteMessageByIds(messageIds)) - .thenAnswer((_) async { - return; - }); + when(() => mockDatabase.messageDao.deleteMessageByIds(messageIds)) + .thenAnswer((_) async => 1); await client.deleteMessageByIds(messageIds); - verify(mockDatabase.messageDao.deleteMessageByIds(messageIds)).called(1); + verify(() => mockDatabase.messageDao.deleteMessageByIds(messageIds)) + .called(1); }); test('deletePinnedMessageByIds', () async { const messageIds = []; - when(mockDatabase.pinnedMessageDao.deleteMessageByIds(messageIds)) - .thenAnswer((_) async { - return; - }); + when(() => mockDatabase.pinnedMessageDao.deleteMessageByIds(messageIds)) + .thenAnswer((_) async => 1); await client.deletePinnedMessageByIds(messageIds); - verify(mockDatabase.pinnedMessageDao.deleteMessageByIds(messageIds)) + verify(() => mockDatabase.pinnedMessageDao.deleteMessageByIds(messageIds)) .called(1); }); test('deleteMessageByCid', () async { const cid = 'testCid'; - when(mockDatabase.messageDao.deleteMessageByCids([cid])) - .thenAnswer((_) async { - return; - }); + when(() => mockDatabase.messageDao.deleteMessageByCids([cid])) + .thenAnswer((_) async => 1); await client.deleteMessageByCid(cid); - verify(mockDatabase.messageDao.deleteMessageByCids([cid])).called(1); + verify(() => mockDatabase.messageDao.deleteMessageByCids([cid])) + .called(1); }); test('deletePinnedMessageByCid', () async { const cid = 'testCid'; - when(mockDatabase.pinnedMessageDao.deleteMessageByCids([cid])) - .thenAnswer((_) async { - return; - }); + when(() => mockDatabase.pinnedMessageDao.deleteMessageByCids([cid])) + .thenAnswer((_) async => 1); await client.deletePinnedMessageByCid(cid); - verify(mockDatabase.pinnedMessageDao.deleteMessageByCids([cid])) + verify(() => mockDatabase.pinnedMessageDao.deleteMessageByCids([cid])) .called(1); }); test('deleteMessageByCids', () async { const cids = []; - when(mockDatabase.messageDao.deleteMessageByCids(cids)) - .thenAnswer((_) async { - return; - }); + when(() => mockDatabase.messageDao.deleteMessageByCids(cids)) + .thenAnswer((_) async => 1); await client.deleteMessageByCids(cids); - verify(mockDatabase.messageDao.deleteMessageByCids(cids)).called(1); + verify(() => mockDatabase.messageDao.deleteMessageByCids(cids)).called(1); }); test('deletePinnedMessageByCids', () async { const cids = []; - when(mockDatabase.pinnedMessageDao.deleteMessageByCids(cids)) - .thenAnswer((_) async { - return; - }); + when(() => mockDatabase.pinnedMessageDao.deleteMessageByCids(cids)) + .thenAnswer((_) async => 1); await client.deletePinnedMessageByCids(cids); - verify(mockDatabase.pinnedMessageDao.deleteMessageByCids(cids)).called(1); + verify(() => mockDatabase.pinnedMessageDao.deleteMessageByCids(cids)) + .called(1); }); test('deleteChannels', () async { const cids = []; - when(mockDatabase.channelDao.deleteChannelByCids(cids)) - .thenAnswer((_) async { - return; - }); + when(() => mockDatabase.channelDao.deleteChannelByCids(cids)) + .thenAnswer((_) async => 1); await client.deleteChannels(cids); - verify(mockDatabase.channelDao.deleteChannelByCids(cids)).called(1); + verify(() => mockDatabase.channelDao.deleteChannelByCids(cids)).called(1); }); test('updateMessages', () async { const cid = 'testCid'; final messages = List.generate(3, (index) => Message()); - when(mockDatabase.messageDao.updateMessages(cid, messages)) - .thenAnswer((_) async { - return; - }); + when(() => mockDatabase.messageDao.updateMessages(cid, messages)) + .thenAnswer((_) => Future.value()); await client.updateMessages(cid, messages); - verify(mockDatabase.messageDao.updateMessages(cid, messages)).called(1); + verify(() => mockDatabase.messageDao.updateMessages(cid, messages)) + .called(1); }); test('updatePinnedMessages', () async { const cid = 'testCid'; final messages = List.generate(3, (index) => Message()); - when(mockDatabase.pinnedMessageDao.updateMessages(cid, messages)) - .thenAnswer((_) async { - return; - }); + when(() => mockDatabase.pinnedMessageDao.updateMessages(cid, messages)) + .thenAnswer((_) => Future.value()); await client.updatePinnedMessages(cid, messages); - verify(mockDatabase.pinnedMessageDao.updateMessages(cid, messages)) + verify(() => mockDatabase.pinnedMessageDao.updateMessages(cid, messages)) .called(1); }); @@ -432,16 +421,14 @@ void main() { List.generate(3, (index) => Message(parentId: 'testParentId$index')); final threads = messages.fold>>( {}, - (prev, curr) { - return prev - ..update( - curr.parentId, - (value) => [...value, curr], - ifAbsent: () => [], - ); - }, + (prev, curr) => prev + ..update( + curr.parentId!, + (value) => [...value, curr], + ifAbsent: () => [], + ), ); - when(mockDatabase.messageDao.getThreadMessages(cid)) + when(() => mockDatabase.messageDao.getThreadMessages(cid)) .thenAnswer((realInvocation) async => messages); final fetchedThreads = await client.getChannelThreads(cid); @@ -452,85 +439,87 @@ void main() { expect(fetched.key, original.key); } - verify(mockDatabase.messageDao.getThreadMessages(cid)).called(1); + verify(() => mockDatabase.messageDao.getThreadMessages(cid)).called(1); }); test('updateChannels', () async { - final channels = List.generate(3, (index) => ChannelModel()); - when(mockDatabase.channelDao.updateChannels(channels)) - .thenAnswer((_) async { - return; - }); + const cid = 'testType:testId'; + final channels = List.generate(3, (index) => ChannelModel(cid: cid)); + when(() => mockDatabase.channelDao.updateChannels(channels)) + .thenAnswer((_) => Future.value()); await client.updateChannels(channels); - verify(mockDatabase.channelDao.updateChannels(channels)).called(1); + verify(() => mockDatabase.channelDao.updateChannels(channels)).called(1); }); test('updateMembers', () async { const cid = 'testCid'; final members = List.generate(3, (index) => Member()); - when(mockDatabase.memberDao.updateMembers(cid, members)) - .thenAnswer((_) async { - return; - }); + when(() => mockDatabase.memberDao.updateMembers(cid, members)) + .thenAnswer((_) => Future.value()); await client.updateMembers(cid, members); - verify(mockDatabase.memberDao.updateMembers(cid, members)).called(1); + verify(() => mockDatabase.memberDao.updateMembers(cid, members)) + .called(1); }); test('updateReads', () async { const cid = 'testCid'; - final reads = List.generate(3, (index) => Read()); - when(mockDatabase.readDao.updateReads(cid, reads)).thenAnswer((_) async { - return; - }); + final reads = List.generate( + 3, + (index) => Read( + user: User(id: 'testUserId$index'), + lastRead: DateTime.now(), + ), + ); + when(() => mockDatabase.readDao.updateReads(cid, reads)) + .thenAnswer((_) => Future.value()); await client.updateReads(cid, reads); - verify(mockDatabase.readDao.updateReads(cid, reads)).called(1); + verify(() => mockDatabase.readDao.updateReads(cid, reads)).called(1); }); test('updateUsers', () async { - final users = List.generate(3, (index) => User()); - when(mockDatabase.userDao.updateUsers(users)).thenAnswer((_) async { - return; - }); + final users = List.generate(3, (index) => User(id: 'testUserId$index')); + when(() => mockDatabase.userDao.updateUsers(users)) + .thenAnswer((_) => Future.value()); await client.updateUsers(users); - verify(mockDatabase.userDao.updateUsers(users)).called(1); + verify(() => mockDatabase.userDao.updateUsers(users)).called(1); }); test('updateReactions', () async { - final reactions = List.generate(3, (index) => Reaction()); - when(mockDatabase.reactionDao.updateReactions(reactions)) - .thenAnswer((_) async { - return; - }); + final reactions = List.generate( + 3, + (index) => Reaction(type: 'testType$index'), + ); + when(() => mockDatabase.reactionDao.updateReactions(reactions)) + .thenAnswer((_) => Future.value()); await client.updateReactions(reactions); - verify(mockDatabase.reactionDao.updateReactions(reactions)).called(1); + verify(() => mockDatabase.reactionDao.updateReactions(reactions)) + .called(1); }); test('deleteReactionsByMessageId', () async { final messageIds = []; - when(mockDatabase.reactionDao.deleteReactionsByMessageIds(messageIds)) - .thenAnswer((_) async { - return; - }); + when(() => + mockDatabase.reactionDao.deleteReactionsByMessageIds(messageIds)) + .thenAnswer((_) => Future.value()); await client.deleteReactionsByMessageId(messageIds); - verify(mockDatabase.reactionDao.deleteReactionsByMessageIds(messageIds)) + verify(() => + mockDatabase.reactionDao.deleteReactionsByMessageIds(messageIds)) .called(1); }); test('deleteMembersByCids', () async { final cids = []; - when(mockDatabase.memberDao.deleteMemberByCids(cids)) - .thenAnswer((_) async { - return; - }); + when(() => mockDatabase.memberDao.deleteMemberByCids(cids)) + .thenAnswer((_) => Future.value()); await client.deleteMembersByCids(cids); - verify(mockDatabase.memberDao.deleteMemberByCids(cids)).called(1); + verify(() => mockDatabase.memberDao.deleteMemberByCids(cids)).called(1); }); tearDown(() async {