diff --git a/Gemfile.lock b/Gemfile.lock
index 141afc122..8d6132849 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -582,7 +582,7 @@ GEM
uri (>= 0.11.1)
net-http-persistent (4.0.2)
connection_pool (~> 2.2)
- net-imap (0.4.24)
+ net-imap (0.6.4.1)
date
net-protocol
net-pop (0.1.2)
diff --git a/app/controllers/api/v1/accounts/categories_controller.rb b/app/controllers/api/v1/accounts/categories_controller.rb
index 686ffaeec..655b3c890 100644
--- a/app/controllers/api/v1/accounts/categories_controller.rb
+++ b/app/controllers/api/v1/accounts/categories_controller.rb
@@ -53,7 +53,7 @@ class Api::V1::Accounts::CategoriesController < Api::V1::Accounts::BaseControlle
def category_params
params.require(:category).permit(
- :name, :description, :position, :slug, :locale, :icon, :parent_category_id, :associated_category_id
+ :name, :description, :position, :slug, :locale, :icon, :icon_color, :parent_category_id, :associated_category_id
)
end
diff --git a/app/controllers/api/v1/accounts/conversations_controller.rb b/app/controllers/api/v1/accounts/conversations_controller.rb
index 2856c7817..2e53fa7c9 100644
--- a/app/controllers/api/v1/accounts/conversations_controller.rb
+++ b/app/controllers/api/v1/accounts/conversations_controller.rb
@@ -140,7 +140,7 @@ class Api::V1::Accounts::ConversationsController < Api::V1::Accounts::BaseContro
def destroy
authorize @conversation, :destroy?
- ::DeleteObjectJob.perform_later(@conversation, Current.user, request.ip)
+ ::Conversations::DeleteService.new(conversation: @conversation, user: Current.user, ip: request.ip).perform
head :ok
end
diff --git a/app/controllers/api/v1/profile/sessions_controller.rb b/app/controllers/api/v1/profile/sessions_controller.rb
new file mode 100644
index 000000000..72e9451eb
--- /dev/null
+++ b/app/controllers/api/v1/profile/sessions_controller.rb
@@ -0,0 +1,36 @@
+class Api::V1::Profile::SessionsController < Api::BaseController
+ before_action :set_session, only: [:destroy]
+
+ def index
+ @sessions = current_user.user_sessions.where(client_id: active_token_client_ids).order(last_activity_at: :desc)
+ @current_client_id = request.headers['client']
+ end
+
+ def destroy
+ if @session.current?(request.headers['client'])
+ render json: { error: I18n.t('profile_settings.sessions.cannot_revoke_current') }, status: :unprocessable_entity
+ return
+ end
+
+ revoke_token!(@session.client_id)
+ @session.destroy!
+ head :ok
+ end
+
+ private
+
+ def set_session
+ @session = current_user.user_sessions.find(params[:id])
+ end
+
+ def revoke_token!(client_id)
+ tokens = current_user.tokens
+ tokens.delete(client_id)
+ current_user.update!(tokens: tokens)
+ end
+
+ def active_token_client_ids
+ now = Time.current.to_i
+ (current_user.tokens || {}).select { |_, v| v['expiry'].to_i > now }.keys
+ end
+end
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 2f389049d..9dea4b4da 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -3,6 +3,7 @@ class ApplicationController < ActionController::Base
include RequestExceptionHandler
include Pundit::Authorization
include SwitchLocale
+ include TrackSessionActivity
skip_before_action :verify_authenticity_token
diff --git a/app/controllers/concerns/track_session_activity.rb b/app/controllers/concerns/track_session_activity.rb
new file mode 100644
index 000000000..f6a512922
--- /dev/null
+++ b/app/controllers/concerns/track_session_activity.rb
@@ -0,0 +1,22 @@
+module TrackSessionActivity
+ extend ActiveSupport::Concern
+
+ included do
+ after_action :update_session_activity
+ end
+
+ private
+
+ def update_session_activity
+ return unless current_user
+ return if request.headers['client'].blank?
+
+ UserSessionTrackingService.new(
+ user: current_user,
+ request: request,
+ client_id: request.headers['client']
+ ).update_activity!
+ rescue StandardError => e
+ Rails.logger.warn "Session activity update failed: #{e.message}"
+ end
+end
diff --git a/app/controllers/devise_overrides/sessions_controller.rb b/app/controllers/devise_overrides/sessions_controller.rb
index bd7bb9b44..587b52c83 100644
--- a/app/controllers/devise_overrides/sessions_controller.rb
+++ b/app/controllers/devise_overrides/sessions_controller.rb
@@ -1,4 +1,6 @@
class DeviseOverrides::SessionsController < DeviseTokenAuth::SessionsController
+ MAX_SESSIONS = ENV.fetch('MAX_USER_SESSIONS', 25).to_i
+
# Prevent session parameter from being passed
# Unpermitted parameter: session
wrap_parameters format: []
@@ -14,12 +16,14 @@ class DeviseOverrides::SessionsController < DeviseTokenAuth::SessionsController
user = find_user_for_authentication
return handle_mfa_required(user) if user&.mfa_enabled?
+ return if user && enforce_session_limit_for_password_login(user)
# Only proceed with standard authentication if no MFA is required
super
end
def render_create_success
+ track_user_session unless @impersonation
render partial: 'devise/auth', formats: [:json], locals: { resource: @resource }
end
@@ -53,6 +57,8 @@ class DeviseOverrides::SessionsController < DeviseTokenAuth::SessionsController
end
def handle_sso_authentication
+ return if !@impersonation && enforce_session_limit_for_password_login(@resource)
+
authenticate_resource_with_sso_token
yield @resource if block_given?
render_create_success
@@ -65,7 +71,10 @@ class DeviseOverrides::SessionsController < DeviseTokenAuth::SessionsController
end
def authenticate_resource_with_sso_token
- @token = @resource.create_token
+ # DTA evicts the earliest-expiring token after save when at max_number_of_devices.
+ # The short-lived impersonation token would always be that one, so pre-evict to make room.
+ make_room_for_impersonation_token if @impersonation
+ @token = @resource.create_token(lifespan: @impersonation ? 2.days.to_i : nil)
@resource.save!
sign_in(:user, @resource, store: false, bypass: false)
@@ -73,11 +82,21 @@ class DeviseOverrides::SessionsController < DeviseTokenAuth::SessionsController
@resource.invalidate_sso_auth_token(params[:sso_auth_token])
end
+ def make_room_for_impersonation_token
+ return if @resource.tokens.size < DeviseTokenAuth.max_number_of_devices
+
+ oldest_client_id = @resource.tokens.min_by { |_, v| v['expiry'].to_i }&.first
+ @resource.tokens.delete(oldest_client_id) if oldest_client_id
+ end
+
def process_sso_auth_token
return if params[:email].blank?
user = User.from_email(params[:email])
- @resource = user if user&.valid_sso_auth_token?(params[:sso_auth_token])
+ return unless user&.valid_sso_auth_token?(params[:sso_auth_token])
+
+ @resource = user
+ @impersonation = user.sso_auth_token_impersonation?(params[:sso_auth_token])
end
def handle_mfa_required(user)
@@ -103,6 +122,7 @@ class DeviseOverrides::SessionsController < DeviseTokenAuth::SessionsController
end
def sign_in_mfa_user(user)
+ evict_oldest_session(user) if sessions_limit_reached?(user)
@resource = user
@token = @resource.create_token
@resource.save!
@@ -114,6 +134,103 @@ class DeviseOverrides::SessionsController < DeviseTokenAuth::SessionsController
def render_mfa_error(message_key, status = :bad_request)
render json: { error: I18n.t(message_key) }, status: status
end
+
+ def sessions_limit_reached?(user)
+ active_token_count(user) >= MAX_SESSIONS
+ end
+
+ def active_token_count(user)
+ now = Time.current.to_i
+ (user.tokens || {}).count { |_, v| v['expiry'].to_i > now }
+ end
+
+ # Returns true when a response has been rendered (e.g., 409 picker). Non-browser clients
+ # auto-evict instead of getting stuck on a UI they can't render.
+ def enforce_session_limit_for_password_login(user)
+ if revoking_sessions?
+ revoke_sessions_for_login(user)
+ return false
+ end
+
+ return false unless sessions_limit_reached?(user)
+
+ # Picker only when every token has a tracked session; partial tracking would
+ # show a misleading count, so fall through to silent eviction instead.
+ if browser_request? && user.user_sessions.count >= user.tokens.size
+ handle_sessions_limit_for_login(user)
+ true
+ else
+ evict_oldest_session(user)
+ false
+ end
+ end
+
+ def browser_request?
+ request.user_agent.to_s.include?('Mozilla')
+ end
+
+ def revoking_sessions?
+ params[:revoke_session_id].present? || params[:revoke_all_sessions].present?
+ end
+
+ def revoke_sessions_for_login(user)
+ if params[:revoke_all_sessions].present?
+ user.tokens = {}
+ user.save!
+ user.user_sessions.destroy_all
+ elsif params[:revoke_session_id].present?
+ session = user.user_sessions.find_by(id: params[:revoke_session_id])
+ return unless session
+
+ user.tokens.delete(session.client_id)
+ user.save!
+ session.destroy!
+ end
+ end
+
+ def evict_oldest_session(user)
+ # Drop pre-rollout untracked tokens first so freshly tracked logins aren't evicted.
+ return evict_oldest_token(user) if user.user_sessions.count < user.tokens.size
+
+ oldest_session = user.user_sessions.order(Arel.sql('COALESCE(last_activity_at, created_at) ASC')).first
+ return evict_oldest_token(user) unless oldest_session
+
+ user.tokens.delete(oldest_session.client_id)
+ user.save!
+ oldest_session.destroy!
+ end
+
+ # Fallback if a token exists without a UserSession row (e.g., legacy data before tracking shipped).
+ def evict_oldest_token(user)
+ return if user.tokens.blank?
+
+ oldest_client_id = user.tokens.min_by { |_, v| v['expiry'].to_i }&.first
+ return unless oldest_client_id
+
+ user.tokens.delete(oldest_client_id)
+ user.save!
+ end
+
+ PICKER_SESSION_FIELDS = %i[id browser_name browser_version device_name platform_name platform_version
+ ip_address city country last_activity_at created_at].freeze
+
+ def handle_sessions_limit_for_login(user)
+ sessions = user.user_sessions.order(last_activity_at: :desc).map { |s| s.slice(*PICKER_SESSION_FIELDS) }
+ render json: { sessions_limit_reached: true, sessions: sessions }, status: :conflict
+ end
+
+ def track_user_session
+ client_id = @token&.try(:client) || response.headers['client']
+ return unless client_id.present? && @resource.present?
+
+ UserSessionTrackingService.new(
+ user: @resource,
+ request: request,
+ client_id: client_id
+ ).create_or_update!
+ rescue StandardError => e
+ Rails.logger.warn "Session tracking failed: #{e.message}"
+ end
end
DeviseOverrides::SessionsController.prepend_mod_with('DeviseOverrides::SessionsController')
diff --git a/app/controllers/twilio/callback_controller.rb b/app/controllers/twilio/callback_controller.rb
index 53075a555..ed1a05376 100644
--- a/app/controllers/twilio/callback_controller.rb
+++ b/app/controllers/twilio/callback_controller.rb
@@ -35,7 +35,17 @@ class Twilio::CallbackController < ApplicationController
:ExternalUserId,
:ParentExternalUserId,
:ProfileUsername,
- :Username
+ :Username,
+ :ReferralBody,
+ :ReferralHeadline,
+ :ReferralSourceId,
+ :ReferralSourceType,
+ :ReferralSourceUrl,
+ :ReferralMediaId,
+ :ReferralMediaContentType,
+ :ReferralMediaUrl,
+ :ReferralNumMedia,
+ :ReferralCtwaClid
)
end
end
diff --git a/app/finders/conversation_finder.rb b/app/finders/conversation_finder.rb
index 1c27d8260..74bf903f5 100644
--- a/app/finders/conversation_finder.rb
+++ b/app/finders/conversation_finder.rb
@@ -12,6 +12,7 @@ class ConversationFinder
'waiting_since_asc' => %w[sort_on_waiting_since asc],
'waiting_since_desc' => %w[sort_on_waiting_since desc],
'priority_desc_created_at_asc' => %w[sort_on_priority_created_at desc],
+ 'unread' => %w[sort_on_unread desc],
# To be removed in v3.5.0
'latest' => %w[sort_on_last_activity_at desc],
diff --git a/app/helpers/portal_helper.rb b/app/helpers/portal_helper.rb
index 0c993ec59..64e46f0b7 100644
--- a/app/helpers/portal_helper.rb
+++ b/app/helpers/portal_helper.rb
@@ -17,15 +17,11 @@ module PortalHelper
uri.to_s
end
- def generate_portal_bg_color(portal_color, theme)
+ def generate_portal_bg(portal_color, theme)
base_color = theme == 'dark' ? 'black' : 'white'
"color-mix(in srgb, #{portal_color} 20%, #{base_color})"
end
- def generate_portal_bg(portal_color, theme)
- generate_portal_bg_color(portal_color, theme)
- end
-
def generate_gradient_to_bottom(theme)
base_color = theme == 'dark' ? '#151718' : 'white'
"linear-gradient(to bottom, transparent, #{base_color})"
@@ -41,6 +37,10 @@ module PortalHelper
language_map[locale] || locale
end
+ def html_lang_attribute(locale)
+ locale.to_s.tr('_', '-')
+ end
+
def theme_query_string(theme)
theme.present? && theme != 'system' ? "?theme=#{theme}" : ''
end
@@ -97,6 +97,18 @@ module PortalHelper
ChatwootMarkdownRenderer.new(content).render_markdown_to_plain_text
end
+ # Renders a stored category icon: a bare ri icon name (e.g. `vip-crown-2-fill/line`) saved color, or a plain emoji character.
+ def render_emoji_or_icon(value, color = nil)
+ return '' if value.blank?
+
+ # Emojis are non-ascii; bare icon names match this safe charset.
+ return ERB::Util.html_escape(value) unless value.match?(/\A[a-z][a-z0-9-]*\z/)
+
+ icon_class = value.start_with?('i-') ? value : "i-ri-#{value}"
+ style = "color: #{color};" if color.to_s.match?(/\A#\h{3,8}\z/)
+ tag.span(class: icon_class, style: style, 'aria-hidden': true)
+ end
+
def thumbnail_bg_color(username)
colors = ['#6D95BA', '#A4C3C3', '#E19191']
return colors.sample if username.blank?
diff --git a/app/javascript/dashboard/api/auth.js b/app/javascript/dashboard/api/auth.js
index a1b15ee79..b9dc59964 100644
--- a/app/javascript/dashboard/api/auth.js
+++ b/app/javascript/dashboard/api/auth.js
@@ -106,4 +106,10 @@ export default {
const urlData = endPoints('resetAccessToken');
return axios.post(urlData.url);
},
+ getSessions() {
+ return axios.get('/api/v1/profile/sessions');
+ },
+ revokeSession(id) {
+ return axios.delete(`/api/v1/profile/sessions/${id}`);
+ },
};
diff --git a/app/javascript/dashboard/api/contacts.js b/app/javascript/dashboard/api/contacts.js
index c39a4cf9d..0b32c0bc2 100644
--- a/app/javascript/dashboard/api/contacts.js
+++ b/app/javascript/dashboard/api/contacts.js
@@ -40,6 +40,12 @@ class ContactAPI extends ApiClient {
return axios.get(`${this.url}/${contactId}/conversations`, { params });
}
+ getAttachments(contactId, page = 1) {
+ return axios.get(`${this.url}/${contactId}/attachments`, {
+ params: { page },
+ });
+ }
+
getContactableInboxes(contactId) {
return axios.get(`${this.url}/${contactId}/contactable_inboxes`);
}
diff --git a/app/javascript/dashboard/components-next/Contacts/ContactsDetailsLayout.vue b/app/javascript/dashboard/components-next/Contacts/ContactsDetailsLayout.vue
index 351cc7071..44fafb6c5 100644
--- a/app/javascript/dashboard/components-next/Contacts/ContactsDetailsLayout.vue
+++ b/app/javascript/dashboard/components-next/Contacts/ContactsDetailsLayout.vue
@@ -128,9 +128,14 @@ const closeMobileSidebar = () => {
@@ -179,9 +184,14 @@ const closeMobileSidebar = () => {
diff --git a/app/javascript/dashboard/components-next/Contacts/ContactsSidebar/ContactCustomAttributes.vue b/app/javascript/dashboard/components-next/Contacts/ContactsSidebar/ContactCustomAttributes.vue
index 039d2c709..1a9246e27 100644
--- a/app/javascript/dashboard/components-next/Contacts/ContactsSidebar/ContactCustomAttributes.vue
+++ b/app/javascript/dashboard/components-next/Contacts/ContactsSidebar/ContactCustomAttributes.vue
@@ -108,7 +108,7 @@ const hasNoUsedAttributes = computed(() => usedAttributes.value.length === 0);
-
+
+import { computed, onMounted, ref } from 'vue';
+import { useI18n } from 'vue-i18n';
+import { useRoute, useRouter } from 'vue-router';
+import { useStore, useMapGetter } from 'dashboard/composables/store';
+import {
+ MEDIA_TYPES,
+ NON_FILE_TYPES,
+} from 'dashboard/components-next/message/constants';
+
+import GalleryView from 'dashboard/components/widgets/conversation/components/GalleryView.vue';
+import Media from 'dashboard/components-next/SharedAttachments/Media.vue';
+import Files from 'dashboard/components-next/SharedAttachments/Files.vue';
+import Spinner from 'dashboard/components-next/spinner/Spinner.vue';
+
+const MEDIA_PEEK_LIMIT = 12;
+const FILES_PEEK_LIMIT = 6;
+
+const route = useRoute();
+const router = useRouter();
+const store = useStore();
+const { t } = useI18n();
+
+const attachmentsByContact = useMapGetter('contacts/getContactAttachments');
+const uiFlags = useMapGetter('contacts/getUIFlags');
+
+const attachments = computed(() =>
+ attachmentsByContact.value(route.params.contactId)
+);
+const isFetching = computed(() => uiFlags.value.isFetchingAttachments);
+
+const hasContent = computed(() =>
+ attachments.value.some(
+ a => a.data_url && !NON_FILE_TYPES.includes(a.file_type)
+ )
+);
+
+const mediaAttachments = computed(() =>
+ attachments.value
+ .filter(a => MEDIA_TYPES.includes(a.file_type) && a.data_url)
+ .sort((a, b) => (b.created_at || 0) - (a.created_at || 0))
+);
+
+const showGallery = ref(false);
+const selectedAttachment = ref(null);
+
+const onMediaSelect = attachment => {
+ selectedAttachment.value = attachment;
+ showGallery.value = true;
+};
+
+const onFileSelect = attachment => {
+ if (attachment.data_url) {
+ window.open(attachment.data_url, '_blank', 'noopener,noreferrer');
+ }
+};
+
+const onJumpToMessage = attachment => {
+ if (!attachment.conversation_id || !attachment.message_id) return;
+ router.push({
+ name: 'inbox_conversation',
+ params: {
+ accountId: route.params.accountId,
+ conversation_id: attachment.conversation_id,
+ },
+ query: { messageId: attachment.message_id },
+ });
+};
+
+onMounted(() => {
+ store.dispatch('contacts/fetchAttachments', route.params.contactId);
+});
+
+
+
+
+
+
+
+
+ {{ t('CONVERSATION_SIDEBAR.SHARED_FILES.EMPTY') }}
+
+
+
+
+
+
+
+
diff --git a/app/javascript/dashboard/components-next/Contacts/ContactsSidebar/ContactMerge.vue b/app/javascript/dashboard/components-next/Contacts/ContactsSidebar/ContactMerge.vue
index d569ae900..1d019ff80 100644
--- a/app/javascript/dashboard/components-next/Contacts/ContactsSidebar/ContactMerge.vue
+++ b/app/javascript/dashboard/components-next/Contacts/ContactsSidebar/ContactMerge.vue
@@ -103,7 +103,7 @@ const onMergeContacts = async () => {
-
+
{{ t('CONTACTS_LAYOUT.SIDEBAR.MERGE.TITLE') }}
diff --git a/app/javascript/dashboard/components-next/Contacts/ContactsSidebar/ContactNotes.vue b/app/javascript/dashboard/components-next/Contacts/ContactsSidebar/ContactNotes.vue
index 789aba1c8..218a1293f 100644
--- a/app/javascript/dashboard/components-next/Contacts/ContactsSidebar/ContactNotes.vue
+++ b/app/javascript/dashboard/components-next/Contacts/ContactsSidebar/ContactNotes.vue
@@ -55,7 +55,7 @@ useKeyboardEvents(keyboardEvents);
-
+
{
const categoryName = computed(() => {
if (props.category?.slug) {
- return `${props.category.icon} ${props.category.name}`;
+ return props.category.name;
}
return t(
'HELP_CENTER.ARTICLES_PAGE.ARTICLE_CARD.CARD.CATEGORY.UNCATEGORISED'
@@ -222,7 +223,15 @@ const handleClick = id => {
{{ authorName || '-' }}
-
+
+
{{ categoryName }}
{
- return `${props.icon} ${props.title}`;
-});
-
const description = computed(() => {
return props.description ? props.description : 'No description added';
});
@@ -83,10 +84,16 @@ const handleAction = ({ action, value }) => {
- {{ categoryTitleWithIcon }}
+
+ {{ title }}
{
const categoryList = computed(() => {
return (
categories.value
- .map(({ name, id, icon }) => ({
+ .map(({ name, id, icon, icon_color: iconColor }) => ({
label: name,
value: id,
emoji: icon,
+ iconColor,
isSelected: isNewArticle.value
? id === (selectedCategoryId.value || selectedCategory.value?.id)
: id === props.article?.category?.id,
@@ -202,10 +204,6 @@ onMounted(() => {
{
return tabs.value.findIndex(tab => tab.value === tabParam);
});
-const activeCategoryName = computed(() => {
- const activeCategory = props.categories.find(
- category => category.slug === route.params.categorySlug
- );
+const activeCategory = computed(() =>
+ props.categories.find(category => category.slug === route.params.categorySlug)
+);
- if (activeCategory) {
- const { icon, name } = activeCategory;
- return `${icon} ${name}`;
- }
-
- return t('HELP_CENTER.ARTICLES_PAGE.ARTICLES_HEADER.CATEGORY.ALL');
-});
+const activeCategoryName = computed(
+ () =>
+ activeCategory.value?.name ||
+ t('HELP_CENTER.ARTICLES_PAGE.ARTICLES_HEADER.CATEGORY.ALL')
+);
const activeLocaleName = computed(() => {
return props.allowedLocales.find(
@@ -94,6 +92,7 @@ const categoryMenuItems = computed(() => {
value: category.slug,
action: 'filter',
emoji: category.icon,
+ iconColor: category.icon_color,
}));
const hasCategorySlug = !!route.params.categorySlug;
@@ -167,14 +166,23 @@ const handleTabChange = value => {
+ >
+
+
+ {{ activeCategoryName }}
+
+
value: category.id,
action: 'move',
emoji: category.icon,
+ iconColor: category.icon_color,
}))
);
diff --git a/app/javascript/dashboard/components-next/HelpCenter/Pages/CategoryPage/CategoryDialog.vue b/app/javascript/dashboard/components-next/HelpCenter/Pages/CategoryPage/CategoryDialog.vue
index d71c17260..efb6be73f 100644
--- a/app/javascript/dashboard/components-next/HelpCenter/Pages/CategoryPage/CategoryDialog.vue
+++ b/app/javascript/dashboard/components-next/HelpCenter/Pages/CategoryPage/CategoryDialog.vue
@@ -38,8 +38,8 @@ const { t } = useI18n();
const route = useRoute();
const handleCategory = async formData => {
- const { id, name, slug, icon, description, locale } = formData;
- const categoryData = { name, icon, slug, description };
+ const { id, name, slug, icon, iconColor, description, locale } = formData;
+ const categoryData = { name, icon, icon_color: iconColor, slug, description };
if (props.mode === 'create') {
categoryData.locale = locale;
diff --git a/app/javascript/dashboard/components-next/HelpCenter/Pages/CategoryPage/CategoryForm.vue b/app/javascript/dashboard/components-next/HelpCenter/Pages/CategoryPage/CategoryForm.vue
index d2bf42ef5..a1d5624c9 100644
--- a/app/javascript/dashboard/components-next/HelpCenter/Pages/CategoryPage/CategoryForm.vue
+++ b/app/javascript/dashboard/components-next/HelpCenter/Pages/CategoryPage/CategoryForm.vue
@@ -18,6 +18,7 @@ import { convertToCategorySlug } from 'dashboard/helper/commons.js';
import Input from 'dashboard/components-next/input/Input.vue';
import TextArea from 'dashboard/components-next/textarea/TextArea.vue';
import Button from 'dashboard/components-next/button/Button.vue';
+import EmojiIcon from 'dashboard/components-next/emoji-icon-picker/EmojiIcon.vue';
const props = defineProps({
mode: {
@@ -49,8 +50,9 @@ const props = defineProps({
const emit = defineEmits(['submit', 'cancel']);
-const EmojiInput = defineAsyncComponent(
- () => import('shared/components/emoji/EmojiInput.vue')
+const EmojiIconPicker = defineAsyncComponent(
+ () =>
+ import('dashboard/components-next/emoji-icon-picker/EmojiIconPicker.vue')
);
const { t } = useI18n();
@@ -72,6 +74,7 @@ const state = reactive({
id: '',
name: '',
icon: '',
+ iconColor: '',
slug: '',
description: '',
locale: '',
@@ -109,8 +112,19 @@ const slugHelpText = computed(() => {
});
});
-const onClickInsertEmoji = emoji => {
- state.icon = emoji;
+const onSelectIcon = ({ type, value, color }) => {
+ state.icon = value;
+ state.iconColor = type === 'icon' ? color : '';
+ isEmojiPickerOpen.value = false;
+};
+
+const onColorChange = color => {
+ state.iconColor = color;
+};
+
+const onRemoveIcon = () => {
+ state.icon = '';
+ state.iconColor = '';
isEmojiPickerOpen.value = false;
};
@@ -139,7 +153,14 @@ watch(
newCategory => {
if (props.mode === 'edit' && newCategory) {
const { id, name, icon, slug, description } = newCategory;
- Object.assign(state, { id, name, icon, slug, description });
+ Object.assign(state, {
+ id,
+ name,
+ icon,
+ iconColor: newCategory.icon_color || '',
+ slug,
+ description,
+ });
}
},
{ immediate: true }
@@ -197,19 +218,29 @@ defineExpose({ state, isSubmitDisabled });
-
+
+
+
+
diff --git a/app/javascript/dashboard/components-next/HelpCenter/Pages/CategoryPage/CategoryHeaderControls.vue b/app/javascript/dashboard/components-next/HelpCenter/Pages/CategoryPage/CategoryHeaderControls.vue
index f25ac8027..d134f1168 100644
--- a/app/javascript/dashboard/components-next/HelpCenter/Pages/CategoryPage/CategoryHeaderControls.vue
+++ b/app/javascript/dashboard/components-next/HelpCenter/Pages/CategoryPage/CategoryHeaderControls.vue
@@ -106,6 +106,7 @@ const breadcrumbItems = computed(() => {
}
),
emoji: selectedCategoryEmoji.value,
+ iconColor: selectedCategory.value?.icon_color,
});
}
return items;
diff --git a/app/javascript/dashboard/components-next/HelpCenter/Pages/CategoryPage/CategoryList.vue b/app/javascript/dashboard/components-next/HelpCenter/Pages/CategoryPage/CategoryList.vue
index 0dc0de895..45e783246 100644
--- a/app/javascript/dashboard/components-next/HelpCenter/Pages/CategoryPage/CategoryList.vue
+++ b/app/javascript/dashboard/components-next/HelpCenter/Pages/CategoryPage/CategoryList.vue
@@ -68,6 +68,7 @@ watch(
:id="element.id"
:title="element.name"
:icon="element.icon"
+ :icon-color="element.icon_color"
:description="element.description"
:articles-count="element.meta?.articles_count || 0"
:slug="element.slug"
diff --git a/app/javascript/dashboard/components-next/HelpCenter/Pages/CategoryPage/EditCategoryDialog.vue b/app/javascript/dashboard/components-next/HelpCenter/Pages/CategoryPage/EditCategoryDialog.vue
index 5b339e568..2456ce596 100644
--- a/app/javascript/dashboard/components-next/HelpCenter/Pages/CategoryPage/EditCategoryDialog.vue
+++ b/app/javascript/dashboard/components-next/HelpCenter/Pages/CategoryPage/EditCategoryDialog.vue
@@ -52,8 +52,8 @@ const activeLocaleCode = computed(() => activeLocale.value?.code ?? '');
const onUpdateCategory = async () => {
if (!categoryFormRef.value) return;
const { state } = categoryFormRef.value;
- const { id, name, slug, icon, description } = state;
- const categoryData = { name, icon, slug, description };
+ const { id, name, slug, icon, iconColor, description } = state;
+ const categoryData = { name, icon, icon_color: iconColor, slug, description };
categoryData.id = id;
try {
diff --git a/app/javascript/dashboard/components-next/NewConversation/components/ActionButtons.vue b/app/javascript/dashboard/components-next/NewConversation/components/ActionButtons.vue
index 22c142322..e488ee107 100644
--- a/app/javascript/dashboard/components-next/NewConversation/components/ActionButtons.vue
+++ b/app/javascript/dashboard/components-next/NewConversation/components/ActionButtons.vue
@@ -53,8 +53,9 @@ const generateUid = () => {
const uploadAttachment = ref(null);
const isEmojiPickerOpen = ref(false);
-const EmojiInput = defineAsyncComponent(
- () => import('shared/components/emoji/EmojiInput.vue')
+const EmojiIconPicker = defineAsyncComponent(
+ () =>
+ import('dashboard/components-next/emoji-icon-picker/EmojiIconPicker.vue')
);
const {
@@ -215,10 +216,11 @@ useEventListener(document, 'paste', onPaste);
class="!w-10"
@click="isEmojiPickerOpen = !isEmojiPickerOpen"
/>
-
+import { computed, ref } from 'vue';
+import { useI18n } from 'vue-i18n';
+import { useAlert } from 'dashboard/composables';
+import { formatBytes } from 'shared/helpers/FileHelper';
+import { dynamicTime, shortTimestamp } from 'shared/helpers/timeHelper';
+import { downloadFile } from '@chatwoot/utils';
+import {
+ MEDIA_TYPES,
+ NON_FILE_TYPES,
+} from 'dashboard/components-next/message/constants';
+
+import FileIcon from 'next/icon/FileIcon.vue';
+import NextButton from 'dashboard/components-next/button/Button.vue';
+
+const props = defineProps({
+ attachments: { type: Array, default: () => [] },
+ peekLimit: { type: Number, default: 0 },
+ showJumpToMessage: { type: Boolean, default: false },
+});
+
+const emit = defineEmits(['select', 'jumpToMessage']);
+
+const { t } = useI18n();
+
+const fileAttachments = computed(() =>
+ [...props.attachments]
+ .filter(
+ a =>
+ a.data_url &&
+ !MEDIA_TYPES.includes(a.file_type) &&
+ !NON_FILE_TYPES.includes(a.file_type)
+ )
+ .sort((a, b) => (b.created_at || 0) - (a.created_at || 0))
+);
+
+const showAll = ref(false);
+const downloadingId = ref(null);
+
+const isPeekable = computed(() => props.peekLimit > 0);
+
+const visibleFiles = computed(() => {
+ if (!isPeekable.value || showAll.value) return fileAttachments.value;
+ return fileAttachments.value.slice(0, props.peekLimit);
+});
+
+const fileNameFromUrl = url => {
+ if (!url) return '';
+ const name = url.split('/').pop();
+ return name ? decodeURIComponent(name) : '';
+};
+
+const displayName = attachment =>
+ fileNameFromUrl(attachment.data_url) ||
+ t('CONVERSATION_SIDEBAR.SHARED_FILES.UNTITLED_FILE');
+
+const displaySize = attachment => {
+ if (attachment.file_size) return formatBytes(attachment.file_size);
+ if (attachment.extension) return attachment.extension.toUpperCase();
+ return '—';
+};
+
+const displayTime = attachment => {
+ if (!attachment.created_at) return '';
+ return shortTimestamp(dynamicTime(attachment.created_at), true);
+};
+
+const onActivate = attachment => emit('select', attachment);
+
+const onDownloadFile = async attachment => {
+ const { id, file_type: type, data_url: url, extension } = attachment;
+ try {
+ downloadingId.value = id;
+ await downloadFile({ url, type, extension });
+ } catch (error) {
+ useAlert(t('CONVERSATION_SIDEBAR.SHARED_FILES.DOWNLOAD_ERROR'));
+ } finally {
+ downloadingId.value = null;
+ }
+};
+
+
+
+
+
+
+ {{ t('CONVERSATION_SIDEBAR.SHARED_FILES.FILES_HEADING') }}
+
+ {{ fileAttachments.length }}
+
+
+
+
+
+ -
+
+
+
+
+
+ {{ displayName(attachment) }}
+
+
+ {{ displaySize(attachment) }}
+
+ · {{ displayTime(attachment) }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/javascript/dashboard/components-next/SharedAttachments/Media.vue b/app/javascript/dashboard/components-next/SharedAttachments/Media.vue
new file mode 100644
index 000000000..6a3875583
--- /dev/null
+++ b/app/javascript/dashboard/components-next/SharedAttachments/Media.vue
@@ -0,0 +1,305 @@
+
+
+
+
+
+
+ {{ t('CONVERSATION_SIDEBAR.SHARED_FILES.MEDIA_HEADING') }}
+
+ {{ mediaAttachments.length }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ displayDuration(attachment) }}
+
+
+
+ {{ displayTime(attachment) }}
+
+
+
+
+
+
+
+
+
+ {{
+ t('CONVERSATION_SIDEBAR.SHARED_FILES.MORE_COUNT', {
+ count: overflow,
+ })
+ }}
+
+
+
+
+
+
+
diff --git a/app/javascript/dashboard/components-next/breadcrumb/Breadcrumb.vue b/app/javascript/dashboard/components-next/breadcrumb/Breadcrumb.vue
index 4e717d762..eccc4b3fa 100644
--- a/app/javascript/dashboard/components-next/breadcrumb/Breadcrumb.vue
+++ b/app/javascript/dashboard/components-next/breadcrumb/Breadcrumb.vue
@@ -2,6 +2,7 @@
import { useI18n } from 'vue-i18n';
import Icon from 'dashboard/components-next/icon/Icon.vue';
+import EmojiIcon from 'dashboard/components-next/emoji-icon-picker/EmojiIcon.vue';
defineProps({
items: {
@@ -47,8 +48,17 @@ const onClick = (item, index) => {
-
- {{ item.emoji ? item.emoji : '' }} {{ item.label }}
+
+
+ {{ item.label }}
diff --git a/app/javascript/dashboard/components-next/dropdown-menu/DropdownMenu.vue b/app/javascript/dashboard/components-next/dropdown-menu/DropdownMenu.vue
index 8fa465704..1849bf685 100644
--- a/app/javascript/dashboard/components-next/dropdown-menu/DropdownMenu.vue
+++ b/app/javascript/dashboard/components-next/dropdown-menu/DropdownMenu.vue
@@ -3,6 +3,7 @@ import { ref, computed, onMounted } from 'vue';
import { useI18n } from 'vue-i18n';
import Icon from 'dashboard/components-next/icon/Icon.vue';
+import EmojiIcon from 'dashboard/components-next/emoji-icon-picker/EmojiIcon.vue';
import Avatar from 'dashboard/components-next/avatar/Avatar.vue';
import Spinner from 'dashboard/components-next/spinner/Spinner.vue';
@@ -162,7 +163,7 @@ onMounted(() => {
>
{{ section.title }}
@@ -207,9 +208,12 @@ onMounted(() => {
class="flex-shrink-0 size-3.5"
/>
- {{
- item.emoji
- }}
+
{
class="flex-shrink-0 size-3.5"
/>
- {{ item.emoji }}
+
+import { useI18n } from 'vue-i18n';
+import { ICON_COLORS } from './constants';
+
+const { t } = useI18n();
+const selectedColor = defineModel({ type: String, default: '' });
+
+
+
+
+
+
+
diff --git a/app/javascript/dashboard/components-next/emoji-icon-picker/EmojiIcon.vue b/app/javascript/dashboard/components-next/emoji-icon-picker/EmojiIcon.vue
new file mode 100644
index 000000000..c4ca21be5
--- /dev/null
+++ b/app/javascript/dashboard/components-next/emoji-icon-picker/EmojiIcon.vue
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
diff --git a/app/javascript/dashboard/components-next/emoji-icon-picker/EmojiIconPicker.vue b/app/javascript/dashboard/components-next/emoji-icon-picker/EmojiIconPicker.vue
new file mode 100644
index 000000000..2272d1b7b
--- /dev/null
+++ b/app/javascript/dashboard/components-next/emoji-icon-picker/EmojiIconPicker.vue
@@ -0,0 +1,310 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ t('EMOJI_ICON_PICKER.NO_ICON') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ t('EMOJI_ICON_PICKER.NO_EMOJI') }}
+
+
+
+
+
diff --git a/app/javascript/dashboard/components-next/emoji-icon-picker/constants.js b/app/javascript/dashboard/components-next/emoji-icon-picker/constants.js
new file mode 100644
index 000000000..bfb28741a
--- /dev/null
+++ b/app/javascript/dashboard/components-next/emoji-icon-picker/constants.js
@@ -0,0 +1,40 @@
+// Prefix that turns a stored icon name (e.g. "rocket-line") into a class.
+// Swapping icon libraries means changing this and the curated set only.
+export const ICON_PREFIX = 'i-ri-';
+
+export const ICON_STYLE = {
+ LINE: 'line',
+ FILL: 'fill',
+};
+
+// Icon values are ascii names (e.g. "rocket-line"); emoji are non-ascii.
+export const isIconValue = value =>
+ typeof value === 'string' && /^[a-z][a-z0-9-]*$/.test(value);
+
+export const iconClassFor = value =>
+ value.startsWith(ICON_PREFIX) ? value : `${ICON_PREFIX}${value}`;
+
+export const ICON_COLORS = [
+ { name: 'SLATE', value: '#64748B' },
+ { name: 'RED', value: '#EF4444' },
+ { name: 'ORANGE', value: '#F97316' },
+ { name: 'AMBER', value: '#F59E0B' },
+ { name: 'GREEN', value: '#22C55E' },
+ { name: 'TEAL', value: '#14B8A6' },
+ { name: 'BLUE', value: '#3B82F6' },
+ { name: 'INDIGO', value: '#6366F1' },
+ { name: 'VIOLET', value: '#8B5CF6' },
+ { name: 'PINK', value: '#EC4899' },
+];
+
+export const DEFAULT_ICON_COLOR = '#3B82F6';
+
+export const PICKER_MODE = {
+ BOTH: 'both',
+ EMOJI: 'emoji',
+};
+
+export const PICKER_TAB = {
+ ICONS: 'icons',
+ EMOJIS: 'emojis',
+};
diff --git a/app/javascript/dashboard/components-next/emoji-icon-picker/icons.js b/app/javascript/dashboard/components-next/emoji-icon-picker/icons.js
new file mode 100644
index 000000000..3cf0d7b3d
--- /dev/null
+++ b/app/javascript/dashboard/components-next/emoji-icon-picker/icons.js
@@ -0,0 +1,983 @@
+// Curated Remix (ri) icons, each with a `line` and `fill` class. The class
+// literals are kept here so Tailwind generates them. { name, keywords, line, fill }
+
+export const CURATED_ICONS = [
+ {
+ name: 'home',
+ keywords: 'home house main',
+ line: 'i-ri-home-line',
+ fill: 'i-ri-home-fill',
+ },
+ {
+ name: 'star',
+ keywords: 'favorite star',
+ line: 'i-ri-star-line',
+ fill: 'i-ri-star-fill',
+ },
+ {
+ name: 'heart',
+ keywords: 'like love',
+ line: 'i-ri-heart-line',
+ fill: 'i-ri-heart-fill',
+ },
+ {
+ name: 'bookmark',
+ keywords: 'save',
+ line: 'i-ri-bookmark-line',
+ fill: 'i-ri-bookmark-fill',
+ },
+ {
+ name: 'flag-2',
+ keywords: 'flag report',
+ line: 'i-ri-flag-2-line',
+ fill: 'i-ri-flag-2-fill',
+ },
+ {
+ name: 'price-tag-3',
+ keywords: 'tag label',
+ line: 'i-ri-price-tag-3-line',
+ fill: 'i-ri-price-tag-3-fill',
+ },
+ {
+ name: 'map-pin',
+ keywords: 'location place',
+ line: 'i-ri-map-pin-line',
+ fill: 'i-ri-map-pin-fill',
+ },
+ {
+ name: 'sparkling-2',
+ keywords: 'magic ai shine sparkle',
+ line: 'i-ri-sparkling-2-line',
+ fill: 'i-ri-sparkling-2-fill',
+ },
+ {
+ name: 'fire',
+ keywords: 'flame hot trending',
+ line: 'i-ri-fire-line',
+ fill: 'i-ri-fire-fill',
+ },
+ {
+ name: 'lightbulb',
+ keywords: 'idea tip',
+ line: 'i-ri-lightbulb-line',
+ fill: 'i-ri-lightbulb-fill',
+ },
+ {
+ name: 'rocket',
+ keywords: 'launch startup ship',
+ line: 'i-ri-rocket-line',
+ fill: 'i-ri-rocket-fill',
+ },
+ {
+ name: 'focus-3',
+ keywords: 'target goal aim',
+ line: 'i-ri-focus-3-line',
+ fill: 'i-ri-focus-3-fill',
+ },
+ {
+ name: 'award',
+ keywords: 'prize badge medal',
+ line: 'i-ri-award-line',
+ fill: 'i-ri-award-fill',
+ },
+ {
+ name: 'trophy',
+ keywords: 'win achievement',
+ line: 'i-ri-trophy-line',
+ fill: 'i-ri-trophy-fill',
+ },
+ {
+ name: 'vip-crown-2',
+ keywords: 'crown premium vip',
+ line: 'i-ri-vip-crown-2-line',
+ fill: 'i-ri-vip-crown-2-fill',
+ },
+ {
+ name: 'vip-diamond',
+ keywords: 'gem diamond premium',
+ line: 'i-ri-vip-diamond-line',
+ fill: 'i-ri-vip-diamond-fill',
+ },
+ {
+ name: 'gift',
+ keywords: 'present reward',
+ line: 'i-ri-gift-line',
+ fill: 'i-ri-gift-fill',
+ },
+ {
+ name: 'notification-3',
+ keywords: 'bell alert',
+ line: 'i-ri-notification-3-line',
+ fill: 'i-ri-notification-3-fill',
+ },
+ {
+ name: 'global',
+ keywords: 'globe world web',
+ line: 'i-ri-global-line',
+ fill: 'i-ri-global-fill',
+ },
+ {
+ name: 'compass-3',
+ keywords: 'explore discover',
+ line: 'i-ri-compass-3-line',
+ fill: 'i-ri-compass-3-fill',
+ },
+ {
+ name: 'puzzle-2',
+ keywords: 'extension integration',
+ line: 'i-ri-puzzle-2-line',
+ fill: 'i-ri-puzzle-2-fill',
+ },
+ {
+ name: 'ticket-2',
+ keywords: 'support coupon',
+ line: 'i-ri-ticket-2-line',
+ fill: 'i-ri-ticket-2-fill',
+ },
+ {
+ name: 'megaphone',
+ keywords: 'announce broadcast marketing',
+ line: 'i-ri-megaphone-line',
+ fill: 'i-ri-megaphone-fill',
+ },
+ {
+ name: 'briefcase-4',
+ keywords: 'work job business',
+ line: 'i-ri-briefcase-4-line',
+ fill: 'i-ri-briefcase-4-fill',
+ },
+ {
+ name: 'building-2',
+ keywords: 'company office org',
+ line: 'i-ri-building-2-line',
+ fill: 'i-ri-building-2-fill',
+ },
+ {
+ name: 'store-2',
+ keywords: 'shop commerce',
+ line: 'i-ri-store-2-line',
+ fill: 'i-ri-store-2-fill',
+ },
+ {
+ name: 'community',
+ keywords: 'group team',
+ line: 'i-ri-community-line',
+ fill: 'i-ri-community-fill',
+ },
+ {
+ name: 'folder',
+ keywords: 'directory',
+ line: 'i-ri-folder-line',
+ fill: 'i-ri-folder-fill',
+ },
+ {
+ name: 'folder-open',
+ keywords: 'directory open',
+ line: 'i-ri-folder-open-line',
+ fill: 'i-ri-folder-open-fill',
+ },
+ {
+ name: 'file',
+ keywords: 'document',
+ line: 'i-ri-file-line',
+ fill: 'i-ri-file-fill',
+ },
+ {
+ name: 'file-text',
+ keywords: 'document doc',
+ line: 'i-ri-file-text-line',
+ fill: 'i-ri-file-text-fill',
+ },
+ {
+ name: 'file-copy',
+ keywords: 'documents files',
+ line: 'i-ri-file-copy-line',
+ fill: 'i-ri-file-copy-fill',
+ },
+ {
+ name: 'clipboard',
+ keywords: 'copy tasks',
+ line: 'i-ri-clipboard-line',
+ fill: 'i-ri-clipboard-fill',
+ },
+ {
+ name: 'book-2',
+ keywords: 'read guide book',
+ line: 'i-ri-book-2-line',
+ fill: 'i-ri-book-2-fill',
+ },
+ {
+ name: 'book-open',
+ keywords: 'read guide docs',
+ line: 'i-ri-book-open-line',
+ fill: 'i-ri-book-open-fill',
+ },
+ {
+ name: 'graduation-cap',
+ keywords: 'learn course education',
+ line: 'i-ri-graduation-cap-line',
+ fill: 'i-ri-graduation-cap-fill',
+ },
+ {
+ name: 'newspaper',
+ keywords: 'news article blog',
+ line: 'i-ri-newspaper-line',
+ fill: 'i-ri-newspaper-fill',
+ },
+ {
+ name: 'sticky-note',
+ keywords: 'note memo',
+ line: 'i-ri-sticky-note-line',
+ fill: 'i-ri-sticky-note-fill',
+ },
+ {
+ name: 'pencil',
+ keywords: 'write edit',
+ line: 'i-ri-pencil-line',
+ fill: 'i-ri-pencil-fill',
+ },
+ {
+ name: 'quill-pen',
+ keywords: 'write author',
+ line: 'i-ri-quill-pen-line',
+ fill: 'i-ri-quill-pen-fill',
+ },
+ {
+ name: 'mark-pen',
+ keywords: 'highlight',
+ line: 'i-ri-mark-pen-line',
+ fill: 'i-ri-mark-pen-fill',
+ },
+ {
+ name: 'calendar-2',
+ keywords: 'date schedule calendar',
+ line: 'i-ri-calendar-2-line',
+ fill: 'i-ri-calendar-2-fill',
+ },
+ {
+ name: 'calendar-event',
+ keywords: 'schedule event',
+ line: 'i-ri-calendar-event-line',
+ fill: 'i-ri-calendar-event-fill',
+ },
+ {
+ name: 'time',
+ keywords: 'clock time',
+ line: 'i-ri-time-line',
+ fill: 'i-ri-time-fill',
+ },
+ {
+ name: 'timer',
+ keywords: 'countdown',
+ line: 'i-ri-timer-line',
+ fill: 'i-ri-timer-fill',
+ },
+ {
+ name: 'history',
+ keywords: 'recent past',
+ line: 'i-ri-history-line',
+ fill: 'i-ri-history-fill',
+ },
+ {
+ name: 'hourglass',
+ keywords: 'wait time',
+ line: 'i-ri-hourglass-line',
+ fill: 'i-ri-hourglass-fill',
+ },
+ {
+ name: 'shopping-cart-2',
+ keywords: 'buy ecommerce cart',
+ line: 'i-ri-shopping-cart-2-line',
+ fill: 'i-ri-shopping-cart-2-fill',
+ },
+ {
+ name: 'shopping-bag-3',
+ keywords: 'buy purchase bag',
+ line: 'i-ri-shopping-bag-3-line',
+ fill: 'i-ri-shopping-bag-3-fill',
+ },
+ {
+ name: 'box-3',
+ keywords: 'package box shipping',
+ line: 'i-ri-box-3-line',
+ fill: 'i-ri-box-3-fill',
+ },
+ {
+ name: 'archive',
+ keywords: 'store box archive',
+ line: 'i-ri-archive-line',
+ fill: 'i-ri-archive-fill',
+ },
+ {
+ name: 'bank-card',
+ keywords: 'pay billing card credit',
+ line: 'i-ri-bank-card-line',
+ fill: 'i-ri-bank-card-fill',
+ },
+ {
+ name: 'wallet-3',
+ keywords: 'money pay wallet',
+ line: 'i-ri-wallet-3-line',
+ fill: 'i-ri-wallet-3-fill',
+ },
+ {
+ name: 'money-dollar-circle',
+ keywords: 'money price usd',
+ line: 'i-ri-money-dollar-circle-line',
+ fill: 'i-ri-money-dollar-circle-fill',
+ },
+ {
+ name: 'copper-coin',
+ keywords: 'coins money',
+ line: 'i-ri-copper-coin-line',
+ fill: 'i-ri-copper-coin-fill',
+ },
+ {
+ name: 'currency',
+ keywords: 'money exchange',
+ line: 'i-ri-currency-line',
+ fill: 'i-ri-currency-fill',
+ },
+ {
+ name: 'receipt',
+ keywords: 'invoice bill',
+ line: 'i-ri-receipt-line',
+ fill: 'i-ri-receipt-fill',
+ },
+ {
+ name: 'percent',
+ keywords: 'discount sale',
+ line: 'i-ri-percent-line',
+ fill: 'i-ri-percent-fill',
+ },
+ {
+ name: 'calculator',
+ keywords: 'math',
+ line: 'i-ri-calculator-line',
+ fill: 'i-ri-calculator-fill',
+ },
+ {
+ name: 'mail',
+ keywords: 'email message',
+ line: 'i-ri-mail-line',
+ fill: 'i-ri-mail-fill',
+ },
+ {
+ name: 'send-plane',
+ keywords: 'send submit',
+ line: 'i-ri-send-plane-line',
+ fill: 'i-ri-send-plane-fill',
+ },
+ {
+ name: 'chat-3',
+ keywords: 'chat talk message',
+ line: 'i-ri-chat-3-line',
+ fill: 'i-ri-chat-3-fill',
+ },
+ {
+ name: 'message-2',
+ keywords: 'chat comment',
+ line: 'i-ri-message-2-line',
+ fill: 'i-ri-message-2-fill',
+ },
+ {
+ name: 'question-answer',
+ keywords: 'chat conversation faq',
+ line: 'i-ri-question-answer-line',
+ fill: 'i-ri-question-answer-fill',
+ },
+ {
+ name: 'phone',
+ keywords: 'call',
+ line: 'i-ri-phone-line',
+ fill: 'i-ri-phone-fill',
+ },
+ {
+ name: 'vidicon',
+ keywords: 'video call meeting',
+ line: 'i-ri-vidicon-line',
+ fill: 'i-ri-vidicon-fill',
+ },
+ {
+ name: 'mic',
+ keywords: 'record voice audio',
+ line: 'i-ri-mic-line',
+ fill: 'i-ri-mic-fill',
+ },
+ {
+ name: 'group',
+ keywords: 'team people users',
+ line: 'i-ri-group-line',
+ fill: 'i-ri-group-fill',
+ },
+ {
+ name: 'user',
+ keywords: 'person account',
+ line: 'i-ri-user-line',
+ fill: 'i-ri-user-fill',
+ },
+ {
+ name: 'user-add',
+ keywords: 'add member',
+ line: 'i-ri-user-add-line',
+ fill: 'i-ri-user-add-fill',
+ },
+ {
+ name: 'contacts',
+ keywords: 'person card contact',
+ line: 'i-ri-contacts-line',
+ fill: 'i-ri-contacts-fill',
+ },
+ {
+ name: 'emotion-happy',
+ keywords: 'happy smile emoji',
+ line: 'i-ri-emotion-happy-line',
+ fill: 'i-ri-emotion-happy-fill',
+ },
+ {
+ name: 'thumb-up',
+ keywords: 'like approve',
+ line: 'i-ri-thumb-up-line',
+ fill: 'i-ri-thumb-up-fill',
+ },
+ {
+ name: 'hand-heart',
+ keywords: 'care support',
+ line: 'i-ri-hand-heart-line',
+ fill: 'i-ri-hand-heart-fill',
+ },
+ {
+ name: 'at',
+ keywords: 'mention email at',
+ line: 'i-ri-at-line',
+ fill: 'i-ri-at-fill',
+ },
+ {
+ name: 'image-2',
+ keywords: 'picture photo image',
+ line: 'i-ri-image-2-line',
+ fill: 'i-ri-image-2-fill',
+ },
+ {
+ name: 'camera-3',
+ keywords: 'photo camera',
+ line: 'i-ri-camera-3-line',
+ fill: 'i-ri-camera-3-fill',
+ },
+ {
+ name: 'film',
+ keywords: 'movie video',
+ line: 'i-ri-film-line',
+ fill: 'i-ri-film-fill',
+ },
+ {
+ name: 'music-2',
+ keywords: 'audio song music',
+ line: 'i-ri-music-2-line',
+ fill: 'i-ri-music-2-fill',
+ },
+ {
+ name: 'headphone',
+ keywords: 'audio support',
+ line: 'i-ri-headphone-line',
+ fill: 'i-ri-headphone-fill',
+ },
+ {
+ name: 'palette',
+ keywords: 'color design art',
+ line: 'i-ri-palette-line',
+ fill: 'i-ri-palette-fill',
+ },
+ {
+ name: 'brush-3',
+ keywords: 'paint design brush',
+ line: 'i-ri-brush-3-line',
+ fill: 'i-ri-brush-3-fill',
+ },
+ {
+ name: 'scissors-2',
+ keywords: 'cut',
+ line: 'i-ri-scissors-2-line',
+ fill: 'i-ri-scissors-2-fill',
+ },
+ {
+ name: 'code-s-slash',
+ keywords: 'develop code programming',
+ line: 'i-ri-code-s-slash-line',
+ fill: 'i-ri-code-s-slash-fill',
+ },
+ {
+ name: 'terminal-box',
+ keywords: 'console cli terminal',
+ line: 'i-ri-terminal-box-line',
+ fill: 'i-ri-terminal-box-fill',
+ },
+ {
+ name: 'git-branch',
+ keywords: 'version code git',
+ line: 'i-ri-git-branch-line',
+ fill: 'i-ri-git-branch-fill',
+ },
+ {
+ name: 'github',
+ keywords: 'git code',
+ line: 'i-ri-github-line',
+ fill: 'i-ri-github-fill',
+ },
+ {
+ name: 'cpu',
+ keywords: 'chip processor',
+ line: 'i-ri-cpu-line',
+ fill: 'i-ri-cpu-fill',
+ },
+ {
+ name: 'database-2',
+ keywords: 'storage data',
+ line: 'i-ri-database-2-line',
+ fill: 'i-ri-database-2-fill',
+ },
+ {
+ name: 'server',
+ keywords: 'host backend',
+ line: 'i-ri-server-line',
+ fill: 'i-ri-server-fill',
+ },
+ {
+ name: 'cloud',
+ keywords: 'storage cloud',
+ line: 'i-ri-cloud-line',
+ fill: 'i-ri-cloud-fill',
+ },
+ {
+ name: 'wifi',
+ keywords: 'network internet',
+ line: 'i-ri-wifi-line',
+ fill: 'i-ri-wifi-fill',
+ },
+ {
+ name: 'computer',
+ keywords: 'screen desktop monitor',
+ line: 'i-ri-computer-line',
+ fill: 'i-ri-computer-fill',
+ },
+ {
+ name: 'smartphone',
+ keywords: 'mobile phone',
+ line: 'i-ri-smartphone-line',
+ fill: 'i-ri-smartphone-fill',
+ },
+ {
+ name: 'macbook',
+ keywords: 'laptop computer',
+ line: 'i-ri-macbook-line',
+ fill: 'i-ri-macbook-fill',
+ },
+ {
+ name: 'bug',
+ keywords: 'issue error bug',
+ line: 'i-ri-bug-line',
+ fill: 'i-ri-bug-fill',
+ },
+ {
+ name: 'command',
+ keywords: 'keyboard shortcut',
+ line: 'i-ri-command-line',
+ fill: 'i-ri-command-fill',
+ },
+ {
+ name: 'settings-3',
+ keywords: 'config gear options settings',
+ line: 'i-ri-settings-3-line',
+ fill: 'i-ri-settings-3-fill',
+ },
+ {
+ name: 'equalizer',
+ keywords: 'controls adjust sliders filter',
+ line: 'i-ri-equalizer-line',
+ fill: 'i-ri-equalizer-fill',
+ },
+ {
+ name: 'tools',
+ keywords: 'fix tools config',
+ line: 'i-ri-tools-line',
+ fill: 'i-ri-tools-fill',
+ },
+ {
+ name: 'hammer',
+ keywords: 'build fix hammer',
+ line: 'i-ri-hammer-line',
+ fill: 'i-ri-hammer-fill',
+ },
+ {
+ name: 'key-2',
+ keywords: 'password access key',
+ line: 'i-ri-key-2-line',
+ fill: 'i-ri-key-2-fill',
+ },
+ {
+ name: 'lock',
+ keywords: 'secure private lock',
+ line: 'i-ri-lock-line',
+ fill: 'i-ri-lock-fill',
+ },
+ {
+ name: 'lock-unlock',
+ keywords: 'open access',
+ line: 'i-ri-lock-unlock-line',
+ fill: 'i-ri-lock-unlock-fill',
+ },
+ {
+ name: 'shield',
+ keywords: 'security protect',
+ line: 'i-ri-shield-line',
+ fill: 'i-ri-shield-fill',
+ },
+ {
+ name: 'shield-check',
+ keywords: 'secure verified',
+ line: 'i-ri-shield-check-line',
+ fill: 'i-ri-shield-check-fill',
+ },
+ {
+ name: 'eye',
+ keywords: 'view visible preview',
+ line: 'i-ri-eye-line',
+ fill: 'i-ri-eye-fill',
+ },
+ {
+ name: 'search',
+ keywords: 'find lookup search',
+ line: 'i-ri-search-line',
+ fill: 'i-ri-search-fill',
+ },
+ {
+ name: 'filter-3',
+ keywords: 'sort refine filter',
+ line: 'i-ri-filter-3-line',
+ fill: 'i-ri-filter-3-fill',
+ },
+ {
+ name: 'links',
+ keywords: 'url chain link',
+ line: 'i-ri-links-line',
+ fill: 'i-ri-links-fill',
+ },
+ {
+ name: 'share-forward',
+ keywords: 'share send distribute',
+ line: 'i-ri-share-forward-line',
+ fill: 'i-ri-share-forward-fill',
+ },
+ {
+ name: 'plug',
+ keywords: 'connect integration',
+ line: 'i-ri-plug-line',
+ fill: 'i-ri-plug-fill',
+ },
+ {
+ name: 'shut-down',
+ keywords: 'power on off',
+ line: 'i-ri-shut-down-line',
+ fill: 'i-ri-shut-down-fill',
+ },
+ {
+ name: 'qr-scan-2',
+ keywords: 'qr scan',
+ line: 'i-ri-qr-scan-2-line',
+ fill: 'i-ri-qr-scan-2-fill',
+ },
+ {
+ name: 'fingerprint',
+ keywords: 'identity auth fingerprint',
+ line: 'i-ri-fingerprint-line',
+ fill: 'i-ri-fingerprint-fill',
+ },
+ {
+ name: 'bar-chart',
+ keywords: 'analytics stats graph bar',
+ line: 'i-ri-bar-chart-line',
+ fill: 'i-ri-bar-chart-fill',
+ },
+ {
+ name: 'pie-chart',
+ keywords: 'analytics stats pie',
+ line: 'i-ri-pie-chart-line',
+ fill: 'i-ri-pie-chart-fill',
+ },
+ {
+ name: 'line-chart',
+ keywords: 'analytics trend graph line',
+ line: 'i-ri-line-chart-line',
+ fill: 'i-ri-line-chart-fill',
+ },
+ {
+ name: 'stock',
+ keywords: 'growth increase trending',
+ line: 'i-ri-stock-line',
+ fill: 'i-ri-stock-fill',
+ },
+ {
+ name: 'pulse',
+ keywords: 'activity health monitor',
+ line: 'i-ri-pulse-line',
+ fill: 'i-ri-pulse-fill',
+ },
+ {
+ name: 'dashboard-3',
+ keywords: 'speed performance dashboard gauge',
+ line: 'i-ri-dashboard-3-line',
+ fill: 'i-ri-dashboard-3-fill',
+ },
+ {
+ name: 'check',
+ keywords: 'done complete ok check',
+ line: 'i-ri-check-line',
+ fill: 'i-ri-check-fill',
+ },
+ {
+ name: 'checkbox-circle',
+ keywords: 'done success check',
+ line: 'i-ri-checkbox-circle-line',
+ fill: 'i-ri-checkbox-circle-fill',
+ },
+ {
+ name: 'close-circle',
+ keywords: 'error fail close',
+ line: 'i-ri-close-circle-line',
+ fill: 'i-ri-close-circle-fill',
+ },
+ {
+ name: 'information',
+ keywords: 'info information',
+ line: 'i-ri-information-line',
+ fill: 'i-ri-information-fill',
+ },
+ {
+ name: 'error-warning',
+ keywords: 'warning alert',
+ line: 'i-ri-error-warning-line',
+ fill: 'i-ri-error-warning-fill',
+ },
+ {
+ name: 'alert',
+ keywords: 'warning caution',
+ line: 'i-ri-alert-line',
+ fill: 'i-ri-alert-fill',
+ },
+ {
+ name: 'question',
+ keywords: 'support faq help',
+ line: 'i-ri-question-line',
+ fill: 'i-ri-question-fill',
+ },
+ {
+ name: 'layout-grid',
+ keywords: 'dashboard apps grid',
+ line: 'i-ri-layout-grid-line',
+ fill: 'i-ri-layout-grid-fill',
+ },
+ {
+ name: 'table',
+ keywords: 'grid data table',
+ line: 'i-ri-table-line',
+ fill: 'i-ri-table-fill',
+ },
+ {
+ name: 'function',
+ keywords: 'kanban board',
+ line: 'i-ri-function-line',
+ fill: 'i-ri-function-fill',
+ },
+ {
+ name: 'map-2',
+ keywords: 'location navigate map',
+ line: 'i-ri-map-2-line',
+ fill: 'i-ri-map-2-fill',
+ },
+ {
+ name: 'navigation',
+ keywords: 'direction gps',
+ line: 'i-ri-navigation-line',
+ fill: 'i-ri-navigation-fill',
+ },
+ {
+ name: 'car',
+ keywords: 'drive vehicle car',
+ line: 'i-ri-car-line',
+ fill: 'i-ri-car-fill',
+ },
+ {
+ name: 'truck',
+ keywords: 'delivery shipping',
+ line: 'i-ri-truck-line',
+ fill: 'i-ri-truck-fill',
+ },
+ {
+ name: 'plane',
+ keywords: 'travel flight plane',
+ line: 'i-ri-plane-line',
+ fill: 'i-ri-plane-fill',
+ },
+ {
+ name: 'bike',
+ keywords: 'cycle bike',
+ line: 'i-ri-bike-line',
+ fill: 'i-ri-bike-fill',
+ },
+ {
+ name: 'anchor',
+ keywords: 'ship marine anchor',
+ line: 'i-ri-anchor-line',
+ fill: 'i-ri-anchor-fill',
+ },
+ {
+ name: 'leaf',
+ keywords: 'eco nature green leaf',
+ line: 'i-ri-leaf-line',
+ fill: 'i-ri-leaf-fill',
+ },
+ {
+ name: 'plant',
+ keywords: 'nature plant grow',
+ line: 'i-ri-plant-line',
+ fill: 'i-ri-plant-fill',
+ },
+ {
+ name: 'sun',
+ keywords: 'day light weather sun',
+ line: 'i-ri-sun-line',
+ fill: 'i-ri-sun-fill',
+ },
+ {
+ name: 'moon',
+ keywords: 'night dark moon',
+ line: 'i-ri-moon-line',
+ fill: 'i-ri-moon-fill',
+ },
+ {
+ name: 'rainy',
+ keywords: 'weather rain',
+ line: 'i-ri-rainy-line',
+ fill: 'i-ri-rainy-fill',
+ },
+ {
+ name: 'umbrella',
+ keywords: 'rain protect umbrella',
+ line: 'i-ri-umbrella-line',
+ fill: 'i-ri-umbrella-fill',
+ },
+ {
+ name: 'landscape',
+ keywords: 'outdoor hike mountain',
+ line: 'i-ri-landscape-line',
+ fill: 'i-ri-landscape-fill',
+ },
+ {
+ name: 'drop',
+ keywords: 'water drop',
+ line: 'i-ri-drop-line',
+ fill: 'i-ri-drop-fill',
+ },
+ {
+ name: 'cup',
+ keywords: 'coffee drink cup',
+ line: 'i-ri-cup-line',
+ fill: 'i-ri-cup-fill',
+ },
+ {
+ name: 'restaurant',
+ keywords: 'food eat restaurant',
+ line: 'i-ri-restaurant-line',
+ fill: 'i-ri-restaurant-fill',
+ },
+ {
+ name: 'cake-3',
+ keywords: 'birthday celebrate cake',
+ line: 'i-ri-cake-3-line',
+ fill: 'i-ri-cake-3-fill',
+ },
+ {
+ name: 'goblet',
+ keywords: 'drink wine bar',
+ line: 'i-ri-goblet-line',
+ fill: 'i-ri-goblet-fill',
+ },
+ {
+ name: 'heart-pulse',
+ keywords: 'health medical',
+ line: 'i-ri-heart-pulse-line',
+ fill: 'i-ri-heart-pulse-fill',
+ },
+ {
+ name: 'stethoscope',
+ keywords: 'health doctor medical',
+ line: 'i-ri-stethoscope-line',
+ fill: 'i-ri-stethoscope-fill',
+ },
+ {
+ name: 'capsule',
+ keywords: 'medicine pill health',
+ line: 'i-ri-capsule-line',
+ fill: 'i-ri-capsule-fill',
+ },
+ {
+ name: 'add',
+ keywords: 'add new create plus',
+ line: 'i-ri-add-line',
+ fill: 'i-ri-add-fill',
+ },
+ {
+ name: 'delete-bin',
+ keywords: 'delete remove trash',
+ line: 'i-ri-delete-bin-line',
+ fill: 'i-ri-delete-bin-fill',
+ },
+ {
+ name: 'download',
+ keywords: 'save export download',
+ line: 'i-ri-download-line',
+ fill: 'i-ri-download-fill',
+ },
+ {
+ name: 'upload',
+ keywords: 'import upload',
+ line: 'i-ri-upload-line',
+ fill: 'i-ri-upload-fill',
+ },
+ {
+ name: 'refresh',
+ keywords: 'reload sync update refresh',
+ line: 'i-ri-refresh-line',
+ fill: 'i-ri-refresh-fill',
+ },
+ {
+ name: 'external-link',
+ keywords: 'open new external',
+ line: 'i-ri-external-link-line',
+ fill: 'i-ri-external-link-fill',
+ },
+ {
+ name: 'logout-box',
+ keywords: 'exit signout logout',
+ line: 'i-ri-logout-box-line',
+ fill: 'i-ri-logout-box-fill',
+ },
+ {
+ name: 'fullscreen',
+ keywords: 'expand maximize fullscreen',
+ line: 'i-ri-fullscreen-line',
+ fill: 'i-ri-fullscreen-fill',
+ },
+ {
+ name: 'drag-move',
+ keywords: 'drag reorder move',
+ line: 'i-ri-drag-move-line',
+ fill: 'i-ri-drag-move-fill',
+ },
+ {
+ name: 'paint-brush',
+ keywords: 'brush paint',
+ line: 'i-ri-paint-brush-line',
+ fill: 'i-ri-paint-brush-fill',
+ },
+];
diff --git a/app/javascript/dashboard/components-next/message/constants.js b/app/javascript/dashboard/components-next/message/constants.js
index 4f8b4f23c..517c083a1 100644
--- a/app/javascript/dashboard/components-next/message/constants.js
+++ b/app/javascript/dashboard/components-next/message/constants.js
@@ -78,6 +78,12 @@ export const MEDIA_TYPES = [
ATTACHMENT_TYPES.IG_REEL,
];
+export const NON_FILE_TYPES = [
+ ATTACHMENT_TYPES.LOCATION,
+ ATTACHMENT_TYPES.FALLBACK,
+ ATTACHMENT_TYPES.CONTACT,
+];
+
export const VOICE_CALL_STATUS = {
IN_PROGRESS: 'in-progress',
RINGING: 'ringing',
diff --git a/app/javascript/dashboard/components-next/sidebar/Sidebar.vue b/app/javascript/dashboard/components-next/sidebar/Sidebar.vue
index f71652ca0..15a007a74 100644
--- a/app/javascript/dashboard/components-next/sidebar/Sidebar.vue
+++ b/app/javascript/dashboard/components-next/sidebar/Sidebar.vue
@@ -21,6 +21,12 @@ import ChannelIcon from 'next/icon/ChannelIcon.vue';
import SidebarAccountSwitcher from './SidebarAccountSwitcher.vue';
import Logo from 'next/icon/Logo.vue';
import ComposeConversation from 'dashboard/components-next/NewConversation/ComposeConversation.vue';
+import {
+ SIDEBAR_SORT_SECTIONS,
+ getSidebarSortOptions,
+ resolveSidebarSort,
+ sortSidebarItems,
+} from 'dashboard/helper/sidebarSort';
const props = defineProps({
isMobileSidebarOpen: {
@@ -50,6 +56,7 @@ const { width: windowWidth } = useWindowSize();
const isMobile = computed(() => windowWidth.value < 768);
const accountId = useMapGetter('getCurrentAccountId');
+const currentUserId = useMapGetter('getCurrentUserID');
const isFeatureEnabledonAccount = useMapGetter(
'accounts/isFeatureEnabledonAccount'
);
@@ -79,6 +86,11 @@ const fetchConversationUnreadCounts = ([currentAccountId, isEnabled]) => {
store.dispatch('conversationUnreadCounts/get');
};
+const fetchSidebarSortPreferences = ([currentAccountId, userId]) => {
+ if (!currentAccountId || !userId) return;
+ store.dispatch('sidebarSortPreferences/initialize');
+};
+
const toggleShortcutModalFn = show => {
if (show) {
emit('openKeyShortcutModal');
@@ -192,6 +204,9 @@ const contactCustomViews = useMapGetter('customViews/getContactCustomViews');
const conversationCustomViews = useMapGetter(
'customViews/getConversationCustomViews'
);
+const getSidebarSectionSort = useMapGetter(
+ 'sidebarSortPreferences/getSectionSort'
+);
onMounted(() => {
store.dispatch('labels/get');
@@ -207,44 +222,62 @@ watch([accountId, hasConversationUnreadCounts], fetchConversationUnreadCounts, {
immediate: true,
});
-const normalizeUnreadCount = count => {
- const unreadCount = Number(count);
- return Number.isFinite(unreadCount) && unreadCount > 0 ? unreadCount : 0;
-};
+watch([accountId, currentUserId], fetchSidebarSortPreferences, {
+ immediate: true,
+});
-const sortByUnreadCount = (items, labelKey, unreadCountKey) =>
- items.slice().sort((a, b) => {
- const unreadCountDiff =
- normalizeUnreadCount(unreadCountKey(b)) -
- normalizeUnreadCount(unreadCountKey(a));
-
- if (unreadCountDiff !== 0) return unreadCountDiff;
-
- return labelKey(a).localeCompare(labelKey(b));
+const getSortOptionsForSection = section =>
+ getSidebarSortOptions(section, {
+ hasUnreadCounts: hasConversationUnreadCounts.value,
});
+const getSortForSection = section =>
+ resolveSidebarSort(section, getSidebarSectionSort.value(section), {
+ hasUnreadCounts: hasConversationUnreadCounts.value,
+ });
+
+const updateSortPreference = (section, sortBy) => {
+ store.dispatch('sidebarSortPreferences/setSectionSort', {
+ section,
+ sortBy,
+ });
+};
+
+const buildSortConfig = section => ({
+ sortOptions: getSortOptionsForSection(section),
+ activeSort: getSortForSection(section),
+ onSortChange: sortBy => updateSortPreference(section, sortBy),
+});
+
+const sortedFolders = computed(() =>
+ sortSidebarItems(conversationCustomViews.value, {
+ sortBy: getSortForSection(SIDEBAR_SORT_SECTIONS.FOLDERS),
+ labelKey: view => view.name,
+ })
+);
+
const sortedTeams = computed(() =>
- sortByUnreadCount(
- teams.value,
- team => team.name,
- team => getTeamUnreadCount.value(team.id)
- )
+ sortSidebarItems(teams.value, {
+ sortBy: getSortForSection(SIDEBAR_SORT_SECTIONS.TEAMS),
+ labelKey: team => team.name,
+ unreadCountKey: team => getTeamUnreadCount.value(team.id),
+ })
);
const sortedInboxes = computed(() =>
- sortByUnreadCount(
- inboxes.value,
- inbox => inbox.name,
- inbox => getInboxUnreadCount.value(inbox.id)
- )
+ sortSidebarItems(inboxes.value, {
+ sortBy: getSortForSection(SIDEBAR_SORT_SECTIONS.CHANNELS),
+ labelKey: inbox => inbox.name,
+ unreadCountKey: inbox => getInboxUnreadCount.value(inbox.id),
+ })
);
const sortedLabels = computed(() =>
- sortByUnreadCount(
- labels.value,
- label => label.title,
- label => getLabelUnreadCount.value(label.id)
- )
+ sortSidebarItems(labels.value, {
+ sortBy: getSortForSection(SIDEBAR_SORT_SECTIONS.LABELS),
+ labelKey: label => label.title,
+ unreadCountKey: label => getLabelUnreadCount.value(label.id),
+ })
);
const closeMobileSidebar = () => {
@@ -300,6 +333,7 @@ const menuItems = computed(() => {
{
name: 'All',
label: t('SIDEBAR.ALL_CONVERSATIONS'),
+ icon: 'i-lucide-inbox',
badgeCount: allUnreadCount.value,
activeOn: ['inbox_conversation'],
to: accountScopedRoute('home'),
@@ -307,12 +341,14 @@ const menuItems = computed(() => {
{
name: 'Mentions',
label: t('SIDEBAR.MENTIONED_CONVERSATIONS'),
+ icon: 'i-lucide-at-sign',
activeOn: ['conversation_through_mentions'],
to: accountScopedRoute('conversation_mentions'),
},
{
name: 'Participating',
label: t('SIDEBAR.PARTICIPATING_CONVERSATIONS'),
+ icon: 'i-lucide-user-round-check',
activeOn: ['conversation_through_participating'],
to: accountScopedRoute('conversation_participating'),
},
@@ -320,6 +356,7 @@ const menuItems = computed(() => {
name: 'Unattended',
activeOn: ['conversation_through_unattended'],
label: t('SIDEBAR.UNATTENDED_CONVERSATIONS'),
+ icon: 'i-lucide-clock-alert',
to: accountScopedRoute('conversation_unattended'),
},
{
@@ -327,7 +364,10 @@ const menuItems = computed(() => {
label: t('SIDEBAR.CUSTOM_VIEWS_FOLDER'),
icon: 'i-lucide-folder',
activeOn: ['conversations_through_folders'],
- children: conversationCustomViews.value.map(view => ({
+ ...buildSortConfig(SIDEBAR_SORT_SECTIONS.FOLDERS),
+ collapsible: true,
+ showTreeLine: true,
+ children: sortedFolders.value.map(view => ({
name: `${view.name}-${view.id}`,
label: view.name,
to: accountScopedRoute('folder_conversations', { id: view.id }),
@@ -338,6 +378,9 @@ const menuItems = computed(() => {
label: t('SIDEBAR.TEAMS'),
icon: 'i-lucide-users',
activeOn: ['conversations_through_team'],
+ ...buildSortConfig(SIDEBAR_SORT_SECTIONS.TEAMS),
+ collapsible: true,
+ showTreeLine: true,
children: sortedTeams.value.map(team => ({
name: `${team.name}-${team.id}`,
label: team.name,
@@ -350,6 +393,9 @@ const menuItems = computed(() => {
label: t('SIDEBAR.CHANNELS'),
icon: 'i-lucide-mailbox',
activeOn: ['conversation_through_inbox'],
+ ...buildSortConfig(SIDEBAR_SORT_SECTIONS.CHANNELS),
+ collapsible: true,
+ showTreeLine: true,
children: sortedInboxes.value.map(inbox => ({
name: `${inbox.name}-${inbox.id}`,
label: inbox.name,
@@ -370,6 +416,9 @@ const menuItems = computed(() => {
label: t('SIDEBAR.LABELS'),
icon: 'i-lucide-tag',
activeOn: ['conversations_through_label'],
+ ...buildSortConfig(SIDEBAR_SORT_SECTIONS.LABELS),
+ collapsible: true,
+ showTreeLine: true,
children: sortedLabels.value.map(label => ({
name: `${label.title}-${label.id}`,
label: label.title,
@@ -481,6 +530,8 @@ const menuItems = computed(() => {
name: 'Segments',
icon: 'i-lucide-group',
label: t('SIDEBAR.CUSTOM_VIEWS_SEGMENTS'),
+ collapsible: true,
+ showTreeLine: true,
children: contactCustomViews.value.map(view => ({
name: `${view.name}-${view.id}`,
label: view.name,
@@ -499,6 +550,8 @@ const menuItems = computed(() => {
name: 'Tagged With',
icon: 'i-lucide-tag',
label: t('SIDEBAR.TAGGED_WITH'),
+ collapsible: true,
+ showTreeLine: true,
children: labels.value.map(label => ({
name: `${label.title}-${label.id}`,
label: label.title,
diff --git a/app/javascript/dashboard/components-next/sidebar/SidebarCollapsedPopover.vue b/app/javascript/dashboard/components-next/sidebar/SidebarCollapsedPopover.vue
index 18e1aea23..2e431eb7e 100644
--- a/app/javascript/dashboard/components-next/sidebar/SidebarCollapsedPopover.vue
+++ b/app/javascript/dashboard/components-next/sidebar/SidebarCollapsedPopover.vue
@@ -1,11 +1,13 @@
-
import Icon from 'next/icon/Icon.vue';
+import SidebarSortMenu from './SidebarSortMenu.vue';
defineProps({
+ collapsible: {
+ type: Boolean,
+ default: false,
+ },
+ isExpanded: {
+ type: Boolean,
+ default: true,
+ },
label: {
type: String,
default: '',
},
icon: {
+ type: [Object, String],
+ default: '',
+ },
+ sortOptions: {
+ type: Array,
+ default: () => [],
+ },
+ activeSort: {
type: String,
default: '',
},
+ showTreeLine: {
+ type: Boolean,
+ default: false,
+ },
+ endTreeLine: {
+ type: Boolean,
+ default: false,
+ },
});
+
+const emit = defineEmits(['toggle', 'update-sort']);
+
+const TREE_VERTICAL_LINE =
+ "before:content-[''] before:absolute before:-top-1 before:w-0.5 before:bg-n-slate-4 before:start-[-0.5rem]";
+const TREE_ELBOW =
+ "after:content-[''] after:absolute after:w-2.5 after:h-3 after:bottom-1/2 after:start-[-0.5rem] after:border-b-2 after:border-s-2 after:rounded-es after:border-n-slate-4";
-
-
-
- {{ label }}
-
+
+
+
+
+
+ {{ label }}
+
+
+
+
+ emit('update-sort', sortBy)"
+ />
+
+
diff --git a/app/javascript/dashboard/components-next/sidebar/SidebarSortMenu.vue b/app/javascript/dashboard/components-next/sidebar/SidebarSortMenu.vue
new file mode 100644
index 000000000..c4e29d881
--- /dev/null
+++ b/app/javascript/dashboard/components-next/sidebar/SidebarSortMenu.vue
@@ -0,0 +1,209 @@
+
+
+
+
+
diff --git a/app/javascript/dashboard/components-next/sidebar/SidebarSubGroup.vue b/app/javascript/dashboard/components-next/sidebar/SidebarSubGroup.vue
index 9aae04019..82005b49a 100644
--- a/app/javascript/dashboard/components-next/sidebar/SidebarSubGroup.vue
+++ b/app/javascript/dashboard/components-next/sidebar/SidebarSubGroup.vue
@@ -1,21 +1,56 @@
-
-
+
+
+
+
+
+
+
+
+
diff --git a/app/javascript/dashboard/components-next/sidebar/specs/SidebarSubGroup.spec.js b/app/javascript/dashboard/components-next/sidebar/specs/SidebarSubGroup.spec.js
new file mode 100644
index 000000000..ccede5d65
--- /dev/null
+++ b/app/javascript/dashboard/components-next/sidebar/specs/SidebarSubGroup.spec.js
@@ -0,0 +1,172 @@
+import { mount } from '@vue/test-utils';
+import { ref } from 'vue';
+import SidebarSubGroup from '../SidebarSubGroup.vue';
+import { LOCAL_STORAGE_KEYS } from 'dashboard/constants/localStorage';
+import { provideSidebarContext } from '../provider';
+
+vi.mock('dashboard/composables/store', () => ({
+ useMapGetter: () => ref(1),
+}));
+
+vi.mock('dashboard/composables/usePolicy', () => ({
+ usePolicy: () => ({
+ shouldShow: () => true,
+ }),
+}));
+
+vi.mock('vue-router', () => ({
+ useRouter: () => ({
+ resolve: () => ({ path: '/' }),
+ getRoutes: () => [],
+ }),
+}));
+
+const children = [
+ {
+ name: 'Sales-1',
+ label: 'Sales',
+ to: { name: 'team_conversations' },
+ },
+];
+
+const mountSubGroup = props => {
+ return mount(
+ {
+ components: { SidebarSubGroup },
+ setup() {
+ provideSidebarContext({});
+ },
+ template: '
',
+ },
+ {
+ attrs: {
+ name: 'Conversation:Teams',
+ label: 'Teams',
+ icon: 'i-lucide-users',
+ children,
+ isExpanded: true,
+ collapsible: true,
+ ...props,
+ },
+ global: {
+ stubs: {
+ SidebarGroupLeaf: {
+ props: {
+ label: { type: String, required: true },
+ hideTreeLine: { type: Boolean, default: false },
+ thinTreeLine: { type: Boolean, default: false },
+ },
+ template:
+ '',
+ },
+ },
+ },
+ }
+ );
+};
+
+describe('SidebarSubGroup', () => {
+ let localStorageStore;
+
+ beforeEach(() => {
+ localStorageStore = {};
+ Object.defineProperty(window, 'localStorage', {
+ value: {
+ getItem: key => localStorageStore[key] || null,
+ setItem: (key, value) => {
+ localStorageStore[key] = String(value);
+ },
+ removeItem: key => {
+ delete localStorageStore[key];
+ },
+ clear: () => {
+ localStorageStore = {};
+ },
+ },
+ configurable: true,
+ });
+
+ window.localStorage.clear();
+ });
+
+ it('keeps collapsible sections expanded by default', () => {
+ const wrapper = mountSubGroup();
+
+ expect(wrapper.find('button').attributes('aria-expanded')).toBe('true');
+ expect(wrapper.find('.sidebar-leaf').isVisible()).toBe(true);
+ });
+
+ it('renders the tree line on the separator, positioned relative to it', () => {
+ const wrapper = mountSubGroup({ showTreeLine: true });
+ const button = wrapper.find('button');
+
+ expect(button.classes()).toContain('relative');
+ expect(button.classes()).toContain('before:bg-n-slate-4');
+ expect(button.classes()).toContain('before:-bottom-1');
+ });
+
+ it('renders the end curve on the last separator', () => {
+ const wrapper = mountSubGroup({ showTreeLine: true, endTreeLine: true });
+ const button = wrapper.find('button');
+
+ expect(button.classes()).toContain('before:h-3');
+ expect(button.classes()).toContain('after:border-b-2');
+ });
+
+ it('draws nested item tree lines via the leaf connectors', () => {
+ const wrapper = mountSubGroup({ showTreeLine: true });
+
+ expect(
+ wrapper.find('.sidebar-leaf').attributes('data-hide-tree-line')
+ ).toBe('false');
+ });
+
+ it('marks nested item connectors as thin', () => {
+ const wrapper = mountSubGroup({ showTreeLine: true });
+
+ expect(
+ wrapper.find('.sidebar-leaf').attributes('data-thin-tree-line')
+ ).toBe('true');
+ });
+
+ it('minimizes the section and stores it by account and section name', async () => {
+ const wrapper = mountSubGroup();
+
+ await wrapper.find('button').trigger('click');
+
+ const storedSections = JSON.parse(
+ window.localStorage.getItem(LOCAL_STORAGE_KEYS.SIDEBAR_MINIMIZED_SECTIONS)
+ );
+ expect(wrapper.find('button').attributes('aria-expanded')).toBe('false');
+ expect(wrapper.find('.sidebar-leaf').isVisible()).toBe(false);
+ expect(storedSections).toEqual({ '1:Conversation:Teams': true });
+ });
+
+ it('uses the stored minimized state when mounted again', () => {
+ window.localStorage.setItem(
+ LOCAL_STORAGE_KEYS.SIDEBAR_MINIMIZED_SECTIONS,
+ JSON.stringify({ '1:Conversation:Teams': true })
+ );
+
+ const wrapper = mountSubGroup();
+
+ expect(wrapper.find('button').attributes('aria-expanded')).toBe('false');
+ expect(wrapper.find('.sidebar-leaf').isVisible()).toBe(false);
+ });
+
+ it('expands a stored minimized section when one of its children is active', () => {
+ window.localStorage.setItem(
+ LOCAL_STORAGE_KEYS.SIDEBAR_MINIMIZED_SECTIONS,
+ JSON.stringify({ '1:Conversation:Teams': true })
+ );
+
+ const wrapper = mountSubGroup({ activeChild: children[0] });
+
+ const storedSections = JSON.parse(
+ window.localStorage.getItem(LOCAL_STORAGE_KEYS.SIDEBAR_MINIMIZED_SECTIONS)
+ );
+ expect(wrapper.find('button').attributes('aria-expanded')).toBe('true');
+ expect(wrapper.find('.sidebar-leaf').isVisible()).toBe(true);
+ expect(storedSections).toEqual({});
+ });
+});
diff --git a/app/javascript/dashboard/components-next/tabbar/TabBar.vue b/app/javascript/dashboard/components-next/tabbar/TabBar.vue
index 6840bc9a9..301f51e07 100644
--- a/app/javascript/dashboard/components-next/tabbar/TabBar.vue
+++ b/app/javascript/dashboard/components-next/tabbar/TabBar.vue
@@ -81,6 +81,7 @@ const showDivider = index => {
+
diff --git a/app/javascript/dashboard/components/widgets/conversation/ConversationBasicFilter.vue b/app/javascript/dashboard/components/widgets/conversation/ConversationBasicFilter.vue
index fa1563ed8..38e85b485 100644
--- a/app/javascript/dashboard/components/widgets/conversation/ConversationBasicFilter.vue
+++ b/app/javascript/dashboard/components/widgets/conversation/ConversationBasicFilter.vue
@@ -78,6 +78,10 @@ const chatSortOptions = computed(() => [
label: t('CHAT_LIST.SORT_ORDER_ITEMS.created_at_asc.TEXT'),
value: 'created_at_asc',
},
+ {
+ label: t('CHAT_LIST.SORT_ORDER_ITEMS.unread.TEXT'),
+ value: 'unread',
+ },
{
label: t('CHAT_LIST.SORT_ORDER_ITEMS.priority_desc.TEXT'),
value: 'priority_desc',
diff --git a/app/javascript/dashboard/components/widgets/conversation/ReplyBox.vue b/app/javascript/dashboard/components/widgets/conversation/ReplyBox.vue
index fc93ff2c7..6af876cc6 100644
--- a/app/javascript/dashboard/components/widgets/conversation/ReplyBox.vue
+++ b/app/javascript/dashboard/components/widgets/conversation/ReplyBox.vue
@@ -56,8 +56,9 @@ import { isFileTypeAllowedForChannel } from 'shared/helpers/FileHelper';
import { LOCAL_STORAGE_KEYS } from 'dashboard/constants/localStorage';
import { LocalStorage } from 'shared/helpers/localStorage';
import { emitter } from 'shared/helpers/mitt';
-const EmojiInput = defineAsyncComponent(
- () => import('shared/components/emoji/EmojiInput.vue')
+const EmojiIconPicker = defineAsyncComponent(
+ () =>
+ import('dashboard/components-next/emoji-icon-picker/EmojiIconPicker.vue')
);
export default {
@@ -66,7 +67,7 @@ export default {
AttachmentPreview,
AudioRecorder,
ReplyBoxBanner,
- EmojiInput,
+ EmojiIconPicker,
MessageSignatureMissingAlert,
ReplyBottomPanel,
ReplyEmailHead,
@@ -1284,13 +1285,15 @@ export default {
:message="inReplyTo"
@dismiss="resetReplyToMessage"
/>
-
{
ACCOUNT_ID
);
expect(window.FB.login).toHaveBeenCalledWith(expect.any(Function), {
- scope: expect.stringContaining('pages_show_list'),
+ scope:
+ 'pages_manage_metadata,business_management,pages_messaging,pages_show_list,pages_read_engagement',
});
});
diff --git a/app/javascript/dashboard/composables/useFacebookPageConnect.js b/app/javascript/dashboard/composables/useFacebookPageConnect.js
index 46a177d58..dbe06dd0d 100644
--- a/app/javascript/dashboard/composables/useFacebookPageConnect.js
+++ b/app/javascript/dashboard/composables/useFacebookPageConnect.js
@@ -1,13 +1,9 @@
import { ref } from 'vue';
import { useMapGetter } from 'dashboard/composables/store';
import ChannelApi from 'dashboard/api/channels';
+import { buildFacebookLoginScopes } from 'dashboard/helper/facebookScopes';
import { setupFacebookSdk } from 'dashboard/routes/dashboard/settings/inbox/channels/whatsapp/utils';
-// Page-management + messaging scopes required to list pages and create a
-// Channel::FacebookPage inbox (mirrors the standalone settings flow).
-const FB_PAGE_SCOPES =
- 'pages_manage_metadata,business_management,pages_messaging,instagram_basic,pages_show_list,pages_read_engagement,instagram_manage_messages';
-
// Headless half of the Facebook Page connect flow: load the Meta SDK, run
// FB.login for page scopes, and fetch the user's pages. The caller owns the
// page-picker UI and the channel creation, because choosing a page is an
@@ -51,7 +47,7 @@ export function useFacebookPageConnect() {
: null
);
},
- { scope: FB_PAGE_SCOPES }
+ { scope: buildFacebookLoginScopes() }
);
});
diff --git a/app/javascript/dashboard/constants/globals.js b/app/javascript/dashboard/constants/globals.js
index 6d68824e4..5623912a5 100644
--- a/app/javascript/dashboard/constants/globals.js
+++ b/app/javascript/dashboard/constants/globals.js
@@ -27,6 +27,7 @@ export default {
WAITING_SINCE_ASC: 'waiting_since_asc',
WAITING_SINCE_DESC: 'waiting_since_desc',
PRIORITY_DESC_CREATED_AT_ASC: 'priority_desc_created_at_asc',
+ UNREAD: 'unread',
},
ARTICLE_STATUS_TYPES: {
DRAFT: 0,
diff --git a/app/javascript/dashboard/constants/localStorage.js b/app/javascript/dashboard/constants/localStorage.js
index 29aa4da77..ef1578af8 100644
--- a/app/javascript/dashboard/constants/localStorage.js
+++ b/app/javascript/dashboard/constants/localStorage.js
@@ -7,4 +7,5 @@ export const LOCAL_STORAGE_KEYS = {
DISMISSED_LABEL_SUGGESTIONS: 'labelSuggestionsDismissed',
MESSAGE_REPLY_TO: 'messageReplyTo',
RECENT_SEARCHES: 'recentSearches',
+ SIDEBAR_MINIMIZED_SECTIONS: 'sidebarMinimizedSections',
};
diff --git a/app/javascript/dashboard/helper/AnalyticsHelper/events.js b/app/javascript/dashboard/helper/AnalyticsHelper/events.js
index 6dc47cfbf..58d2821ef 100644
--- a/app/javascript/dashboard/helper/AnalyticsHelper/events.js
+++ b/app/javascript/dashboard/helper/AnalyticsHelper/events.js
@@ -154,6 +154,11 @@ export const YEAR_IN_REVIEW_EVENTS = Object.freeze({
SHARE_CLICKED: 'Year in Review: Share clicked',
});
+export const SESSION_EVENTS = Object.freeze({
+ LIMIT_HIT: 'Session limit reached at login',
+ REVOKED_FROM_PROFILE: 'Revoked an active session',
+});
+
export const ONBOARDING_EVENTS = Object.freeze({
ACCOUNT_DETAILS_VISITED: 'Onboarding: Account details visited',
ACCOUNT_DETAILS_COMPLETED: 'Onboarding: Account details completed',
diff --git a/app/javascript/dashboard/helper/facebookScopes.js b/app/javascript/dashboard/helper/facebookScopes.js
new file mode 100644
index 000000000..755b3f465
--- /dev/null
+++ b/app/javascript/dashboard/helper/facebookScopes.js
@@ -0,0 +1,22 @@
+export const FACEBOOK_PAGE_SCOPES = [
+ 'pages_manage_metadata',
+ 'business_management',
+ 'pages_messaging',
+ 'pages_show_list',
+ 'pages_read_engagement',
+];
+
+export const INSTAGRAM_SCOPES = [
+ 'instagram_basic',
+ 'instagram_manage_messages',
+];
+
+export const buildFacebookLoginScopes = ({
+ includeInstagramScopes = false,
+} = {}) => {
+ const scopes = [...FACEBOOK_PAGE_SCOPES];
+ if (includeInstagramScopes) {
+ scopes.push(...INSTAGRAM_SCOPES);
+ }
+ return scopes.join(',');
+};
diff --git a/app/javascript/dashboard/helper/sidebarSort.js b/app/javascript/dashboard/helper/sidebarSort.js
new file mode 100644
index 000000000..801dd94e2
--- /dev/null
+++ b/app/javascript/dashboard/helper/sidebarSort.js
@@ -0,0 +1,181 @@
+export const SIDEBAR_SORT_KEYS = Object.freeze({
+ CREATED_DESC: 'created_at_desc',
+ CREATED_ASC: 'created_at_asc',
+ ALPHABETICAL_ASC: 'alphabetical_asc',
+ ALPHABETICAL_DESC: 'alphabetical_desc',
+ UNREAD_COUNT_DESC: 'unread_count_desc',
+ UNREAD_COUNT_ASC: 'unread_count_asc',
+});
+
+export const SIDEBAR_SORT_SECTIONS = Object.freeze({
+ FOLDERS: 'folders',
+ TEAMS: 'teams',
+ CHANNELS: 'channels',
+ LABELS: 'labels',
+});
+
+export const SIDEBAR_SORT_OPTIONS_BY_SECTION = Object.freeze({
+ [SIDEBAR_SORT_SECTIONS.FOLDERS]: [
+ SIDEBAR_SORT_KEYS.CREATED_DESC,
+ SIDEBAR_SORT_KEYS.CREATED_ASC,
+ SIDEBAR_SORT_KEYS.ALPHABETICAL_ASC,
+ SIDEBAR_SORT_KEYS.ALPHABETICAL_DESC,
+ ],
+ [SIDEBAR_SORT_SECTIONS.TEAMS]: [
+ SIDEBAR_SORT_KEYS.CREATED_DESC,
+ SIDEBAR_SORT_KEYS.CREATED_ASC,
+ SIDEBAR_SORT_KEYS.ALPHABETICAL_ASC,
+ SIDEBAR_SORT_KEYS.ALPHABETICAL_DESC,
+ SIDEBAR_SORT_KEYS.UNREAD_COUNT_DESC,
+ SIDEBAR_SORT_KEYS.UNREAD_COUNT_ASC,
+ ],
+ [SIDEBAR_SORT_SECTIONS.CHANNELS]: [
+ SIDEBAR_SORT_KEYS.CREATED_DESC,
+ SIDEBAR_SORT_KEYS.CREATED_ASC,
+ SIDEBAR_SORT_KEYS.ALPHABETICAL_ASC,
+ SIDEBAR_SORT_KEYS.ALPHABETICAL_DESC,
+ SIDEBAR_SORT_KEYS.UNREAD_COUNT_DESC,
+ SIDEBAR_SORT_KEYS.UNREAD_COUNT_ASC,
+ ],
+ [SIDEBAR_SORT_SECTIONS.LABELS]: [
+ SIDEBAR_SORT_KEYS.CREATED_DESC,
+ SIDEBAR_SORT_KEYS.CREATED_ASC,
+ SIDEBAR_SORT_KEYS.ALPHABETICAL_ASC,
+ SIDEBAR_SORT_KEYS.ALPHABETICAL_DESC,
+ SIDEBAR_SORT_KEYS.UNREAD_COUNT_DESC,
+ SIDEBAR_SORT_KEYS.UNREAD_COUNT_ASC,
+ ],
+});
+
+const UNREAD_COUNT_SORT_OPTIONS = [
+ SIDEBAR_SORT_KEYS.UNREAD_COUNT_DESC,
+ SIDEBAR_SORT_KEYS.UNREAD_COUNT_ASC,
+];
+
+export const DEFAULT_SIDEBAR_SORT_PREFERENCES = Object.freeze({
+ [SIDEBAR_SORT_SECTIONS.FOLDERS]: SIDEBAR_SORT_KEYS.CREATED_DESC,
+ [SIDEBAR_SORT_SECTIONS.TEAMS]: SIDEBAR_SORT_KEYS.UNREAD_COUNT_DESC,
+ [SIDEBAR_SORT_SECTIONS.CHANNELS]: SIDEBAR_SORT_KEYS.UNREAD_COUNT_DESC,
+ [SIDEBAR_SORT_SECTIONS.LABELS]: SIDEBAR_SORT_KEYS.UNREAD_COUNT_DESC,
+});
+
+export const isValidSidebarSort = (section, sortBy) => {
+ return SIDEBAR_SORT_OPTIONS_BY_SECTION[section]?.includes(sortBy);
+};
+
+const isUnreadCountSort = sortBy => UNREAD_COUNT_SORT_OPTIONS.includes(sortBy);
+
+export const getSidebarSortOptions = (
+ section,
+ { hasUnreadCounts = true } = {}
+) => {
+ const options = SIDEBAR_SORT_OPTIONS_BY_SECTION[section] || [];
+
+ if (hasUnreadCounts) return options;
+
+ return options.filter(option => !isUnreadCountSort(option));
+};
+
+export const resolveSidebarSort = (
+ section,
+ sortBy,
+ { hasUnreadCounts = true } = {}
+) => {
+ const options = getSidebarSortOptions(section, { hasUnreadCounts });
+
+ if (options.includes(sortBy)) return sortBy;
+ if (!hasUnreadCounts && isUnreadCountSort(sortBy)) {
+ return SIDEBAR_SORT_KEYS.ALPHABETICAL_ASC;
+ }
+
+ return options[0] || SIDEBAR_SORT_KEYS.ALPHABETICAL_ASC;
+};
+
+export const normalizeSidebarSortPreferences = (preferences = {}) => {
+ const savedPreferences = preferences || {};
+
+ return Object.keys(DEFAULT_SIDEBAR_SORT_PREFERENCES).reduce(
+ (result, section) => {
+ const sortBy = savedPreferences[section];
+ result[section] = isValidSidebarSort(section, sortBy)
+ ? sortBy
+ : DEFAULT_SIDEBAR_SORT_PREFERENCES[section];
+ return result;
+ },
+ {}
+ );
+};
+
+const normalizeUnreadCount = count => {
+ const unreadCount = Number(count);
+ return Number.isFinite(unreadCount) && unreadCount > 0 ? unreadCount : 0;
+};
+
+const getCreatedValue = item => {
+ const createdAt = item.created_at || item.createdAt;
+
+ if (typeof createdAt === 'number') return createdAt;
+
+ if (createdAt) {
+ const timestamp = Date.parse(createdAt);
+ if (Number.isFinite(timestamp)) return timestamp;
+ }
+
+ const id = Number(item.id);
+ return Number.isFinite(id) ? id : 0;
+};
+
+const getLabelValue = (item, labelKey) => {
+ return String(labelKey(item) || '');
+};
+
+const compareAlphabetically = (a, b, labelKey) => {
+ return getLabelValue(a, labelKey).localeCompare(
+ getLabelValue(b, labelKey),
+ undefined,
+ {
+ sensitivity: 'base',
+ }
+ );
+};
+
+export const sortSidebarItems = (
+ items,
+ { sortBy, labelKey, unreadCountKey = () => 0 }
+) => {
+ return (items || []).slice().sort((a, b) => {
+ if (sortBy === SIDEBAR_SORT_KEYS.CREATED_DESC) {
+ const createdDiff = getCreatedValue(b) - getCreatedValue(a);
+ if (createdDiff !== 0) return createdDiff;
+ return compareAlphabetically(a, b, labelKey);
+ }
+
+ if (sortBy === SIDEBAR_SORT_KEYS.CREATED_ASC) {
+ const createdDiff = getCreatedValue(a) - getCreatedValue(b);
+ if (createdDiff !== 0) return createdDiff;
+ return compareAlphabetically(a, b, labelKey);
+ }
+
+ if (sortBy === SIDEBAR_SORT_KEYS.ALPHABETICAL_DESC) {
+ return compareAlphabetically(b, a, labelKey);
+ }
+
+ if (sortBy === SIDEBAR_SORT_KEYS.UNREAD_COUNT_DESC) {
+ const unreadCountDiff =
+ normalizeUnreadCount(unreadCountKey(b)) -
+ normalizeUnreadCount(unreadCountKey(a));
+
+ if (unreadCountDiff !== 0) return unreadCountDiff;
+ }
+
+ if (sortBy === SIDEBAR_SORT_KEYS.UNREAD_COUNT_ASC) {
+ const unreadCountDiff =
+ normalizeUnreadCount(unreadCountKey(a)) -
+ normalizeUnreadCount(unreadCountKey(b));
+
+ if (unreadCountDiff !== 0) return unreadCountDiff;
+ }
+
+ return compareAlphabetically(a, b, labelKey);
+ });
+};
diff --git a/app/javascript/dashboard/helper/specs/sidebarSort.spec.js b/app/javascript/dashboard/helper/specs/sidebarSort.spec.js
new file mode 100644
index 000000000..3919252c2
--- /dev/null
+++ b/app/javascript/dashboard/helper/specs/sidebarSort.spec.js
@@ -0,0 +1,205 @@
+import {
+ DEFAULT_SIDEBAR_SORT_PREFERENCES,
+ SIDEBAR_SORT_KEYS,
+ SIDEBAR_SORT_SECTIONS,
+ getSidebarSortOptions,
+ normalizeSidebarSortPreferences,
+ resolveSidebarSort,
+ sortSidebarItems,
+} from '../sidebarSort';
+
+const items = [
+ {
+ id: 1,
+ name: 'Billing',
+ created_at: '2024-01-01T00:00:00.000Z',
+ },
+ {
+ id: 3,
+ name: 'Accounts',
+ created_at: '2024-03-01T00:00:00.000Z',
+ },
+ {
+ id: 2,
+ name: 'Support',
+ created_at: '2024-02-01T00:00:00.000Z',
+ },
+];
+
+describe('#sortSidebarItems', () => {
+ it('sorts by created date descending', () => {
+ const sortedItems = sortSidebarItems(items, {
+ sortBy: SIDEBAR_SORT_KEYS.CREATED_DESC,
+ labelKey: item => item.name,
+ });
+
+ expect(sortedItems.map(item => item.name)).toEqual([
+ 'Accounts',
+ 'Support',
+ 'Billing',
+ ]);
+ });
+
+ it('sorts by created date ascending', () => {
+ const sortedItems = sortSidebarItems(items, {
+ sortBy: SIDEBAR_SORT_KEYS.CREATED_ASC,
+ labelKey: item => item.name,
+ });
+
+ expect(sortedItems.map(item => item.name)).toEqual([
+ 'Billing',
+ 'Support',
+ 'Accounts',
+ ]);
+ });
+
+ it('falls back to id when created date is not present', () => {
+ const sortedItems = sortSidebarItems(
+ items.map(({ created_at: _createdAt, ...item }) => item),
+ {
+ sortBy: SIDEBAR_SORT_KEYS.CREATED_DESC,
+ labelKey: item => item.name,
+ }
+ );
+
+ expect(sortedItems.map(item => item.id)).toEqual([3, 2, 1]);
+ });
+
+ it('sorts alphabetically from A to Z', () => {
+ const sortedItems = sortSidebarItems(items, {
+ sortBy: SIDEBAR_SORT_KEYS.ALPHABETICAL_ASC,
+ labelKey: item => item.name,
+ });
+
+ expect(sortedItems.map(item => item.name)).toEqual([
+ 'Accounts',
+ 'Billing',
+ 'Support',
+ ]);
+ });
+
+ it('sorts alphabetically from Z to A', () => {
+ const sortedItems = sortSidebarItems(items, {
+ sortBy: SIDEBAR_SORT_KEYS.ALPHABETICAL_DESC,
+ labelKey: item => item.name,
+ });
+
+ expect(sortedItems.map(item => item.name)).toEqual([
+ 'Support',
+ 'Billing',
+ 'Accounts',
+ ]);
+ });
+
+ it('sorts by unread count descending and falls back to alphabetical order', () => {
+ const unreadCounts = {
+ 1: 3,
+ 2: 3,
+ 3: 7,
+ };
+
+ const sortedItems = sortSidebarItems(items, {
+ sortBy: SIDEBAR_SORT_KEYS.UNREAD_COUNT_DESC,
+ labelKey: item => item.name,
+ unreadCountKey: item => unreadCounts[item.id],
+ });
+
+ expect(sortedItems.map(item => item.name)).toEqual([
+ 'Accounts',
+ 'Billing',
+ 'Support',
+ ]);
+ });
+
+ it('sorts by unread count ascending and falls back to alphabetical order', () => {
+ const unreadCounts = {
+ 1: 3,
+ 2: 3,
+ 3: 7,
+ };
+
+ const sortedItems = sortSidebarItems(items, {
+ sortBy: SIDEBAR_SORT_KEYS.UNREAD_COUNT_ASC,
+ labelKey: item => item.name,
+ unreadCountKey: item => unreadCounts[item.id],
+ });
+
+ expect(sortedItems.map(item => item.name)).toEqual([
+ 'Billing',
+ 'Support',
+ 'Accounts',
+ ]);
+ });
+});
+
+describe('#normalizeSidebarSortPreferences', () => {
+ it('keeps valid preferences', () => {
+ const preferences = normalizeSidebarSortPreferences({
+ [SIDEBAR_SORT_SECTIONS.FOLDERS]: SIDEBAR_SORT_KEYS.ALPHABETICAL_DESC,
+ [SIDEBAR_SORT_SECTIONS.TEAMS]: SIDEBAR_SORT_KEYS.CREATED_ASC,
+ });
+
+ expect(preferences).toEqual({
+ ...DEFAULT_SIDEBAR_SORT_PREFERENCES,
+ [SIDEBAR_SORT_SECTIONS.FOLDERS]: SIDEBAR_SORT_KEYS.ALPHABETICAL_DESC,
+ [SIDEBAR_SORT_SECTIONS.TEAMS]: SIDEBAR_SORT_KEYS.CREATED_ASC,
+ });
+ });
+
+ it('falls back to defaults for unsupported preferences', () => {
+ const preferences = normalizeSidebarSortPreferences({
+ [SIDEBAR_SORT_SECTIONS.FOLDERS]: SIDEBAR_SORT_KEYS.UNREAD_COUNT_DESC,
+ });
+
+ expect(preferences).toEqual(DEFAULT_SIDEBAR_SORT_PREFERENCES);
+ });
+
+ it('falls back to defaults when stored preferences are null', () => {
+ expect(normalizeSidebarSortPreferences(null)).toEqual(
+ DEFAULT_SIDEBAR_SORT_PREFERENCES
+ );
+ });
+});
+
+describe('#getSidebarSortOptions', () => {
+ it('keeps unread count options when unread counts are enabled', () => {
+ const options = getSidebarSortOptions(SIDEBAR_SORT_SECTIONS.TEAMS, {
+ hasUnreadCounts: true,
+ });
+
+ expect(options).toContain(SIDEBAR_SORT_KEYS.UNREAD_COUNT_DESC);
+ expect(options).toContain(SIDEBAR_SORT_KEYS.UNREAD_COUNT_ASC);
+ });
+
+ it('removes unread count options when unread counts are disabled', () => {
+ const options = getSidebarSortOptions(SIDEBAR_SORT_SECTIONS.TEAMS, {
+ hasUnreadCounts: false,
+ });
+
+ expect(options).not.toContain(SIDEBAR_SORT_KEYS.UNREAD_COUNT_DESC);
+ expect(options).not.toContain(SIDEBAR_SORT_KEYS.UNREAD_COUNT_ASC);
+ expect(options).toContain(SIDEBAR_SORT_KEYS.ALPHABETICAL_ASC);
+ });
+});
+
+describe('#resolveSidebarSort', () => {
+ it('keeps unread count sort when unread counts are enabled', () => {
+ const sortBy = resolveSidebarSort(
+ SIDEBAR_SORT_SECTIONS.TEAMS,
+ SIDEBAR_SORT_KEYS.UNREAD_COUNT_DESC,
+ { hasUnreadCounts: true }
+ );
+
+ expect(sortBy).toBe(SIDEBAR_SORT_KEYS.UNREAD_COUNT_DESC);
+ });
+
+ it('falls back to alphabetical sort when unread counts are disabled', () => {
+ const sortBy = resolveSidebarSort(
+ SIDEBAR_SORT_SECTIONS.TEAMS,
+ SIDEBAR_SORT_KEYS.UNREAD_COUNT_DESC,
+ { hasUnreadCounts: false }
+ );
+
+ expect(sortBy).toBe(SIDEBAR_SORT_KEYS.ALPHABETICAL_ASC);
+ });
+});
diff --git a/app/javascript/dashboard/i18n/locale/en/chatlist.json b/app/javascript/dashboard/i18n/locale/en/chatlist.json
index 1384dae2b..45755892d 100644
--- a/app/javascript/dashboard/i18n/locale/en/chatlist.json
+++ b/app/javascript/dashboard/i18n/locale/en/chatlist.json
@@ -79,6 +79,9 @@
},
"priority_desc_created_at_asc": {
"TEXT": "Priority: Highest first, Created: Oldest first"
+ },
+ "unread": {
+ "TEXT": "Unread Count: Highest first"
}
},
"ATTACHMENTS": {
diff --git a/app/javascript/dashboard/i18n/locale/en/contact.json b/app/javascript/dashboard/i18n/locale/en/contact.json
index 8d0b73dfc..1a10b253f 100644
--- a/app/javascript/dashboard/i18n/locale/en/contact.json
+++ b/app/javascript/dashboard/i18n/locale/en/contact.json
@@ -511,6 +511,7 @@
"ATTRIBUTES": "Attributes",
"HISTORY": "History",
"NOTES": "Notes",
+ "MEDIA": "Media",
"MERGE": "Merge"
},
"HISTORY": {
diff --git a/app/javascript/dashboard/i18n/locale/en/conversation.json b/app/javascript/dashboard/i18n/locale/en/conversation.json
index 544e00bf6..4ac8d4a5b 100644
--- a/app/javascript/dashboard/i18n/locale/en/conversation.json
+++ b/app/javascript/dashboard/i18n/locale/en/conversation.json
@@ -403,7 +403,8 @@
"VIEW_ALL": "View all",
"SHOW_LESS": "Show less",
"MORE_COUNT": "+{count}",
- "UNTITLED_FILE": "Untitled file"
+ "UNTITLED_FILE": "Untitled file",
+ "JUMP_TO_MESSAGE": "Jump to message"
},
"SHOPIFY": {
"ORDER_ID": "Order #{id}",
diff --git a/app/javascript/dashboard/i18n/locale/en/emoji.json b/app/javascript/dashboard/i18n/locale/en/emoji.json
index d5b96f0f9..f5572d569 100644
--- a/app/javascript/dashboard/i18n/locale/en/emoji.json
+++ b/app/javascript/dashboard/i18n/locale/en/emoji.json
@@ -3,5 +3,33 @@
"PLACEHOLDER": "Search emojis",
"NOT_FOUND": "No emoji match your search",
"REMOVE": "Remove"
+ },
+ "EMOJI_ICON_PICKER": {
+ "TABS": {
+ "ICONS": "Icons",
+ "EMOJIS": "Emojis"
+ },
+ "SEARCH_EMOJI": "Search emoji…",
+ "SEARCH_ICON": "Search icons…",
+ "FREQUENTLY_USED": "Frequently used",
+ "NO_EMOJI": "No emoji match your search",
+ "NO_ICON": "No icons match your search",
+ "REMOVE": "Remove",
+ "STYLE": {
+ "OUTLINE": "Outline icons",
+ "FILLED": "Filled icons"
+ },
+ "COLORS": {
+ "SLATE": "Slate",
+ "RED": "Red",
+ "ORANGE": "Orange",
+ "AMBER": "Amber",
+ "GREEN": "Green",
+ "TEAL": "Teal",
+ "BLUE": "Blue",
+ "INDIGO": "Indigo",
+ "VIOLET": "Violet",
+ "PINK": "Pink"
+ }
}
}
diff --git a/app/javascript/dashboard/i18n/locale/en/index.js b/app/javascript/dashboard/i18n/locale/en/index.js
index 31486a247..12db16ba7 100644
--- a/app/javascript/dashboard/i18n/locale/en/index.js
+++ b/app/javascript/dashboard/i18n/locale/en/index.js
@@ -40,6 +40,7 @@ import whatsappTemplates from './whatsappTemplates.json';
import contentTemplates from './contentTemplates.json';
import mfa from './mfa.json';
import onboarding from './onboarding.json';
+import sessionLimit from './sessionLimit.json';
import yearInReview from './yearInReview.json';
export default {
@@ -85,5 +86,6 @@ export default {
...contentTemplates,
...mfa,
...onboarding,
+ ...sessionLimit,
...yearInReview,
};
diff --git a/app/javascript/dashboard/i18n/locale/en/sessionLimit.json b/app/javascript/dashboard/i18n/locale/en/sessionLimit.json
new file mode 100644
index 000000000..926745c23
--- /dev/null
+++ b/app/javascript/dashboard/i18n/locale/en/sessionLimit.json
@@ -0,0 +1,12 @@
+{
+ "SESSION_LIMIT": {
+ "TITLE": "Active session limit reached",
+ "DESCRIPTION": "You have reached your limit of active sessions. Please end a session before logging in.",
+ "END": "End",
+ "END_ALL": "End all sessions",
+ "LOG_IN": "Log in",
+ "CANCEL": "Back to login",
+ "UNKNOWN_DEVICE": "Unknown device",
+ "STARTED": "Started"
+ }
+}
diff --git a/app/javascript/dashboard/i18n/locale/en/settings.json b/app/javascript/dashboard/i18n/locale/en/settings.json
index bfbd920a7..5e2543698 100644
--- a/app/javascript/dashboard/i18n/locale/en/settings.json
+++ b/app/javascript/dashboard/i18n/locale/en/settings.json
@@ -86,6 +86,17 @@
"NOTE": "Manage additional security features for your account.",
"MFA_BUTTON": "Manage Two-Factor Authentication"
},
+ "SESSIONS_SECTION": {
+ "TITLE": "Active Sessions",
+ "NOTE": "These are the devices currently logged in to your account.",
+ "CURRENT": "Current session",
+ "REVOKE": "Revoke",
+ "REVOKE_SUCCESS": "Session revoked successfully",
+ "REVOKE_ERROR": "Unable to revoke session. Please try again.",
+ "FETCH_ERROR": "Unable to fetch sessions. Please try again.",
+ "LAST_ACTIVE": "Last active",
+ "UNKNOWN_DEVICE": "Unknown device"
+ },
"ACCESS_TOKEN": {
"TITLE": "Access Token",
"NOTE": "This token can be used if you are building an API based integration",
@@ -379,6 +390,21 @@
"INFO_TEXT": "Let the system automatically mark you offline when you aren't using the app or dashboard.",
"INFO_SHORT": "Automatically mark offline when you aren't using the app."
},
+ "SORT_TOOLTIP": "Sort",
+ "SORT_BY": "Sort by",
+ "SORT_GROUPS": {
+ "CREATED": "Sort by date of creation",
+ "ALPHABETICAL": "Sort in alphabetical order",
+ "UNREAD_COUNT": "Sort by unread count"
+ },
+ "SORT_OPTIONS": {
+ "CREATED_DESC": "Newest first",
+ "CREATED_ASC": "Oldest first",
+ "ALPHABETICAL_ASC": "Ascending (A - Z)",
+ "ALPHABETICAL_DESC": "Descending (Z - A)",
+ "UNREAD_COUNT_DESC": "Highest first",
+ "UNREAD_COUNT_ASC": "Lowest first"
+ },
"DOCS": "Read docs",
"SECURITY": "Security",
"CAPTAIN_AI": "Captain",
diff --git a/app/javascript/dashboard/routes/dashboard/contacts/pages/ContactManageView.vue b/app/javascript/dashboard/routes/dashboard/contacts/pages/ContactManageView.vue
index fe23e23d3..05142bb82 100644
--- a/app/javascript/dashboard/routes/dashboard/contacts/pages/ContactManageView.vue
+++ b/app/javascript/dashboard/routes/dashboard/contacts/pages/ContactManageView.vue
@@ -11,6 +11,7 @@ import ContactDetails from 'dashboard/components-next/Contacts/Pages/ContactDeta
import TabBar from 'dashboard/components-next/tabbar/TabBar.vue';
import ContactNotes from 'dashboard/components-next/Contacts/ContactsSidebar/ContactNotes.vue';
import ContactHistory from 'dashboard/components-next/Contacts/ContactsSidebar/ContactHistory.vue';
+import ContactMedia from 'dashboard/components-next/Contacts/ContactsSidebar/ContactMedia.vue';
import ContactMerge from 'dashboard/components-next/Contacts/ContactsSidebar/ContactMerge.vue';
import ContactCustomAttributes from 'dashboard/components-next/Contacts/ContactsSidebar/ContactCustomAttributes.vue';
@@ -40,6 +41,7 @@ const CONTACT_TABS_OPTIONS = [
{ key: 'ATTRIBUTES', value: 'attributes' },
{ key: 'HISTORY', value: 'history' },
{ key: 'NOTES', value: 'notes' },
+ { key: 'MEDIA', value: 'media' },
{ key: 'MERGE', value: 'merge' },
];
@@ -149,8 +151,8 @@ onMounted(() => {
:selected-contact="selectedContact"
@go-to-contacts-list="goToContactsList"
/>
-
-
+
+
{
@tab-changed="handleTabChange"
/>
+
+
{
/>
+
- [...allAttachments.value].sort(
- (a, b) => (b.created_at || 0) - (a.created_at || 0)
- )
-);
+const { t } = useI18n();
const mediaAttachments = computed(() =>
- sortedAttachments.value.filter(a => MEDIA_TYPES.includes(a.file_type))
+ allAttachments.value
+ .filter(a => MEDIA_TYPES.includes(a.file_type) && a.data_url)
+ .sort((a, b) => (b.created_at || 0) - (a.created_at || 0))
);
-const fileAttachments = computed(() =>
- sortedAttachments.value.filter(
- a => !MEDIA_TYPES.includes(a.file_type) && a.data_url
+const hasContent = computed(() =>
+ allAttachments.value.some(
+ a => a.data_url && !NON_FILE_TYPES.includes(a.file_type)
)
);
-const showAllMedia = ref(false);
-const showAllFiles = ref(false);
-
-const visibleMedia = computed(() =>
- showAllMedia.value
- ? mediaAttachments.value
- : mediaAttachments.value.slice(0, MEDIA_PEEK_LIMIT)
-);
-
-const visibleFiles = computed(() =>
- showAllFiles.value
- ? fileAttachments.value
- : fileAttachments.value.slice(0, FILES_PEEK_LIMIT)
-);
-
-const mediaOverflow = computed(() => {
- const total = mediaAttachments.value.length;
- return total > MEDIA_PEEK_LIMIT ? total - (MEDIA_PEEK_LIMIT - 1) : 0;
-});
-
const showGallery = ref(false);
const selectedAttachment = ref(null);
-const downloadingId = ref(null);
-const fileNameFromUrl = url => {
- if (!url) return '';
- const name = url.split('/').pop();
- return name ? decodeURIComponent(name) : '';
-};
-
-const onDownloadFile = async attachment => {
- const { id, file_type: type, data_url: url, extension } = attachment;
- try {
- downloadingId.value = id;
- await downloadFile({ url, type, extension });
- } catch (error) {
- useAlert(t('CONVERSATION_SIDEBAR.SHARED_FILES.DOWNLOAD_ERROR'));
- } finally {
- downloadingId.value = null;
- }
-};
-
-const isVideoType = type =>
- [ATTACHMENT_TYPES.VIDEO, ATTACHMENT_TYPES.IG_REEL].includes(type);
-
-const isAudioType = type => type === ATTACHMENT_TYPES.AUDIO;
-const isPlayableType = type => isVideoType(type) || isAudioType(type);
-
-const durations = ref({});
-
-const onLoadedMetadata = (attachment, event) => {
- const seconds = event.target?.duration;
- if (Number.isFinite(seconds) && seconds > 0) {
- durations.value[attachment.id] = seconds;
- }
-};
-
-const displayDuration = attachment => {
- const seconds = durations.value[attachment.id];
- return seconds ? formatDuration(Math.round(seconds)) : '';
-};
-
-const isOverflowTile = index =>
- !showAllMedia.value &&
- mediaOverflow.value > 0 &&
- index === MEDIA_PEEK_LIMIT - 1;
-
-const onTileActivate = (attachment, index) => {
- if (isOverflowTile(index)) {
- showAllMedia.value = true;
- return;
- }
+const onMediaSelect = attachment => {
selectedAttachment.value = attachment;
showGallery.value = true;
};
-const failedThumbs = ref(new Set());
-const failedPreviews = ref(new Set());
-
-const imagePreviewSrc = ({
- id,
- file_type: type,
- thumb_url: thumbUrl,
- data_url: dataUrl,
-}) => {
- const canUseThumb = thumbUrl && !failedThumbs.value.has(id);
- if (type === ATTACHMENT_TYPES.IMAGE) return canUseThumb ? thumbUrl : dataUrl;
- if (isVideoType(type)) return canUseThumb ? thumbUrl : null;
- return null;
-};
-
-const onPreviewError = ({
- id,
- file_type: type,
- thumb_url: thumbUrl,
- data_url: dataUrl,
-}) => {
- const canRetryWithFull = thumbUrl && !failedThumbs.value.has(id) && dataUrl;
- if (
- canRetryWithFull &&
- (type === ATTACHMENT_TYPES.IMAGE || isVideoType(type))
- ) {
- failedThumbs.value.add(id);
- return;
+const onFileSelect = attachment => {
+ if (attachment.data_url) {
+ window.open(attachment.data_url, '_blank', 'noopener,noreferrer');
}
- failedPreviews.value.add(id);
-};
-
-const hasPreview = attachment =>
- !!imagePreviewSrc(attachment) && !failedPreviews.value.has(attachment.id);
-const hasVideoPreview = attachment =>
- isVideoType(attachment.file_type) &&
- attachment.data_url &&
- !failedPreviews.value.has(attachment.id);
-
-const fallbackIcon = type => {
- if (type === ATTACHMENT_TYPES.AUDIO) return 'i-lucide-music';
- if (isVideoType(type)) return 'i-lucide-video';
- return 'i-lucide-image';
-};
-
-const displayName = attachment =>
- fileNameFromUrl(attachment.data_url) ||
- t('CONVERSATION_SIDEBAR.SHARED_FILES.UNTITLED_FILE');
-
-const displaySize = attachment => {
- if (attachment.file_size) return formatBytes(attachment.file_size);
- if (attachment.extension) return attachment.extension.toUpperCase();
- return '—';
-};
-
-const displayTime = attachment => {
- if (!attachment.created_at) return '';
- return shortTimestamp(dynamicTime(attachment.created_at), true);
};
-
+
-
+
{{ t('CONVERSATION_SIDEBAR.SHARED_FILES.EMPTY') }}
-
-
-
-
- {{ t('CONVERSATION_SIDEBAR.SHARED_FILES.MEDIA_HEADING') }}
-
- {{ mediaAttachments.length }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ displayDuration(attachment) }}
-
-
-
- {{ displayTime(attachment) }}
-
-
-
-
-
-
-
-
-
- {{
- t('CONVERSATION_SIDEBAR.SHARED_FILES.MORE_COUNT', {
- count: mediaOverflow,
- })
- }}
-
-
-
-
-
-
-
-
-
- {{ t('CONVERSATION_SIDEBAR.SHARED_FILES.FILES_HEADING') }}
-
- {{ fileAttachments.length }}
-
-
-
-
-
-
-
+
+
+
+
+import { ref, onMounted } from 'vue';
+import { useI18n } from 'vue-i18n';
+import { formatDistanceToNow, parseISO } from 'date-fns';
+import { useAlert } from 'dashboard/composables';
+import authAPI from 'dashboard/api/auth';
+import AnalyticsHelper from 'dashboard/helper/AnalyticsHelper';
+import { SESSION_EVENTS } from 'dashboard/helper/AnalyticsHelper/events';
+import Icon from 'dashboard/components-next/icon/Icon.vue';
+import Button from 'dashboard/components-next/button/Button.vue';
+
+const { t } = useI18n();
+const sessions = ref([]);
+const loading = ref(false);
+
+const relativeTime = dateStr => {
+ if (!dateStr) return '';
+ return formatDistanceToNow(parseISO(dateStr), { addSuffix: true });
+};
+
+const isUnknown = val => !val || val === 'Unknown' || val === 'Unknown Browser';
+
+const deviceIcon = session => {
+ const name = (session.device_name || '').toLowerCase();
+ if (
+ name.includes('iphone') ||
+ name.includes('android') ||
+ name.includes('mobile')
+ ) {
+ return 'i-lucide-smartphone';
+ }
+ if (name.includes('ipad') || name.includes('tablet')) {
+ return 'i-lucide-tablet';
+ }
+ return 'i-lucide-monitor';
+};
+
+const sessionLabel = session => {
+ const parts = [];
+ if (!isUnknown(session.browser_name)) {
+ parts.push(
+ session.browser_version
+ ? `${session.browser_name} ${session.browser_version}`
+ : session.browser_name
+ );
+ }
+ if (!isUnknown(session.platform_name)) parts.push(session.platform_name);
+ return (
+ parts.join(' on ') ||
+ t('PROFILE_SETTINGS.FORM.SESSIONS_SECTION.UNKNOWN_DEVICE')
+ );
+};
+
+const locationLabel = session => {
+ const parts = [];
+ if (session.city) parts.push(session.city);
+ if (session.country) parts.push(session.country);
+ return parts.join(', ');
+};
+
+const fetchSessions = async () => {
+ loading.value = true;
+ try {
+ const { data } = await authAPI.getSessions();
+ sessions.value = data;
+ } catch {
+ useAlert(t('PROFILE_SETTINGS.FORM.SESSIONS_SECTION.FETCH_ERROR'));
+ } finally {
+ loading.value = false;
+ }
+};
+
+const revokeSession = async session => {
+ try {
+ await authAPI.revokeSession(session.id);
+ sessions.value = sessions.value.filter(s => s.id !== session.id);
+ AnalyticsHelper.track(SESSION_EVENTS.REVOKED_FROM_PROFILE);
+ useAlert(t('PROFILE_SETTINGS.FORM.SESSIONS_SECTION.REVOKE_SUCCESS'));
+ } catch {
+ useAlert(t('PROFILE_SETTINGS.FORM.SESSIONS_SECTION.REVOKE_ERROR'));
+ }
+};
+
+onMounted(fetchSessions);
+
+
+
+
+
+
+
+
+
+
+ {{ sessionLabel(session) }}
+
+
+ {{ $t('PROFILE_SETTINGS.FORM.SESSIONS_SECTION.CURRENT') }}
+
+
+
+ {{ locationLabel(session) }}
+
+
+ {{ $t('PROFILE_SETTINGS.FORM.SESSIONS_SECTION.LAST_ACTIVE') }}
+ {{ relativeTime(session.last_activity_at) }}
+
+
+
+
+
+
+
diff --git a/app/javascript/dashboard/routes/dashboard/settings/profile/Index.vue b/app/javascript/dashboard/routes/dashboard/settings/profile/Index.vue
index 75eb8a2f8..04de7bda2 100644
--- a/app/javascript/dashboard/routes/dashboard/settings/profile/Index.vue
+++ b/app/javascript/dashboard/routes/dashboard/settings/profile/Index.vue
@@ -20,6 +20,7 @@ import SectionLayout from '../account/components/SectionLayout.vue';
import BaseSettingsHeader from '../components/BaseSettingsHeader.vue';
import AccessToken from './AccessToken.vue';
import MfaSettingsCard from './MfaSettingsCard.vue';
+import ActiveSessions from './ActiveSessions.vue';
import Policy from 'dashboard/components/policy.vue';
import RadioCard from 'dashboard/components-next/radioCard/RadioCard.vue';
import {
@@ -42,6 +43,7 @@ export default {
AudioNotifications,
AccessToken,
MfaSettingsCard,
+ ActiveSessions,
BaseSettingsHeader,
},
setup() {
@@ -307,6 +309,13 @@ export default {
>
+
+
+
{
payload.append('inbox_id', inboxId);
payload.append('contact_id', contactId);
payload.append('source_id', sourceId);
- payload.append('additional_attributes[mail_subject]', mailSubject);
+ if (mailSubject) {
+ payload.append('additional_attributes[mail_subject]', mailSubject);
+ }
payload.append('assignee_id', assigneeId);
return payload;
diff --git a/app/javascript/dashboard/store/modules/contacts/actions.js b/app/javascript/dashboard/store/modules/contacts/actions.js
index 2aea4d4d5..76bbf1f26 100644
--- a/app/javascript/dashboard/store/modules/contacts/actions.js
+++ b/app/javascript/dashboard/store/modules/contacts/actions.js
@@ -2,12 +2,12 @@ import {
DuplicateContactException,
ExceptionWithMessage,
} from 'shared/helpers/CustomErrors';
-import types from '../../mutation-types';
-import ContactAPI from '../../../api/contacts';
import snakecaseKeys from 'snakecase-keys';
import AccountActionsAPI from '../../../api/accountActions';
+import ContactAPI from '../../../api/contacts';
import AnalyticsHelper from '../../../helper/AnalyticsHelper';
import { CONTACTS_EVENTS } from '../../../helper/AnalyticsHelper/events';
+import types from '../../mutation-types';
const buildContactFormData = contactParams => {
const formData = new FormData();
@@ -114,6 +114,19 @@ export const actions = {
}
},
+ fetchAttachments: async ({ commit }, id) => {
+ commit(types.SET_CONTACT_UI_FLAG, { isFetchingAttachments: true });
+ try {
+ const response = await ContactAPI.getAttachments(id);
+ commit(types.SET_CONTACT_ATTACHMENTS, {
+ id,
+ data: response.data.payload,
+ });
+ } finally {
+ commit(types.SET_CONTACT_UI_FLAG, { isFetchingAttachments: false });
+ }
+ },
+
update: async ({ commit }, { id, isFormData = false, ...contactParams }) => {
const { avatar, customAttributes, ...paramsToDecamelize } = contactParams;
const decamelizedContactParams = {
diff --git a/app/javascript/dashboard/store/modules/contacts/getters.js b/app/javascript/dashboard/store/modules/contacts/getters.js
index 5a75ac091..425ba9449 100644
--- a/app/javascript/dashboard/store/modules/contacts/getters.js
+++ b/app/javascript/dashboard/store/modules/contacts/getters.js
@@ -24,6 +24,7 @@ export const getters = {
stopPaths: ['custom_attributes'],
});
},
+ getContactAttachments: $state => id => $state.records[id]?.attachments || [],
getMeta: $state => {
return $state.meta;
},
diff --git a/app/javascript/dashboard/store/modules/contacts/mutations.js b/app/javascript/dashboard/store/modules/contacts/mutations.js
index 5eef7e2b3..5fbd04ed1 100644
--- a/app/javascript/dashboard/store/modules/contacts/mutations.js
+++ b/app/javascript/dashboard/store/modules/contacts/mutations.js
@@ -58,7 +58,15 @@ export const mutations = {
},
[types.EDIT_CONTACT]: ($state, data) => {
- $state.records[data.id] = data;
+ const existingAttachments = $state.records[data.id]?.attachments;
+ $state.records[data.id] = existingAttachments
+ ? { ...data, attachments: existingAttachments }
+ : data;
+ },
+
+ [types.SET_CONTACT_ATTACHMENTS]: ($state, { id, data }) => {
+ if (!$state.records[id]) $state.records[id] = {};
+ $state.records[id].attachments = data;
},
[types.DELETE_CONTACT]: ($state, id) => {
diff --git a/app/javascript/dashboard/store/modules/sidebarSortPreferences.js b/app/javascript/dashboard/store/modules/sidebarSortPreferences.js
new file mode 100644
index 000000000..2973c80be
--- /dev/null
+++ b/app/javascript/dashboard/store/modules/sidebarSortPreferences.js
@@ -0,0 +1,82 @@
+import { LocalStorage } from 'shared/helpers/localStorage';
+import {
+ DEFAULT_SIDEBAR_SORT_PREFERENCES,
+ isValidSidebarSort,
+ normalizeSidebarSortPreferences,
+} from 'dashboard/helper/sidebarSort';
+
+const STORAGE_NAME = 'chatwoot_sidebar_sort_preferences';
+export const SET_SIDEBAR_SORT_PREFERENCES = 'SET_SIDEBAR_SORT_PREFERENCES';
+
+const getPreferenceScope = rootGetters => {
+ const currentUserId = rootGetters.getCurrentUserID;
+ const currentAccountId = rootGetters.getCurrentAccountId;
+
+ if (!currentUserId || !currentAccountId) return null;
+
+ return `${currentUserId}:${currentAccountId}`;
+};
+
+export const state = {
+ preferences: { ...DEFAULT_SIDEBAR_SORT_PREFERENCES },
+ storageKey: null,
+};
+
+export const getters = {
+ getSectionSort: $state => section => {
+ return (
+ $state.preferences[section] || DEFAULT_SIDEBAR_SORT_PREFERENCES[section]
+ );
+ },
+};
+
+export const actions = {
+ initialize({ commit, rootGetters }) {
+ const storageKey = getPreferenceScope(rootGetters);
+ const storedPreferences = storageKey
+ ? LocalStorage.getFromJsonStore(STORAGE_NAME, storageKey)
+ : {};
+
+ commit(SET_SIDEBAR_SORT_PREFERENCES, {
+ preferences: normalizeSidebarSortPreferences(storedPreferences),
+ storageKey,
+ });
+ },
+ setSectionSort({ commit, rootGetters, state: currentState }, payload = {}) {
+ const { section, sortBy } = payload;
+
+ if (!isValidSidebarSort(section, sortBy)) return;
+
+ const storageKey =
+ currentState.storageKey || getPreferenceScope(rootGetters);
+ const preferences = {
+ ...currentState.preferences,
+ [section]: sortBy,
+ };
+
+ commit(SET_SIDEBAR_SORT_PREFERENCES, {
+ preferences,
+ storageKey,
+ });
+
+ if (storageKey) {
+ LocalStorage.updateJsonStore(STORAGE_NAME, storageKey, preferences);
+ }
+ },
+};
+
+export const mutations = {
+ [SET_SIDEBAR_SORT_PREFERENCES]($state, payload = {}) {
+ const { preferences = {}, storageKey = null } = payload;
+ $state.preferences = normalizeSidebarSortPreferences(preferences);
+ $state.storageKey = storageKey;
+ },
+};
+
+export default {
+ namespaced: true,
+ state,
+ getters,
+ actions,
+ mutations,
+};
diff --git a/app/javascript/dashboard/store/modules/specs/contactConversations/actions.spec.js b/app/javascript/dashboard/store/modules/specs/contactConversations/actions.spec.js
index b403c0d4a..dd3b52587 100644
--- a/app/javascript/dashboard/store/modules/specs/contactConversations/actions.spec.js
+++ b/app/javascript/dashboard/store/modules/specs/contactConversations/actions.spec.js
@@ -282,6 +282,24 @@ describe('createConversationPayload', () => {
expect(payload.get('assignee_id')).toBe(options.params.assigneeId);
expect(payload.getAll('message[attachments][]')).toEqual([]);
});
+
+ it('omits mail_subject when mailSubject is undefined', () => {
+ const options = {
+ params: {
+ inboxId: '1',
+ message: {
+ content: 'Test message content',
+ },
+ sourceId: '12',
+ assigneeId: '123',
+ },
+ contactId: '23',
+ };
+
+ const payload = createConversationPayload(options);
+
+ expect(payload.has('additional_attributes[mail_subject]')).toBe(false);
+ });
});
describe('createWhatsAppConversationPayload', () => {
diff --git a/app/javascript/dashboard/store/modules/specs/contacts/actions.spec.js b/app/javascript/dashboard/store/modules/specs/contacts/actions.spec.js
index 00b7bb83b..bb2f0393b 100644
--- a/app/javascript/dashboard/store/modules/specs/contacts/actions.spec.js
+++ b/app/javascript/dashboard/store/modules/specs/contacts/actions.spec.js
@@ -438,4 +438,48 @@ describe('#actions', () => {
]);
});
});
+
+ describe('#fetchAttachments', () => {
+ const attachments = [
+ { id: 11, message_id: 21, file_type: 'image' },
+ { id: 12, message_id: 22, file_type: 'file' },
+ ];
+
+ it('fetches and stores attachments on the contact record', async () => {
+ axios.get.mockResolvedValue({ data: { payload: attachments } });
+ const state = { records: { 1: { id: 1 } } };
+ await actions.fetchAttachments({ commit, state }, 1);
+ expect(commit.mock.calls).toEqual([
+ [types.SET_CONTACT_UI_FLAG, { isFetchingAttachments: true }],
+ [types.SET_CONTACT_ATTACHMENTS, { id: 1, data: attachments }],
+ [types.SET_CONTACT_UI_FLAG, { isFetchingAttachments: false }],
+ ]);
+ });
+
+ it('refetches even when attachments are already cached', async () => {
+ axios.get.mockResolvedValue({ data: { payload: attachments } });
+ const state = {
+ records: { 1: { id: 1, attachments: [{ id: 99 }] } },
+ };
+ await actions.fetchAttachments({ commit, state }, 1);
+ expect(axios.get).toHaveBeenCalled();
+ expect(commit.mock.calls).toEqual([
+ [types.SET_CONTACT_UI_FLAG, { isFetchingAttachments: true }],
+ [types.SET_CONTACT_ATTACHMENTS, { id: 1, data: attachments }],
+ [types.SET_CONTACT_UI_FLAG, { isFetchingAttachments: false }],
+ ]);
+ });
+
+ it('clears the loading flag and rethrows when the API errors', async () => {
+ axios.get.mockRejectedValue(new Error('Network error'));
+ const state = { records: { 1: { id: 1 } } };
+ await expect(
+ actions.fetchAttachments({ commit, state }, 1)
+ ).rejects.toThrow('Network error');
+ expect(commit.mock.calls).toEqual([
+ [types.SET_CONTACT_UI_FLAG, { isFetchingAttachments: true }],
+ [types.SET_CONTACT_UI_FLAG, { isFetchingAttachments: false }],
+ ]);
+ });
+ });
});
diff --git a/app/javascript/dashboard/store/modules/specs/contacts/getters.spec.js b/app/javascript/dashboard/store/modules/specs/contacts/getters.spec.js
index 38973ec9d..260ca8f2a 100644
--- a/app/javascript/dashboard/store/modules/specs/contacts/getters.spec.js
+++ b/app/javascript/dashboard/store/modules/specs/contacts/getters.spec.js
@@ -50,4 +50,22 @@ describe('#getters', () => {
};
expect(getters.getAppliedContactFilters(state)).toEqual(filters);
});
+
+ describe('getContactAttachments', () => {
+ it('returns the attachments stored on the contact record', () => {
+ const data = [{ id: 11, file_type: 'image' }];
+ const state = { records: { 1: { id: 1, attachments: data } } };
+ expect(getters.getContactAttachments(state)(1)).toEqual(data);
+ });
+
+ it('returns an empty array when the contact has no cached attachments', () => {
+ const state = { records: { 1: { id: 1 } } };
+ expect(getters.getContactAttachments(state)(1)).toEqual([]);
+ });
+
+ it('returns an empty array when the contact is not in the store', () => {
+ const state = { records: {} };
+ expect(getters.getContactAttachments(state)(99)).toEqual([]);
+ });
+ });
});
diff --git a/app/javascript/dashboard/store/modules/specs/contacts/mutations.spec.js b/app/javascript/dashboard/store/modules/specs/contacts/mutations.spec.js
index 4eb40ff61..962cc6ea4 100644
--- a/app/javascript/dashboard/store/modules/specs/contacts/mutations.spec.js
+++ b/app/javascript/dashboard/store/modules/specs/contacts/mutations.spec.js
@@ -63,6 +63,21 @@ describe('#mutations', () => {
1: { id: 1, name: 'contact2', email: 'contact2@chatwoot.com' },
});
});
+
+ it('preserves a cached attachments list across edits', () => {
+ const attachments = [{ id: 11, file_type: 'image' }];
+ const state = {
+ records: {
+ 1: { id: 1, name: 'contact1', attachments },
+ },
+ };
+ mutations[types.EDIT_CONTACT](state, { id: 1, name: 'contact2' });
+ expect(state.records[1]).toEqual({
+ id: 1,
+ name: 'contact2',
+ attachments,
+ });
+ });
});
describe('#SET_CONTACT_FILTERS', () => {
@@ -102,4 +117,33 @@ describe('#mutations', () => {
expect(state.appliedFilters).toEqual([]);
});
});
+
+ describe('#SET_CONTACT_ATTACHMENTS', () => {
+ it('attaches the list to the existing contact record', () => {
+ const state = { records: { 1: { id: 1, name: 'Sivin' } } };
+ const data = [{ id: 11, file_type: 'image' }];
+ mutations[types.SET_CONTACT_ATTACHMENTS](state, { id: 1, data });
+ expect(state.records[1]).toEqual({
+ id: 1,
+ name: 'Sivin',
+ attachments: data,
+ });
+ });
+
+ it('creates a record shell when the contact is not yet loaded', () => {
+ const state = { records: {} };
+ const data = [{ id: 12, file_type: 'file' }];
+ mutations[types.SET_CONTACT_ATTACHMENTS](state, { id: 5, data });
+ expect(state.records[5]).toEqual({ attachments: data });
+ });
+
+ it('replaces an existing attachment list', () => {
+ const state = {
+ records: { 1: { id: 1, attachments: [{ id: 99 }] } },
+ };
+ const data = [{ id: 11 }];
+ mutations[types.SET_CONTACT_ATTACHMENTS](state, { id: 1, data });
+ expect(state.records[1].attachments).toEqual(data);
+ });
+ });
});
diff --git a/app/javascript/dashboard/store/modules/specs/sidebarSortPreferences/actions.spec.js b/app/javascript/dashboard/store/modules/specs/sidebarSortPreferences/actions.spec.js
new file mode 100644
index 000000000..7fee1d018
--- /dev/null
+++ b/app/javascript/dashboard/store/modules/specs/sidebarSortPreferences/actions.spec.js
@@ -0,0 +1,106 @@
+import { LocalStorage } from 'shared/helpers/localStorage';
+import {
+ DEFAULT_SIDEBAR_SORT_PREFERENCES,
+ SIDEBAR_SORT_KEYS,
+ SIDEBAR_SORT_SECTIONS,
+} from 'dashboard/helper/sidebarSort';
+import {
+ SET_SIDEBAR_SORT_PREFERENCES,
+ actions,
+} from '../../sidebarSortPreferences';
+
+vi.mock('shared/helpers/localStorage', () => ({
+ LocalStorage: {
+ getFromJsonStore: vi.fn(),
+ updateJsonStore: vi.fn(),
+ },
+}));
+
+const rootGetters = {
+ getCurrentUserID: 1,
+ getCurrentAccountId: 2,
+};
+
+describe('#actions', () => {
+ const commit = vi.fn();
+
+ beforeEach(() => {
+ commit.mockClear();
+ LocalStorage.getFromJsonStore.mockReset();
+ LocalStorage.updateJsonStore.mockReset();
+ });
+
+ describe('#initialize', () => {
+ it('loads scoped preferences from local storage', () => {
+ LocalStorage.getFromJsonStore.mockReturnValue({
+ [SIDEBAR_SORT_SECTIONS.FOLDERS]: SIDEBAR_SORT_KEYS.ALPHABETICAL_ASC,
+ });
+
+ actions.initialize({ commit, rootGetters });
+
+ expect(LocalStorage.getFromJsonStore).toHaveBeenCalledWith(
+ 'chatwoot_sidebar_sort_preferences',
+ '1:2'
+ );
+ expect(commit).toHaveBeenCalledWith(SET_SIDEBAR_SORT_PREFERENCES, {
+ preferences: {
+ ...DEFAULT_SIDEBAR_SORT_PREFERENCES,
+ [SIDEBAR_SORT_SECTIONS.FOLDERS]: SIDEBAR_SORT_KEYS.ALPHABETICAL_ASC,
+ },
+ storageKey: '1:2',
+ });
+ });
+ });
+
+ describe('#setSectionSort', () => {
+ it('persists valid preferences to local storage', () => {
+ const state = {
+ preferences: DEFAULT_SIDEBAR_SORT_PREFERENCES,
+ storageKey: '1:2',
+ };
+
+ actions.setSectionSort(
+ { commit, rootGetters, state },
+ {
+ section: SIDEBAR_SORT_SECTIONS.LABELS,
+ sortBy: SIDEBAR_SORT_KEYS.ALPHABETICAL_DESC,
+ }
+ );
+
+ const preferences = {
+ ...DEFAULT_SIDEBAR_SORT_PREFERENCES,
+ [SIDEBAR_SORT_SECTIONS.LABELS]: SIDEBAR_SORT_KEYS.ALPHABETICAL_DESC,
+ };
+
+ expect(commit).toHaveBeenCalledWith(SET_SIDEBAR_SORT_PREFERENCES, {
+ preferences,
+ storageKey: '1:2',
+ });
+ expect(LocalStorage.updateJsonStore).toHaveBeenCalledWith(
+ 'chatwoot_sidebar_sort_preferences',
+ '1:2',
+ preferences
+ );
+ });
+
+ it('ignores invalid preferences', () => {
+ actions.setSectionSort(
+ {
+ commit,
+ rootGetters,
+ state: {
+ preferences: DEFAULT_SIDEBAR_SORT_PREFERENCES,
+ storageKey: '1:2',
+ },
+ },
+ {
+ section: SIDEBAR_SORT_SECTIONS.FOLDERS,
+ sortBy: SIDEBAR_SORT_KEYS.UNREAD_COUNT_DESC,
+ }
+ );
+
+ expect(commit).not.toHaveBeenCalled();
+ expect(LocalStorage.updateJsonStore).not.toHaveBeenCalled();
+ });
+ });
+});
diff --git a/app/javascript/dashboard/store/modules/specs/sidebarSortPreferences/getters.spec.js b/app/javascript/dashboard/store/modules/specs/sidebarSortPreferences/getters.spec.js
new file mode 100644
index 000000000..6c43e23bc
--- /dev/null
+++ b/app/javascript/dashboard/store/modules/specs/sidebarSortPreferences/getters.spec.js
@@ -0,0 +1,31 @@
+import {
+ DEFAULT_SIDEBAR_SORT_PREFERENCES,
+ SIDEBAR_SORT_KEYS,
+ SIDEBAR_SORT_SECTIONS,
+} from 'dashboard/helper/sidebarSort';
+import { getters } from '../../sidebarSortPreferences';
+
+describe('#getters', () => {
+ it('returns section sort preference', () => {
+ const state = {
+ preferences: {
+ ...DEFAULT_SIDEBAR_SORT_PREFERENCES,
+ [SIDEBAR_SORT_SECTIONS.TEAMS]: SIDEBAR_SORT_KEYS.CREATED_ASC,
+ },
+ };
+
+ expect(getters.getSectionSort(state)(SIDEBAR_SORT_SECTIONS.TEAMS)).toBe(
+ SIDEBAR_SORT_KEYS.CREATED_ASC
+ );
+ });
+
+ it('falls back to default section sort preference', () => {
+ const state = {
+ preferences: {},
+ };
+
+ expect(getters.getSectionSort(state)(SIDEBAR_SORT_SECTIONS.LABELS)).toBe(
+ SIDEBAR_SORT_KEYS.UNREAD_COUNT_DESC
+ );
+ });
+});
diff --git a/app/javascript/dashboard/store/modules/specs/sidebarSortPreferences/mutations.spec.js b/app/javascript/dashboard/store/modules/specs/sidebarSortPreferences/mutations.spec.js
new file mode 100644
index 000000000..2f19c84bd
--- /dev/null
+++ b/app/javascript/dashboard/store/modules/specs/sidebarSortPreferences/mutations.spec.js
@@ -0,0 +1,34 @@
+import {
+ DEFAULT_SIDEBAR_SORT_PREFERENCES,
+ SIDEBAR_SORT_KEYS,
+ SIDEBAR_SORT_SECTIONS,
+} from 'dashboard/helper/sidebarSort';
+import {
+ SET_SIDEBAR_SORT_PREFERENCES,
+ mutations,
+} from '../../sidebarSortPreferences';
+
+describe('#mutations', () => {
+ it('sets normalized preferences', () => {
+ const state = {
+ preferences: {},
+ storageKey: null,
+ };
+
+ mutations[SET_SIDEBAR_SORT_PREFERENCES](state, {
+ preferences: {
+ [SIDEBAR_SORT_SECTIONS.FOLDERS]: SIDEBAR_SORT_KEYS.ALPHABETICAL_DESC,
+ [SIDEBAR_SORT_SECTIONS.LABELS]: 'invalid',
+ },
+ storageKey: '1:2',
+ });
+
+ expect(state).toEqual({
+ preferences: {
+ ...DEFAULT_SIDEBAR_SORT_PREFERENCES,
+ [SIDEBAR_SORT_SECTIONS.FOLDERS]: SIDEBAR_SORT_KEYS.ALPHABETICAL_DESC,
+ },
+ storageKey: '1:2',
+ });
+ });
+});
diff --git a/app/javascript/dashboard/store/mutation-types.js b/app/javascript/dashboard/store/mutation-types.js
index 1597b7ea6..059d9636e 100644
--- a/app/javascript/dashboard/store/mutation-types.js
+++ b/app/javascript/dashboard/store/mutation-types.js
@@ -139,6 +139,7 @@ export default {
SET_CONTACT_META: 'SET_CONTACT_META',
SET_CONTACT_UI_FLAG: 'SET_CONTACT_UI_FLAG',
SET_CONTACT_ITEM: 'SET_CONTACT_ITEM',
+ SET_CONTACT_ATTACHMENTS: 'SET_CONTACT_ATTACHMENTS',
SET_CONTACTS: 'SET_CONTACTS',
APPEND_CONTACTS: 'APPEND_CONTACTS',
CLEAR_CONTACTS: 'CLEAR_CONTACTS',
diff --git a/app/javascript/shared/components/emoji/EmojiInput.vue b/app/javascript/shared/components/emoji/EmojiInput.vue
deleted file mode 100644
index 586496d8a..000000000
--- a/app/javascript/shared/components/emoji/EmojiInput.vue
+++ /dev/null
@@ -1,255 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
- {{ selectedKey }}
-
-
-
-
-
-
-
-
- {{ category.name }}
-
-
-
-
-
-
-
-
-
-
- {{ $t('EMOJI.NOT_FOUND') }}
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/javascript/shared/components/emoji/EmojiPicker.vue b/app/javascript/shared/components/emoji/EmojiPicker.vue
new file mode 100644
index 000000000..a671a9cef
--- /dev/null
+++ b/app/javascript/shared/components/emoji/EmojiPicker.vue
@@ -0,0 +1,99 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ t('EMOJI_ICON_PICKER.NO_EMOJI') }}
+
+
+
+
+
diff --git a/app/javascript/shared/components/emoji/emojisGroup.json b/app/javascript/shared/components/emoji/emojisGroup.json
index ebb14ebce..090ea5762 100644
--- a/app/javascript/shared/components/emoji/emojisGroup.json
+++ b/app/javascript/shared/components/emoji/emojisGroup.json
@@ -3,11 +3,7 @@
"name": "Smileys & Emotion",
"slug": "smileys_emotion",
"emojis": [
- {
- "emoji": "😀",
- "name": "grinning face",
- "slug": "grinning_face"
- },
+ { "emoji": "😀", "name": "grinning face", "slug": "grinning_face" },
{
"emoji": "😃",
"name": "grinning face with big eyes",
@@ -48,21 +44,13 @@
"name": "slightly smiling face",
"slug": "slightly_smiling_face_smile"
},
- {
- "emoji": "🙃",
- "name": "upside-down face",
- "slug": "upside_down_face"
- },
+ { "emoji": "🙃", "name": "upside-down face", "slug": "upside_down_face" },
{
"emoji": "🫠",
"name": "melting face",
"slug": "melting_face_disappear_dissolve_melt_liquid"
},
- {
- "emoji": "😉",
- "name": "winking face",
- "slug": "winking_face"
- },
+ { "emoji": "😉", "name": "winking face", "slug": "winking_face" },
{
"emoji": "😊",
"name": "smiling face with smiling eyes",
@@ -93,11 +81,7 @@
"name": "face blowing a kiss",
"slug": "face_blowing_a_kiss"
},
- {
- "emoji": "😗",
- "name": "kissing face",
- "slug": "kissing_face"
- },
+ { "emoji": "😗", "name": "kissing face", "slug": "kissing_face" },
{
"emoji": "😚",
"name": "kissing face with closed eyes",
@@ -118,11 +102,7 @@
"name": "face savoring food",
"slug": "face_savoring_food_delicious_yum_tongue_savouring"
},
- {
- "emoji": "😛",
- "name": "face with tongue",
- "slug": "face_with_tongue"
- },
+ { "emoji": "😛", "name": "face with tongue", "slug": "face_with_tongue" },
{
"emoji": "😜",
"name": "winking face with tongue",
@@ -138,11 +118,7 @@
"name": "squinting face with tongue",
"slug": "squinting_face_with_tongue_eye_face_horrible_tongue"
},
- {
- "emoji": "🤑",
- "name": "money-mouth face",
- "slug": "money_mouth_face"
- },
+ { "emoji": "🤑", "name": "money-mouth face", "slug": "money_mouth_face" },
{
"emoji": "🤗",
"name": "smiling face with open hands",
@@ -168,11 +144,7 @@
"name": "shushing face",
"slug": "shushing_face_quiet_silence"
},
- {
- "emoji": "🤔",
- "name": "thinking face",
- "slug": "thinking_face"
- },
+ { "emoji": "🤔", "name": "thinking face", "slug": "thinking_face" },
{
"emoji": "🫡",
"name": "saluting face",
@@ -213,11 +185,7 @@
"name": "face in clouds",
"slug": "face_in_clouds_absentminded_fog"
},
- {
- "emoji": "😏",
- "name": "smirking face",
- "slug": "smirking_face"
- },
+ { "emoji": "😏", "name": "smirking face", "slug": "smirking_face" },
{
"emoji": "😒",
"name": "unamused face",
@@ -228,11 +196,7 @@
"name": "face with rolling eyes",
"slug": "face_with_rolling_eyes_eyeroll"
},
- {
- "emoji": "😬",
- "name": "grimacing face",
- "slug": "grimacing_face"
- },
+ { "emoji": "😬", "name": "grimacing face", "slug": "grimacing_face" },
{
"emoji": "😮💨",
"name": "face exhaling",
@@ -243,11 +207,7 @@
"name": "lying face",
"slug": "lying_face_lie_pinocchio"
},
- {
- "emoji": "😌",
- "name": "relieved face",
- "slug": "relieved_face"
- },
+ { "emoji": "😌", "name": "relieved face", "slug": "relieved_face" },
{
"emoji": "😔",
"name": "pensive face",
@@ -258,11 +218,7 @@
"name": "sleepy face",
"slug": "sleepy_face_good_night"
},
- {
- "emoji": "🤤",
- "name": "drooling face",
- "slug": "drooling_face"
- },
+ { "emoji": "🤤", "name": "drooling face", "slug": "drooling_face" },
{
"emoji": "😴",
"name": "sleeping face",
@@ -288,11 +244,7 @@
"name": "nauseated face",
"slug": "nauseated_face_vomit"
},
- {
- "emoji": "🤮",
- "name": "face vomiting",
- "slug": "face_vomiting_puke"
- },
+ { "emoji": "🤮", "name": "face vomiting", "slug": "face_vomiting_puke" },
{
"emoji": "🤧",
"name": "sneezing face",
@@ -348,31 +300,19 @@
"name": "smiling face with sunglasses",
"slug": "smiling_face_with_sunglasses_bright_cool_shades"
},
- {
- "emoji": "🤓",
- "name": "nerd face",
- "slug": "nerd_face_geek"
- },
+ { "emoji": "🤓", "name": "nerd face", "slug": "nerd_face_geek" },
{
"emoji": "🧐",
"name": "face with monocle",
"slug": "face_with_monocle_stuffy_wealthy"
},
- {
- "emoji": "😕",
- "name": "confused face",
- "slug": "confused_face_meh"
- },
+ { "emoji": "😕", "name": "confused face", "slug": "confused_face_meh" },
{
"emoji": "🫤",
"name": "face with diagonal mouth",
"slug": "face_with_diagonal_mouth_disappointed_meh_skeptical_unsure"
},
- {
- "emoji": "😟",
- "name": "worried face",
- "slug": "worried_face"
- },
+ { "emoji": "😟", "name": "worried face", "slug": "worried_face" },
{
"emoji": "🙁",
"name": "slightly frowning face",
@@ -393,11 +333,7 @@
"name": "astonished face",
"slug": "astonished_face_shocked_totally"
},
- {
- "emoji": "😳",
- "name": "flushed face",
- "slug": "flushed_face_dazed"
- },
+ { "emoji": "😳", "name": "flushed face", "slug": "flushed_face_dazed" },
{
"emoji": "🥺",
"name": "pleading face",
@@ -413,16 +349,8 @@
"name": "frowning face with open mouth",
"slug": "frowning_face_with_open_mouth"
},
- {
- "emoji": "😧",
- "name": "anguished face",
- "slug": "anguished_face"
- },
- {
- "emoji": "😨",
- "name": "fearful face",
- "slug": "fearful_face_scared"
- },
+ { "emoji": "😧", "name": "anguished face", "slug": "anguished_face" },
+ { "emoji": "😨", "name": "fearful face", "slug": "fearful_face_scared" },
{
"emoji": "😰",
"name": "anxious face with sweat",
@@ -448,16 +376,8 @@
"name": "face screaming in fear",
"slug": "face_screaming_in_fear_munch_scared_scream"
},
- {
- "emoji": "😖",
- "name": "confounded face",
- "slug": "confounded_face"
- },
- {
- "emoji": "😣",
- "name": "persevering face",
- "slug": "persevering_face"
- },
+ { "emoji": "😖", "name": "confounded face", "slug": "confounded_face" },
+ { "emoji": "😣", "name": "persevering face", "slug": "persevering_face" },
{
"emoji": "😞",
"name": "disappointed face",
@@ -468,16 +388,8 @@
"name": "downcast face with sweat",
"slug": "downcast_face_with_sweat_cold"
},
- {
- "emoji": "😩",
- "name": "weary face",
- "slug": "weary_face_tired"
- },
- {
- "emoji": "😫",
- "name": "tired face",
- "slug": "tired_face"
- },
+ { "emoji": "😩", "name": "weary face", "slug": "weary_face_tired" },
+ { "emoji": "😫", "name": "tired face", "slug": "tired_face" },
{
"emoji": "🥱",
"name": "yawning face",
@@ -493,11 +405,7 @@
"name": "enraged face",
"slug": "enraged_face_angry_enraged_mad_red_face"
},
- {
- "emoji": "😠",
- "name": "angry face",
- "slug": "angry_face_mad"
- },
+ { "emoji": "😠", "name": "angry face", "slug": "angry_face_mad" },
{
"emoji": "🤬",
"name": "face with symbols on mouth",
@@ -528,11 +436,7 @@
"name": "pile of poo",
"slug": "pile_of_poo_poop_dung_monster"
},
- {
- "emoji": "🤡",
- "name": "clown face",
- "slug": "clown_face"
- },
+ { "emoji": "🤡", "name": "clown face", "slug": "clown_face" },
{
"emoji": "👹",
"name": "ogre",
@@ -543,11 +447,7 @@
"name": "goblin",
"slug": "goblin_creature_fantasy_goblin_monster_face"
},
- {
- "emoji": "👻",
- "name": "ghost",
- "slug": "ghost_face_monster_fantasy"
- },
+ { "emoji": "👻", "name": "ghost", "slug": "ghost_face_monster_fantasy" },
{
"emoji": "👽",
"name": "alien",
@@ -558,11 +458,7 @@
"name": "alien monster",
"slug": "alien_monster_face_monster_creature_ufo"
},
- {
- "emoji": "🤖",
- "name": "robot",
- "slug": "robot_face_monster_robot"
- },
+ { "emoji": "🤖", "name": "robot", "slug": "robot_face_monster_robot" },
{
"emoji": "😺",
"name": "grinning cat",
@@ -603,11 +499,7 @@
"name": "crying cat",
"slug": "crying_cat_cry_sad_tear"
},
- {
- "emoji": "😾",
- "name": "pouting cat",
- "slug": "pouting_cat_face"
- },
+ { "emoji": "😾", "name": "pouting cat", "slug": "pouting_cat_face" },
{
"emoji": "🙈",
"name": "see-no-evil monkey",
@@ -623,11 +515,7 @@
"name": "speak-no-evil monkey",
"slug": "speak_no_evil_monkey_face_forbidden"
},
- {
- "emoji": "💌",
- "name": "love letter",
- "slug": "love_letter_mail"
- },
+ { "emoji": "💌", "name": "love letter", "slug": "love_letter_mail" },
{
"emoji": "💘",
"name": "heart with arrow",
@@ -653,21 +541,9 @@
"name": "beating heart",
"slug": "beating_heart_heartbeat_pulsating"
},
- {
- "emoji": "💞",
- "name": "revolving hearts",
- "slug": "revolving_hearts"
- },
- {
- "emoji": "💕",
- "name": "two hearts",
- "slug": "two_hearts_love"
- },
- {
- "emoji": "💟",
- "name": "heart decoration",
- "slug": "heart_decoration"
- },
+ { "emoji": "💞", "name": "revolving hearts", "slug": "revolving_hearts" },
+ { "emoji": "💕", "name": "two hearts", "slug": "two_hearts_love" },
+ { "emoji": "💟", "name": "heart decoration", "slug": "heart_decoration" },
{
"emoji": "❣️",
"name": "heart exclamation",
@@ -688,56 +564,16 @@
"name": "mending heart",
"slug": "mending_heart_healthier_improving_recovery_recuperating_well"
},
- {
- "emoji": "❤️",
- "name": "red heart",
- "slug": "red_heart"
- },
- {
- "emoji": "🧡",
- "name": "orange heart",
- "slug": "orange_heart"
- },
- {
- "emoji": "💛",
- "name": "yellow heart",
- "slug": "yellow_heart"
- },
- {
- "emoji": "💚",
- "name": "green heart",
- "slug": "green_heart"
- },
- {
- "emoji": "💙",
- "name": "blue heart",
- "slug": "blue_heart"
- },
- {
- "emoji": "💜",
- "name": "purple heart",
- "slug": "purple_heart"
- },
- {
- "emoji": "🤎",
- "name": "brown heart",
- "slug": "brown_heart"
- },
- {
- "emoji": "🖤",
- "name": "black heart",
- "slug": "black_heart"
- },
- {
- "emoji": "🤍",
- "name": "white heart",
- "slug": "white_heart"
- },
- {
- "emoji": "💋",
- "name": "kiss mark",
- "slug": "kiss_mark_lips"
- },
+ { "emoji": "❤️", "name": "red heart", "slug": "red_heart" },
+ { "emoji": "🧡", "name": "orange heart", "slug": "orange_heart" },
+ { "emoji": "💛", "name": "yellow heart", "slug": "yellow_heart" },
+ { "emoji": "💚", "name": "green heart", "slug": "green_heart" },
+ { "emoji": "💙", "name": "blue heart", "slug": "blue_heart" },
+ { "emoji": "💜", "name": "purple heart", "slug": "purple_heart" },
+ { "emoji": "🤎", "name": "brown heart", "slug": "brown_heart" },
+ { "emoji": "🖤", "name": "black heart", "slug": "black_heart" },
+ { "emoji": "🤍", "name": "white heart", "slug": "white_heart" },
+ { "emoji": "💋", "name": "kiss mark", "slug": "kiss_mark_lips" },
{
"emoji": "💯",
"name": "hundred points",
@@ -753,11 +589,7 @@
"name": "collision",
"slug": "collision_boom_comic_explode"
},
- {
- "emoji": "💫",
- "name": "dizzy",
- "slug": "dizzy_star_comic"
- },
+ { "emoji": "💫", "name": "dizzy", "slug": "dizzy_star_comic" },
{
"emoji": "💦",
"name": "sweat droplets",
@@ -768,11 +600,7 @@
"name": "dashing away",
"slug": "dashing_away_running_comic"
},
- {
- "emoji": "🕳️",
- "name": "hole",
- "slug": "hole"
- },
+ { "emoji": "🕳️", "name": "hole", "slug": "hole" },
{
"emoji": "💬",
"name": "speech balloon",
@@ -825,7 +653,7 @@
"slug": "hand_with_fingers_splayed"
},
{
- "emoji": "✋",
+ "emoji": "✋️",
"name": "raised hand",
"slug": "raised_hand_high_5_high_five"
},
@@ -854,11 +682,7 @@
"name": "palm up hand",
"slug": "palm_up_hand_catch_beckon_come_offer"
},
- {
- "emoji": "👌",
- "name": "OK hand",
- "slug": "ok_hand"
- },
+ { "emoji": "👌", "name": "OK hand", "slug": "ok_hand" },
{
"emoji": "🤌",
"name": "pinched fingers",
@@ -869,11 +693,7 @@
"name": "pinching hand",
"slug": "pinching_hand_small_amount"
},
- {
- "emoji": "✌️",
- "name": "victory hand",
- "slug": "victory_hand_v"
- },
+ { "emoji": "✌️", "name": "victory hand", "slug": "victory_hand_v" },
{
"emoji": "🤞",
"name": "crossed fingers",
@@ -914,11 +734,7 @@
"name": "backhand index pointing up",
"slug": "backhand_index_pointing_up_hand_point_up_finger"
},
- {
- "emoji": "🖕",
- "name": "middle finger",
- "slug": "middle_finger_hand"
- },
+ { "emoji": "🖕", "name": "middle finger", "slug": "middle_finger_hand" },
{
"emoji": "👇",
"name": "backhand index pointing down",
@@ -934,18 +750,10 @@
"name": "index pointing at the viewer",
"slug": "index_pointing_at_the_viewer_you_point"
},
+ { "emoji": "👍", "name": "thumbs up", "slug": "thumbs_up_+1_hand" },
+ { "emoji": "👎", "name": "thumbs down", "slug": "thumbs_down_-1_hand" },
{
- "emoji": "👍",
- "name": "thumbs up",
- "slug": "thumbs_up_+1_hand"
- },
- {
- "emoji": "👎",
- "name": "thumbs down",
- "slug": "thumbs_down_-1_hand"
- },
- {
- "emoji": "✊",
+ "emoji": "✊️",
"name": "raised fist",
"slug": "raised_fist_hand_punch_clenched_fist"
},
@@ -974,16 +782,8 @@
"name": "raising hands",
"slug": "raising_hands_celebration_gesture_hooray_raised_hands"
},
- {
- "emoji": "🫶",
- "name": "heart hands",
- "slug": "heart_hands_love"
- },
- {
- "emoji": "👐",
- "name": "open hands",
- "slug": "open_hands"
- },
+ { "emoji": "🫶", "name": "heart hands", "slug": "heart_hands_love" },
+ { "emoji": "👐", "name": "open hands", "slug": "open_hands" },
{
"emoji": "🤲",
"name": "palms up together",
@@ -999,46 +799,26 @@
"name": "folded hands",
"slug": "folded_hands_ask_hand_high_5_high_five_please_pray_thanks"
},
- {
- "emoji": "✍️",
- "name": "writing hand",
- "slug": "writing_hand_write"
- },
+ { "emoji": "✍️", "name": "writing hand", "slug": "writing_hand_write" },
{
"emoji": "💅",
"name": "nail polish",
"slug": "nail_polish_care_manicure_cosmetics"
},
- {
- "emoji": "🤳",
- "name": "selfie",
- "slug": "selfie_camera_phone_selfie"
- },
+ { "emoji": "🤳", "name": "selfie", "slug": "selfie_camera_phone_selfie" },
{
"emoji": "💪",
"name": "flexed biceps",
"slug": "flexed_biceps_comic_flex_muscle_strength"
},
- {
- "emoji": "🦾",
- "name": "mechanical arm",
- "slug": "mechanical_arm"
- },
+ { "emoji": "🦾", "name": "mechanical arm", "slug": "mechanical_arm" },
{
"emoji": "🦿",
"name": "mechanical leg",
"slug": "mechanical_leg_kick"
},
- {
- "emoji": "🦵",
- "name": "leg",
- "slug": "leg_kick_limb"
- },
- {
- "emoji": "🦶",
- "name": "foot",
- "slug": "foot_kick_stomp"
- },
+ { "emoji": "🦵", "name": "leg", "slug": "leg_kick_limb" },
+ { "emoji": "🦶", "name": "foot", "slug": "foot_kick_stomp" },
{
"emoji": "👂",
"name": "ear",
@@ -1049,16 +829,8 @@
"name": "ear with hearing aid",
"slug": "ear_with_hearing_aid_accessibility"
},
- {
- "emoji": "👃",
- "name": "nose",
- "slug": "nose_body"
- },
- {
- "emoji": "🧠",
- "name": "brain",
- "slug": "brain_intelligent"
- },
+ { "emoji": "👃", "name": "nose", "slug": "nose_body" },
+ { "emoji": "🧠", "name": "brain", "slug": "brain_intelligent" },
{
"emoji": "🫀",
"name": "anatomical heart",
@@ -1069,61 +841,25 @@
"name": "lungs",
"slug": "lungs_organ_exhalation_inhalation_respiration"
},
- {
- "emoji": "🦷",
- "name": "tooth",
- "slug": "tooth_dentist"
- },
- {
- "emoji": "🦴",
- "name": "bone",
- "slug": "bone_skeleton"
- },
- {
- "emoji": "👀",
- "name": "eyes",
- "slug": "eyes_face_eye"
- },
- {
- "emoji": "👁️",
- "name": "eye",
- "slug": "eye_body"
- },
- {
- "emoji": "👅",
- "name": "tongue",
- "slug": "tongue_body"
- },
- {
- "emoji": "👄",
- "name": "mouth",
- "slug": "mouth_lips"
- },
+ { "emoji": "🦷", "name": "tooth", "slug": "tooth_dentist" },
+ { "emoji": "🦴", "name": "bone", "slug": "bone_skeleton" },
+ { "emoji": "👀", "name": "eyes", "slug": "eyes_face_eye" },
+ { "emoji": "👁️", "name": "eye", "slug": "eye_body" },
+ { "emoji": "👅", "name": "tongue", "slug": "tongue_body" },
+ { "emoji": "👄", "name": "mouth", "slug": "mouth_lips" },
{
"emoji": "🫦",
"name": "biting lip",
"slug": "biting_lip_anxious_fear_flirting_nervous_uncomfortable_worried"
},
- {
- "emoji": "👶",
- "name": "baby",
- "slug": "baby_young"
- },
+ { "emoji": "👶", "name": "baby", "slug": "baby_young" },
{
"emoji": "🧒",
"name": "child",
"slug": "child_gender_neutral_young_unspecified_gender"
},
- {
- "emoji": "👦",
- "name": "boy",
- "slug": "boy_young"
- },
- {
- "emoji": "👧",
- "name": "girl",
- "slug": "girl_Virgo_young_zodiac"
- },
+ { "emoji": "👦", "name": "boy", "slug": "boy_young" },
+ { "emoji": "👧", "name": "girl", "slug": "girl_Virgo_young_zodiac" },
{
"emoji": "🧑",
"name": "person",
@@ -1134,66 +870,26 @@
"name": "person blond hair",
"slug": "person_blond_hair"
},
- {
- "emoji": "👨",
- "name": "man",
- "slug": "man_adult_man"
- },
+ { "emoji": "👨", "name": "man", "slug": "man_adult_man" },
{
"emoji": "🧔",
"name": "person beard",
"slug": "person_beard_bewhiskered"
},
- {
- "emoji": "🧔♂️",
- "name": "man beard",
- "slug": "man_beard"
- },
- {
- "emoji": "🧔♀️",
- "name": "woman beard",
- "slug": "woman_beard"
- },
- {
- "emoji": "👨🦰",
- "name": "man red hair",
- "slug": "man_red_hair"
- },
- {
- "emoji": "👨🦱",
- "name": "man curly hair",
- "slug": "man_curly_hair"
- },
- {
- "emoji": "👨🦳",
- "name": "man white hair",
- "slug": "man_white_hair"
- },
- {
- "emoji": "👨🦲",
- "name": "man bald",
- "slug": "man_bald"
- },
- {
- "emoji": "👩",
- "name": "woman",
- "slug": "woman_adult"
- },
- {
- "emoji": "👩🦰",
- "name": "woman red hair",
- "slug": "woman_red_hair"
- },
+ { "emoji": "🧔♂️", "name": "man beard", "slug": "man_beard" },
+ { "emoji": "🧔♀️", "name": "woman beard", "slug": "woman_beard" },
+ { "emoji": "👨🦰", "name": "man red hair", "slug": "man_red_hair" },
+ { "emoji": "👨🦱", "name": "man curly hair", "slug": "man_curly_hair" },
+ { "emoji": "👨🦳", "name": "man white hair", "slug": "man_white_hair" },
+ { "emoji": "👨🦲", "name": "man bald", "slug": "man_bald" },
+ { "emoji": "👩", "name": "woman", "slug": "woman_adult" },
+ { "emoji": "👩🦰", "name": "woman red hair", "slug": "woman_red_hair" },
{
"emoji": "🧑🦰",
"name": "person red hair",
"slug": "person_red_hair_unspecified_gender"
},
- {
- "emoji": "👩🦱",
- "name": "woman curly hair",
- "slug": "woman_curly_hair"
- },
+ { "emoji": "👩🦱", "name": "woman curly hair", "slug": "woman_curly_hair" },
{
"emoji": "🧑🦱",
"name": "person curly hair",
@@ -1209,51 +905,19 @@
"name": "person white hair",
"slug": "person_white_hair"
},
- {
- "emoji": "👩🦲",
- "name": "woman bald",
- "slug": "woman_bald"
- },
- {
- "emoji": "🧑🦲",
- "name": "person bald",
- "slug": "person_bald"
- },
- {
- "emoji": "👱♀️",
- "name": "woman blond hair",
- "slug": "woman_blond_hair"
- },
- {
- "emoji": "👱♂️",
- "name": "man blond hair",
- "slug": "man_blond_hair"
- },
- {
- "emoji": "🧓",
- "name": "older person",
- "slug": "older_person"
- },
- {
- "emoji": "👴",
- "name": "old man",
- "slug": "old_man"
- },
- {
- "emoji": "👵",
- "name": "old woman",
- "slug": "old_woman"
- },
+ { "emoji": "👩🦲", "name": "woman bald", "slug": "woman_bald" },
+ { "emoji": "🧑🦲", "name": "person bald", "slug": "person_bald" },
+ { "emoji": "👱♀️", "name": "woman blond hair", "slug": "woman_blond_hair" },
+ { "emoji": "👱♂️", "name": "man blond hair", "slug": "man_blond_hair" },
+ { "emoji": "🧓", "name": "older person", "slug": "older_person" },
+ { "emoji": "👴", "name": "old man", "slug": "old_man" },
+ { "emoji": "👵", "name": "old woman", "slug": "old_woman" },
{
"emoji": "🙍",
"name": "person frowning",
"slug": "person_frowning_gesture"
},
- {
- "emoji": "🙍♂️",
- "name": "man frowning",
- "slug": "man_frowning_gesture"
- },
+ { "emoji": "🙍♂️", "name": "man frowning", "slug": "man_frowning_gesture" },
{
"emoji": "🙍♀️",
"name": "woman frowning",
@@ -1264,11 +928,7 @@
"name": "person pouting",
"slug": "person_pouting_gesture"
},
- {
- "emoji": "🙎♂️",
- "name": "man pouting",
- "slug": "man_pouting_gesture"
- },
+ { "emoji": "🙎♂️", "name": "man pouting", "slug": "man_pouting_gesture" },
{
"emoji": "🙎♀️",
"name": "woman pouting",
@@ -1339,16 +999,8 @@
"name": "deaf person",
"slug": "deaf_person_ear_deafness_accessibility"
},
- {
- "emoji": "🧏♂️",
- "name": "deaf man",
- "slug": "deaf_man"
- },
- {
- "emoji": "🧏♀️",
- "name": "deaf woman",
- "slug": "deaf_woman"
- },
+ { "emoji": "🧏♂️", "name": "deaf man", "slug": "deaf_man" },
+ { "emoji": "🧏♀️", "name": "deaf woman", "slug": "deaf_woman" },
{
"emoji": "🙇",
"name": "person bowing",
@@ -1409,16 +1061,8 @@
"name": "woman health worker",
"slug": "woman_health_worker_doctor_nurse_therapist"
},
- {
- "emoji": "🧑🎓",
- "name": "student",
- "slug": "student_graduate"
- },
- {
- "emoji": "👨🎓",
- "name": "man student",
- "slug": "man_student_graduate"
- },
+ { "emoji": "🧑🎓", "name": "student", "slug": "student_graduate" },
+ { "emoji": "👨🎓", "name": "man student", "slug": "man_student_graduate" },
{
"emoji": "👩🎓",
"name": "woman student",
@@ -1439,11 +1083,7 @@
"name": "woman teacher",
"slug": "woman_teacher_instructor_professor"
},
- {
- "emoji": "🧑⚖️",
- "name": "judge",
- "slug": "judge_scales_justice"
- },
+ { "emoji": "🧑⚖️", "name": "judge", "slug": "judge_scales_justice" },
{
"emoji": "👨⚖️",
"name": "man judge",
@@ -1454,11 +1094,7 @@
"name": "woman judge",
"slug": "woman_judge_scales_justice"
},
- {
- "emoji": "🧑🌾",
- "name": "farmer",
- "slug": "farmer_gardener_rancher"
- },
+ { "emoji": "🧑🌾", "name": "farmer", "slug": "farmer_gardener_rancher" },
{
"emoji": "👨🌾",
"name": "man farmer",
@@ -1469,21 +1105,9 @@
"name": "woman farmer",
"slug": "woman_farmer_gardener_rancher"
},
- {
- "emoji": "🧑🍳",
- "name": "cook",
- "slug": "cook_chef"
- },
- {
- "emoji": "👨🍳",
- "name": "man cook",
- "slug": "man_cook"
- },
- {
- "emoji": "👩🍳",
- "name": "woman cook",
- "slug": "woman_cook"
- },
+ { "emoji": "🧑🍳", "name": "cook", "slug": "cook_chef" },
+ { "emoji": "👨🍳", "name": "man cook", "slug": "man_cook" },
+ { "emoji": "👩🍳", "name": "woman cook", "slug": "woman_cook" },
{
"emoji": "🧑🔧",
"name": "mechanic",
@@ -1574,71 +1198,23 @@
"name": "woman singer",
"slug": "woman_singer_actress_entertainer_rock_star"
},
- {
- "emoji": "🧑🎨",
- "name": "artist",
- "slug": "artist"
- },
- {
- "emoji": "👨🎨",
- "name": "man artist",
- "slug": "man_artist"
- },
- {
- "emoji": "👩🎨",
- "name": "woman artist",
- "slug": "woman_artist"
- },
- {
- "emoji": "🧑✈️",
- "name": "pilot",
- "slug": "pilot_plane"
- },
- {
- "emoji": "👨✈️",
- "name": "man pilot",
- "slug": "man_pilot"
- },
- {
- "emoji": "👩✈️",
- "name": "woman pilot",
- "slug": "woman_pilot"
- },
- {
- "emoji": "🧑🚀",
- "name": "astronaut",
- "slug": "astronaut"
- },
- {
- "emoji": "👨🚀",
- "name": "man astronaut",
- "slug": "man_astronaut"
- },
- {
- "emoji": "👩🚀",
- "name": "woman astronaut",
- "slug": "woman_astronaut"
- },
- {
- "emoji": "🧑🚒",
- "name": "firefighter",
- "slug": "firefighter"
- },
- {
- "emoji": "👨🚒",
- "name": "man firefighter",
- "slug": "man_firefighter"
- },
+ { "emoji": "🧑🎨", "name": "artist", "slug": "artist" },
+ { "emoji": "👨🎨", "name": "man artist", "slug": "man_artist" },
+ { "emoji": "👩🎨", "name": "woman artist", "slug": "woman_artist" },
+ { "emoji": "🧑✈️", "name": "pilot", "slug": "pilot_plane" },
+ { "emoji": "👨✈️", "name": "man pilot", "slug": "man_pilot" },
+ { "emoji": "👩✈️", "name": "woman pilot", "slug": "woman_pilot" },
+ { "emoji": "🧑🚀", "name": "astronaut", "slug": "astronaut" },
+ { "emoji": "👨🚀", "name": "man astronaut", "slug": "man_astronaut" },
+ { "emoji": "👩🚀", "name": "woman astronaut", "slug": "woman_astronaut" },
+ { "emoji": "🧑🚒", "name": "firefighter", "slug": "firefighter" },
+ { "emoji": "👨🚒", "name": "man firefighter", "slug": "man_firefighter" },
{
"emoji": "👩🚒",
"name": "woman firefighter",
"slug": "woman_firefighter"
},
- {
- "emoji": "👮",
- "name": "police officer",
- "slug": "police_officer_cop"
- },
+ { "emoji": "👮", "name": "police officer", "slug": "police_officer_cop" },
{
"emoji": "👮♂️",
"name": "man police officer",
@@ -1649,11 +1225,7 @@
"name": "woman police officer",
"slug": "woman_police_officer_cop"
},
- {
- "emoji": "🕵️",
- "name": "detective",
- "slug": "detective_spy_sleuth"
- },
+ { "emoji": "🕵️", "name": "detective", "slug": "detective_spy_sleuth" },
{
"emoji": "🕵️♂️",
"name": "man detective",
@@ -1664,26 +1236,10 @@
"name": "woman detective",
"slug": "woman_detective_spy_sleuth"
},
- {
- "emoji": "💂",
- "name": "guard",
- "slug": "guard"
- },
- {
- "emoji": "💂♂️",
- "name": "man guard",
- "slug": "man_guard"
- },
- {
- "emoji": "💂♀️",
- "name": "woman guard",
- "slug": "woman_guard"
- },
- {
- "emoji": "🥷",
- "name": "ninja_fighter_stealth",
- "slug": "ninja"
- },
+ { "emoji": "💂", "name": "guard", "slug": "guard" },
+ { "emoji": "💂♂️", "name": "man guard", "slug": "man_guard" },
+ { "emoji": "💂♀️", "name": "woman guard", "slug": "woman_guard" },
+ { "emoji": "🥷", "name": "ninja_fighter_stealth", "slug": "ninja" },
{
"emoji": "👷",
"name": "construction worker",
@@ -1704,16 +1260,8 @@
"name": "person with crown",
"slug": "person_with_crown"
},
- {
- "emoji": "🤴",
- "name": "prince",
- "slug": "prince"
- },
- {
- "emoji": "👸",
- "name": "princess",
- "slug": "princess"
- },
+ { "emoji": "🤴", "name": "prince", "slug": "prince" },
+ { "emoji": "👸", "name": "princess", "slug": "princess" },
{
"emoji": "👳",
"name": "person wearing turban",
@@ -1739,76 +1287,28 @@
"name": "woman with headscarf",
"slug": "woman_with_headscarf_hijab"
},
- {
- "emoji": "🤵",
- "name": "person in tuxedo",
- "slug": "person_in_tuxedo"
- },
- {
- "emoji": "🤵♂️",
- "name": "man in tuxedo",
- "slug": "man_in_tuxedo"
- },
- {
- "emoji": "🤵♀️",
- "name": "woman in tuxedo",
- "slug": "woman_in_tuxedo"
- },
- {
- "emoji": "👰",
- "name": "person with veil",
- "slug": "person_with_veil"
- },
- {
- "emoji": "👰♂️",
- "name": "man with veil",
- "slug": "man_with_veil"
- },
- {
- "emoji": "👰♀️",
- "name": "woman with veil",
- "slug": "woman_with_veil"
- },
- {
- "emoji": "🤰",
- "name": "pregnant woman",
- "slug": "pregnant_woman"
- },
- {
- "emoji": "🫃",
- "name": "pregnant man",
- "slug": "pregnant_man"
- },
- {
- "emoji": "🫄",
- "name": "pregnant person",
- "slug": "pregnant_person"
- },
- {
- "emoji": "🤱",
- "name": "breast-feeding",
- "slug": "breast_feeding"
- },
+ { "emoji": "🤵", "name": "person in tuxedo", "slug": "person_in_tuxedo" },
+ { "emoji": "🤵♂️", "name": "man in tuxedo", "slug": "man_in_tuxedo" },
+ { "emoji": "🤵♀️", "name": "woman in tuxedo", "slug": "woman_in_tuxedo" },
+ { "emoji": "👰", "name": "person with veil", "slug": "person_with_veil" },
+ { "emoji": "👰♂️", "name": "man with veil", "slug": "man_with_veil" },
+ { "emoji": "👰♀️", "name": "woman with veil", "slug": "woman_with_veil" },
+ { "emoji": "🤰", "name": "pregnant woman", "slug": "pregnant_woman" },
+ { "emoji": "🫃", "name": "pregnant man", "slug": "pregnant_man" },
+ { "emoji": "🫄", "name": "pregnant person", "slug": "pregnant_person" },
+ { "emoji": "🤱", "name": "breast-feeding", "slug": "breast_feeding" },
{
"emoji": "👩🍼",
"name": "woman feeding baby",
"slug": "woman_feeding_baby"
},
- {
- "emoji": "👨🍼",
- "name": "man feeding baby",
- "slug": "man_feeding_baby"
- },
+ { "emoji": "👨🍼", "name": "man feeding baby", "slug": "man_feeding_baby" },
{
"emoji": "🧑🍼",
"name": "person feeding baby",
"slug": "person_feeding_baby"
},
- {
- "emoji": "👼",
- "name": "baby angel",
- "slug": "baby_angel"
- },
+ { "emoji": "👼", "name": "baby angel", "slug": "baby_angel" },
{
"emoji": "🎅",
"name": "Santa Claus",
@@ -1819,26 +1319,10 @@
"name": "Mrs. Claus",
"slug": "mrs_claus_christmas_mother_santa_claus"
},
- {
- "emoji": "🧑🎄",
- "name": "mx claus",
- "slug": "mx_claus"
- },
- {
- "emoji": "🦸",
- "name": "superhero",
- "slug": "superhero"
- },
- {
- "emoji": "🦸♂️",
- "name": "man superhero",
- "slug": "man_superhero"
- },
- {
- "emoji": "🦸♀️",
- "name": "woman superhero",
- "slug": "woman_superhero"
- },
+ { "emoji": "🧑🎄", "name": "mx claus", "slug": "mx_claus" },
+ { "emoji": "🦸", "name": "superhero", "slug": "superhero" },
+ { "emoji": "🦸♂️", "name": "man superhero", "slug": "man_superhero" },
+ { "emoji": "🦸♀️", "name": "woman superhero", "slug": "woman_superhero" },
{
"emoji": "🦹",
"name": "supervillain",
@@ -1854,116 +1338,32 @@
"name": "woman supervillain",
"slug": "woman_supervillain_criminal_evil"
},
- {
- "emoji": "🧙",
- "name": "mage",
- "slug": "mage_sorceress_wizard"
- },
+ { "emoji": "🧙", "name": "mage", "slug": "mage_sorceress_wizard" },
{
"emoji": "🧙♂️",
"name": "man mage",
"slug": "man_mage_sorceress_wizard"
},
- {
- "emoji": "🧙♀️",
- "name": "woman mage",
- "slug": "witch"
- },
- {
- "emoji": "🧚",
- "name": "fairy",
- "slug": "fairy"
- },
- {
- "emoji": "🧚♂️",
- "name": "man fairy",
- "slug": "man_fairy"
- },
- {
- "emoji": "🧚♀️",
- "name": "woman fairy",
- "slug": "woman_fairy"
- },
- {
- "emoji": "🧛",
- "name": "vampire",
- "slug": "vampire"
- },
- {
- "emoji": "🧛♂️",
- "name": "man vampire",
- "slug": "man_vampire"
- },
- {
- "emoji": "🧛♀️",
- "name": "woman vampire",
- "slug": "woman_vampire"
- },
- {
- "emoji": "🧜",
- "name": "merperson",
- "slug": "merperson"
- },
- {
- "emoji": "🧜♂️",
- "name": "merman",
- "slug": "merman"
- },
- {
- "emoji": "🧜♀️",
- "name": "mermaid",
- "slug": "mermaid"
- },
- {
- "emoji": "🧝",
- "name": "elf",
- "slug": "elf"
- },
- {
- "emoji": "🧝♂️",
- "name": "man elf",
- "slug": "man_elf"
- },
- {
- "emoji": "🧝♀️",
- "name": "woman elf",
- "slug": "woman_elf"
- },
- {
- "emoji": "🧞",
- "name": "genie",
- "slug": "genie"
- },
- {
- "emoji": "🧞♂️",
- "name": "man genie",
- "slug": "man_genie"
- },
- {
- "emoji": "🧞♀️",
- "name": "woman genie",
- "slug": "woman_genie"
- },
- {
- "emoji": "🧟",
- "name": "zombie",
- "slug": "zombie"
- },
- {
- "emoji": "🧟♂️",
- "name": "man zombie",
- "slug": "man_zombie"
- },
- {
- "emoji": "🧟♀️",
- "name": "woman zombie",
- "slug": "woman_zombie"
- },
- {
- "emoji": "🧌",
- "name": "troll",
- "slug": "troll"
- },
+ { "emoji": "🧙♀️", "name": "woman mage", "slug": "witch" },
+ { "emoji": "🧚", "name": "fairy", "slug": "fairy" },
+ { "emoji": "🧚♂️", "name": "man fairy", "slug": "man_fairy" },
+ { "emoji": "🧚♀️", "name": "woman fairy", "slug": "woman_fairy" },
+ { "emoji": "🧛", "name": "vampire", "slug": "vampire" },
+ { "emoji": "🧛♂️", "name": "man vampire", "slug": "man_vampire" },
+ { "emoji": "🧛♀️", "name": "woman vampire", "slug": "woman_vampire" },
+ { "emoji": "🧜", "name": "merperson", "slug": "merperson" },
+ { "emoji": "🧜♂️", "name": "merman", "slug": "merman" },
+ { "emoji": "🧜♀️", "name": "mermaid", "slug": "mermaid" },
+ { "emoji": "🧝", "name": "elf", "slug": "elf" },
+ { "emoji": "🧝♂️", "name": "man elf", "slug": "man_elf" },
+ { "emoji": "🧝♀️", "name": "woman elf", "slug": "woman_elf" },
+ { "emoji": "🧞", "name": "genie", "slug": "genie" },
+ { "emoji": "🧞♂️", "name": "man genie", "slug": "man_genie" },
+ { "emoji": "🧞♀️", "name": "woman genie", "slug": "woman_genie" },
+ { "emoji": "🧟", "name": "zombie", "slug": "zombie" },
+ { "emoji": "🧟♂️", "name": "man zombie", "slug": "man_zombie" },
+ { "emoji": "🧟♀️", "name": "woman zombie", "slug": "woman_zombie" },
+ { "emoji": "🧌", "name": "troll", "slug": "troll" },
{
"emoji": "💆",
"name": "person getting massage",
@@ -1999,46 +1399,14 @@
"name": "person walking",
"slug": "person_walking_hike"
},
- {
- "emoji": "🚶♂️",
- "name": "man walking",
- "slug": "man_walking_hike"
- },
- {
- "emoji": "🚶♀️",
- "name": "woman walking",
- "slug": "woman_walking_hike"
- },
- {
- "emoji": "🧍",
- "name": "person standing",
- "slug": "person_standing"
- },
- {
- "emoji": "🧍♂️",
- "name": "man standing",
- "slug": "man_standing"
- },
- {
- "emoji": "🧍♀️",
- "name": "woman standing",
- "slug": "woman_standing"
- },
- {
- "emoji": "🧎",
- "name": "person kneeling",
- "slug": "person_kneeling"
- },
- {
- "emoji": "🧎♂️",
- "name": "man kneeling",
- "slug": "man_kneeling"
- },
- {
- "emoji": "🧎♀️",
- "name": "woman kneeling",
- "slug": "woman_kneeling"
- },
+ { "emoji": "🚶♂️", "name": "man walking", "slug": "man_walking_hike" },
+ { "emoji": "🚶♀️", "name": "woman walking", "slug": "woman_walking_hike" },
+ { "emoji": "🧍", "name": "person standing", "slug": "person_standing" },
+ { "emoji": "🧍♂️", "name": "man standing", "slug": "man_standing" },
+ { "emoji": "🧍♀️", "name": "woman standing", "slug": "woman_standing" },
+ { "emoji": "🧎", "name": "person kneeling", "slug": "person_kneeling" },
+ { "emoji": "🧎♂️", "name": "man kneeling", "slug": "man_kneeling" },
+ { "emoji": "🧎♀️", "name": "woman kneeling", "slug": "woman_kneeling" },
{
"emoji": "🧑🦯",
"name": "person with white cane",
@@ -2084,31 +1452,11 @@
"name": "woman in manual wheelchair",
"slug": "woman_in_manual_wheelchair_accessibility"
},
- {
- "emoji": "🏃",
- "name": "person running",
- "slug": "person_running"
- },
- {
- "emoji": "🏃♂️",
- "name": "man running",
- "slug": "man_running"
- },
- {
- "emoji": "🏃♀️",
- "name": "woman running",
- "slug": "woman_running"
- },
- {
- "emoji": "💃",
- "name": "woman dancing",
- "slug": "woman_dancing"
- },
- {
- "emoji": "🕺",
- "name": "man dancing",
- "slug": "man_dancing"
- },
+ { "emoji": "🏃", "name": "person running", "slug": "person_running" },
+ { "emoji": "🏃♂️", "name": "man running", "slug": "man_running" },
+ { "emoji": "🏃♀️", "name": "woman running", "slug": "woman_running" },
+ { "emoji": "💃", "name": "woman dancing", "slug": "woman_dancing" },
+ { "emoji": "🕺", "name": "man dancing", "slug": "man_dancing" },
{
"emoji": "🕴️",
"name": "person in suit levitating",
@@ -2144,101 +1492,33 @@
"name": "woman in steamy room",
"slug": "woman_in_steamy_room"
},
- {
- "emoji": "🧗",
- "name": "person climbing",
- "slug": "person_climbing"
- },
- {
- "emoji": "🧗♂️",
- "name": "man climbing",
- "slug": "man_climbing"
- },
- {
- "emoji": "🧗♀️",
- "name": "woman climbing",
- "slug": "woman_climbing"
- },
- {
- "emoji": "🤺",
- "name": "person fencing",
- "slug": "person_fencing"
- },
- {
- "emoji": "🏇",
- "name": "horse racing",
- "slug": "horse_racing"
- },
- {
- "emoji": "⛷️",
- "name": "skier",
- "slug": "skier"
- },
- {
- "emoji": "🏂",
- "name": "snowboarder",
- "slug": "snowboarder"
- },
- {
- "emoji": "🏌️",
- "name": "person golfing",
- "slug": "person_golfing"
- },
- {
- "emoji": "🏌️♂️",
- "name": "man golfing",
- "slug": "man_golfing"
- },
- {
- "emoji": "🏌️♀️",
- "name": "woman golfing",
- "slug": "woman_golfing"
- },
- {
- "emoji": "🏄",
- "name": "person surfing",
- "slug": "person_surfing"
- },
- {
- "emoji": "🏄♂️",
- "name": "man surfing",
- "slug": "man_surfing"
- },
- {
- "emoji": "🏄♀️",
- "name": "woman surfing",
- "slug": "woman_surfing"
- },
+ { "emoji": "🧗", "name": "person climbing", "slug": "person_climbing" },
+ { "emoji": "🧗♂️", "name": "man climbing", "slug": "man_climbing" },
+ { "emoji": "🧗♀️", "name": "woman climbing", "slug": "woman_climbing" },
+ { "emoji": "🤺", "name": "person fencing", "slug": "person_fencing" },
+ { "emoji": "🏇", "name": "horse racing", "slug": "horse_racing" },
+ { "emoji": "⛷️", "name": "skier", "slug": "skier" },
+ { "emoji": "🏂", "name": "snowboarder", "slug": "snowboarder" },
+ { "emoji": "🏌️", "name": "person golfing", "slug": "person_golfing" },
+ { "emoji": "🏌️♂️", "name": "man golfing", "slug": "man_golfing" },
+ { "emoji": "🏌️♀️", "name": "woman golfing", "slug": "woman_golfing" },
+ { "emoji": "🏄", "name": "person surfing", "slug": "person_surfing" },
+ { "emoji": "🏄♂️", "name": "man surfing", "slug": "man_surfing" },
+ { "emoji": "🏄♀️", "name": "woman surfing", "slug": "woman_surfing" },
{
"emoji": "🚣",
"name": "person rowing boat",
"slug": "person_rowing_boat"
},
- {
- "emoji": "🚣♂️",
- "name": "man rowing boat",
- "slug": "man_rowing_boat"
- },
+ { "emoji": "🚣♂️", "name": "man rowing boat", "slug": "man_rowing_boat" },
{
"emoji": "🚣♀️",
"name": "woman rowing boat",
"slug": "woman_rowing_boat"
},
- {
- "emoji": "🏊",
- "name": "person swimming",
- "slug": "person_swimming"
- },
- {
- "emoji": "🏊♂️",
- "name": "man swimming",
- "slug": "man_swimming"
- },
- {
- "emoji": "🏊♀️",
- "name": "woman swimming",
- "slug": "woman_swimming"
- },
+ { "emoji": "🏊", "name": "person swimming", "slug": "person_swimming" },
+ { "emoji": "🏊♂️", "name": "man swimming", "slug": "man_swimming" },
+ { "emoji": "🏊♀️", "name": "woman swimming", "slug": "woman_swimming" },
{
"emoji": "⛹️",
"name": "person bouncing ball",
@@ -2269,21 +1549,9 @@
"name": "woman lifting weights",
"slug": "woman_lifting_weights"
},
- {
- "emoji": "🚴",
- "name": "person biking",
- "slug": "person_biking"
- },
- {
- "emoji": "🚴♂️",
- "name": "man biking",
- "slug": "man_biking"
- },
- {
- "emoji": "🚴♀️",
- "name": "woman biking",
- "slug": "woman_biking"
- },
+ { "emoji": "🚴", "name": "person biking", "slug": "person_biking" },
+ { "emoji": "🚴♂️", "name": "man biking", "slug": "man_biking" },
+ { "emoji": "🚴♀️", "name": "woman biking", "slug": "woman_biking" },
{
"emoji": "🚵",
"name": "person mountain biking",
@@ -2304,31 +1572,15 @@
"name": "person cartwheeling",
"slug": "person_cartwheeling"
},
- {
- "emoji": "🤸♂️",
- "name": "man cartwheeling",
- "slug": "man_cartwheeling"
- },
+ { "emoji": "🤸♂️", "name": "man cartwheeling", "slug": "man_cartwheeling" },
{
"emoji": "🤸♀️",
"name": "woman cartwheeling",
"slug": "woman_cartwheeling"
},
- {
- "emoji": "🤼",
- "name": "people wrestling",
- "slug": "people_wrestling"
- },
- {
- "emoji": "🤼♂️",
- "name": "men wrestling",
- "slug": "men_wrestling"
- },
- {
- "emoji": "🤼♀️",
- "name": "women wrestling",
- "slug": "women_wrestling"
- },
+ { "emoji": "🤼", "name": "people wrestling", "slug": "people_wrestling" },
+ { "emoji": "🤼♂️", "name": "men wrestling", "slug": "men_wrestling" },
+ { "emoji": "🤼♀️", "name": "women wrestling", "slug": "women_wrestling" },
{
"emoji": "🤽",
"name": "person playing water polo",
@@ -2359,21 +1611,9 @@
"name": "woman playing handball",
"slug": "woman_playing_handball"
},
- {
- "emoji": "🤹",
- "name": "person juggling",
- "slug": "person_juggling"
- },
- {
- "emoji": "🤹♂️",
- "name": "man juggling",
- "slug": "man_juggling"
- },
- {
- "emoji": "🤹♀️",
- "name": "woman juggling",
- "slug": "woman_juggling"
- },
+ { "emoji": "🤹", "name": "person juggling", "slug": "person_juggling" },
+ { "emoji": "🤹♂️", "name": "man juggling", "slug": "man_juggling" },
+ { "emoji": "🤹♀️", "name": "woman juggling", "slug": "woman_juggling" },
{
"emoji": "🧘",
"name": "person in lotus position",
@@ -2394,11 +1634,7 @@
"name": "person taking bath",
"slug": "person_taking_bath"
},
- {
- "emoji": "🛌",
- "name": "person in bed",
- "slug": "person_in_bed"
- },
+ { "emoji": "🛌", "name": "person in bed", "slug": "person_in_bed" },
{
"emoji": "🧑🤝🧑",
"name": "people holding hands",
@@ -2419,21 +1655,9 @@
"name": "men holding hands",
"slug": "men_holding_hands"
},
- {
- "emoji": "💏",
- "name": "kiss",
- "slug": "kiss"
- },
- {
- "emoji": "👩❤️💋👨",
- "name": "kiss woman, man",
- "slug": "kiss_woman_man"
- },
- {
- "emoji": "👨❤️💋👨",
- "name": "kiss man, man",
- "slug": "kiss_man_man"
- },
+ { "emoji": "💏", "name": "kiss", "slug": "kiss" },
+ { "emoji": "👩❤️💋👨", "name": "kiss woman, man", "slug": "kiss_woman_man" },
+ { "emoji": "👨❤️💋👨", "name": "kiss man, man", "slug": "kiss_man_man" },
{
"emoji": "👩❤️💋👩",
"name": "kiss woman, woman",
@@ -2459,11 +1683,7 @@
"name": "couple with heart woman, woman",
"slug": "couple_with_heart_woman_woman"
},
- {
- "emoji": "👪",
- "name": "family",
- "slug": "family"
- },
+ { "emoji": "👪", "name": "family", "slug": "family" },
{
"emoji": "👨👩👦",
"name": "family man, woman, boy",
@@ -2539,21 +1759,13 @@
"name": "family woman, woman, girl, girl",
"slug": "family_woman_woman_girl_girl"
},
- {
- "emoji": "👨👦",
- "name": "family man, boy",
- "slug": "family_man_boy"
- },
+ { "emoji": "👨👦", "name": "family man, boy", "slug": "family_man_boy" },
{
"emoji": "👨👦👦",
"name": "family man, boy, boy",
"slug": "family_man_boy_boy"
},
- {
- "emoji": "👨👧",
- "name": "family man, girl",
- "slug": "family_man_girl"
- },
+ { "emoji": "👨👧", "name": "family man, girl", "slug": "family_man_girl" },
{
"emoji": "👨👧👦",
"name": "family man, girl, boy",
@@ -2589,11 +1801,7 @@
"name": "family woman, girl, girl",
"slug": "family_woman_girl_girl"
},
- {
- "emoji": "🗣️",
- "name": "speaking head",
- "slug": "speaking_head"
- },
+ { "emoji": "🗣️", "name": "speaking head", "slug": "speaking_head" },
{
"emoji": "👤",
"name": "bust in silhouette",
@@ -2604,1408 +1812,336 @@
"name": "busts in silhouette",
"slug": "busts_in_silhouette"
},
- {
- "emoji": "🫂",
- "name": "people hugging",
- "slug": "people_hugging"
- },
- {
- "emoji": "👣",
- "name": "footprints",
- "slug": "footprints"
- }
+ { "emoji": "🫂", "name": "people hugging", "slug": "people_hugging" },
+ { "emoji": "👣", "name": "footprints", "slug": "footprints" }
]
},
{
"name": "Animals & Nature",
"slug": "animals_nature",
"emojis": [
- {
- "emoji": "🐵",
- "name": "monkey face",
- "slug": "monkey_face"
- },
- {
- "emoji": "🐒",
- "name": "monkey",
- "slug": "monkey"
- },
- {
- "emoji": "🦍",
- "name": "gorilla",
- "slug": "gorilla"
- },
- {
- "emoji": "🦧",
- "name": "orangutan",
- "slug": "orangutan"
- },
- {
- "emoji": "🐶",
- "name": "dog face",
- "slug": "dog_face"
- },
- {
- "emoji": "🐕",
- "name": "dog",
- "slug": "dog"
- },
- {
- "emoji": "🦮",
- "name": "guide dog",
- "slug": "guide_dog"
- },
- {
- "emoji": "🐕🦺",
- "name": "service dog",
- "slug": "service_dog"
- },
- {
- "emoji": "🐩",
- "name": "poodle",
- "slug": "poodle"
- },
- {
- "emoji": "🐺",
- "name": "wolf",
- "slug": "wolf"
- },
- {
- "emoji": "🦊",
- "name": "fox",
- "slug": "fox"
- },
- {
- "emoji": "🦝",
- "name": "raccoon",
- "slug": "raccoon"
- },
- {
- "emoji": "🐱",
- "name": "cat face",
- "slug": "cat_face"
- },
- {
- "emoji": "🐈",
- "name": "cat",
- "slug": "cat"
- },
- {
- "emoji": "🐈⬛",
- "name": "black cat",
- "slug": "black_cat"
- },
- {
- "emoji": "🦁",
- "name": "lion",
- "slug": "lion"
- },
- {
- "emoji": "🐯",
- "name": "tiger face",
- "slug": "tiger_face"
- },
- {
- "emoji": "🐅",
- "name": "tiger",
- "slug": "tiger"
- },
- {
- "emoji": "🐆",
- "name": "leopard",
- "slug": "leopard"
- },
- {
- "emoji": "🐴",
- "name": "horse face",
- "slug": "horse_face"
- },
- {
- "emoji": "🐎",
- "name": "horse",
- "slug": "horse"
- },
- {
- "emoji": "🦄",
- "name": "unicorn",
- "slug": "unicorn"
- },
- {
- "emoji": "🦓",
- "name": "zebra",
- "slug": "zebra"
- },
- {
- "emoji": "🦌",
- "name": "deer",
- "slug": "deer"
- },
- {
- "emoji": "🦬",
- "name": "bison",
- "slug": "bison"
- },
- {
- "emoji": "🐮",
- "name": "cow face",
- "slug": "cow_face"
- },
- {
- "emoji": "🐂",
- "name": "ox",
- "slug": "ox"
- },
- {
- "emoji": "🐃",
- "name": "water buffalo",
- "slug": "water_buffalo"
- },
- {
- "emoji": "🐄",
- "name": "cow",
- "slug": "cow"
- },
- {
- "emoji": "🐷",
- "name": "pig face",
- "slug": "pig_face"
- },
- {
- "emoji": "🐖",
- "name": "pig",
- "slug": "pig"
- },
- {
- "emoji": "🐗",
- "name": "boar",
- "slug": "boar"
- },
- {
- "emoji": "🐽",
- "name": "pig nose",
- "slug": "pig_nose"
- },
- {
- "emoji": "🐏",
- "name": "ram",
- "slug": "ram"
- },
- {
- "emoji": "🐑",
- "name": "ewe",
- "slug": "ewe"
- },
- {
- "emoji": "🐐",
- "name": "goat",
- "slug": "goat"
- },
- {
- "emoji": "🐪",
- "name": "camel",
- "slug": "camel"
- },
- {
- "emoji": "🐫",
- "name": "two-hump camel",
- "slug": "two_hump_camel"
- },
- {
- "emoji": "🦙",
- "name": "llama",
- "slug": "llama"
- },
- {
- "emoji": "🦒",
- "name": "giraffe",
- "slug": "giraffe"
- },
- {
- "emoji": "🐘",
- "name": "elephant",
- "slug": "elephant"
- },
- {
- "emoji": "🦣",
- "name": "mammoth",
- "slug": "mammoth"
- },
- {
- "emoji": "🦏",
- "name": "rhinoceros",
- "slug": "rhinoceros"
- },
- {
- "emoji": "🦛",
- "name": "hippopotamus",
- "slug": "hippopotamus"
- },
- {
- "emoji": "🐭",
- "name": "mouse face",
- "slug": "mouse_face"
- },
- {
- "emoji": "🐁",
- "name": "mouse",
- "slug": "mouse"
- },
- {
- "emoji": "🐀",
- "name": "rat",
- "slug": "rat"
- },
- {
- "emoji": "🐹",
- "name": "hamster",
- "slug": "hamster"
- },
- {
- "emoji": "🐰",
- "name": "rabbit face",
- "slug": "rabbit_face"
- },
- {
- "emoji": "🐇",
- "name": "rabbit",
- "slug": "rabbit"
- },
- {
- "emoji": "🐿️",
- "name": "chipmunk",
- "slug": "chipmunk"
- },
- {
- "emoji": "🦫",
- "name": "beaver",
- "slug": "beaver"
- },
- {
- "emoji": "🦔",
- "name": "hedgehog",
- "slug": "hedgehog"
- },
- {
- "emoji": "🦇",
- "name": "bat",
- "slug": "bat"
- },
- {
- "emoji": "🐻",
- "name": "bear",
- "slug": "bear"
- },
- {
- "emoji": "🐻❄️",
- "name": "polar bear",
- "slug": "polar_bear"
- },
- {
- "emoji": "🐨",
- "name": "koala",
- "slug": "koala"
- },
- {
- "emoji": "🐼",
- "name": "panda",
- "slug": "panda"
- },
- {
- "emoji": "🦥",
- "name": "sloth",
- "slug": "sloth"
- },
- {
- "emoji": "🦦",
- "name": "otter",
- "slug": "otter"
- },
- {
- "emoji": "🦨",
- "name": "skunk",
- "slug": "skunk"
- },
- {
- "emoji": "🦘",
- "name": "kangaroo",
- "slug": "kangaroo"
- },
- {
- "emoji": "🦡",
- "name": "badger",
- "slug": "badger"
- },
- {
- "emoji": "🐾",
- "name": "paw prints",
- "slug": "paw_prints"
- },
- {
- "emoji": "🦃",
- "name": "turkey",
- "slug": "turkey"
- },
- {
- "emoji": "🐔",
- "name": "chicken",
- "slug": "chicken"
- },
- {
- "emoji": "🐓",
- "name": "rooster",
- "slug": "rooster"
- },
- {
- "emoji": "🐣",
- "name": "hatching chick",
- "slug": "hatching_chick"
- },
- {
- "emoji": "🐤",
- "name": "baby chick",
- "slug": "baby_chick"
- },
+ { "emoji": "🐵", "name": "monkey face", "slug": "monkey_face" },
+ { "emoji": "🐒", "name": "monkey", "slug": "monkey" },
+ { "emoji": "🦍", "name": "gorilla", "slug": "gorilla" },
+ { "emoji": "🦧", "name": "orangutan", "slug": "orangutan" },
+ { "emoji": "🐶", "name": "dog face", "slug": "dog_face" },
+ { "emoji": "🐕", "name": "dog", "slug": "dog" },
+ { "emoji": "🦮", "name": "guide dog", "slug": "guide_dog" },
+ { "emoji": "🐕🦺", "name": "service dog", "slug": "service_dog" },
+ { "emoji": "🐩", "name": "poodle", "slug": "poodle" },
+ { "emoji": "🐺", "name": "wolf", "slug": "wolf" },
+ { "emoji": "🦊", "name": "fox", "slug": "fox" },
+ { "emoji": "🦝", "name": "raccoon", "slug": "raccoon" },
+ { "emoji": "🐱", "name": "cat face", "slug": "cat_face" },
+ { "emoji": "🐈", "name": "cat", "slug": "cat" },
+ { "emoji": "🐈⬛", "name": "black cat", "slug": "black_cat" },
+ { "emoji": "🦁", "name": "lion", "slug": "lion" },
+ { "emoji": "🐯", "name": "tiger face", "slug": "tiger_face" },
+ { "emoji": "🐅", "name": "tiger", "slug": "tiger" },
+ { "emoji": "🐆", "name": "leopard", "slug": "leopard" },
+ { "emoji": "🐴", "name": "horse face", "slug": "horse_face" },
+ { "emoji": "🐎", "name": "horse", "slug": "horse" },
+ { "emoji": "🦄", "name": "unicorn", "slug": "unicorn" },
+ { "emoji": "🦓", "name": "zebra", "slug": "zebra" },
+ { "emoji": "🦌", "name": "deer", "slug": "deer" },
+ { "emoji": "🦬", "name": "bison", "slug": "bison" },
+ { "emoji": "🐮", "name": "cow face", "slug": "cow_face" },
+ { "emoji": "🐂", "name": "ox", "slug": "ox" },
+ { "emoji": "🐃", "name": "water buffalo", "slug": "water_buffalo" },
+ { "emoji": "🐄", "name": "cow", "slug": "cow" },
+ { "emoji": "🐷", "name": "pig face", "slug": "pig_face" },
+ { "emoji": "🐖", "name": "pig", "slug": "pig" },
+ { "emoji": "🐗", "name": "boar", "slug": "boar" },
+ { "emoji": "🐽", "name": "pig nose", "slug": "pig_nose" },
+ { "emoji": "🐏", "name": "ram", "slug": "ram" },
+ { "emoji": "🐑", "name": "ewe", "slug": "ewe" },
+ { "emoji": "🐐", "name": "goat", "slug": "goat" },
+ { "emoji": "🐪", "name": "camel", "slug": "camel" },
+ { "emoji": "🐫", "name": "two-hump camel", "slug": "two_hump_camel" },
+ { "emoji": "🦙", "name": "llama", "slug": "llama" },
+ { "emoji": "🦒", "name": "giraffe", "slug": "giraffe" },
+ { "emoji": "🐘", "name": "elephant", "slug": "elephant" },
+ { "emoji": "🦣", "name": "mammoth", "slug": "mammoth" },
+ { "emoji": "🦏", "name": "rhinoceros", "slug": "rhinoceros" },
+ { "emoji": "🦛", "name": "hippopotamus", "slug": "hippopotamus" },
+ { "emoji": "🐭", "name": "mouse face", "slug": "mouse_face" },
+ { "emoji": "🐁", "name": "mouse", "slug": "mouse" },
+ { "emoji": "🐀", "name": "rat", "slug": "rat" },
+ { "emoji": "🐹", "name": "hamster", "slug": "hamster" },
+ { "emoji": "🐰", "name": "rabbit face", "slug": "rabbit_face" },
+ { "emoji": "🐇", "name": "rabbit", "slug": "rabbit" },
+ { "emoji": "🐿️", "name": "chipmunk", "slug": "chipmunk" },
+ { "emoji": "🦫", "name": "beaver", "slug": "beaver" },
+ { "emoji": "🦔", "name": "hedgehog", "slug": "hedgehog" },
+ { "emoji": "🦇", "name": "bat", "slug": "bat" },
+ { "emoji": "🐻", "name": "bear", "slug": "bear" },
+ { "emoji": "🐻❄️", "name": "polar bear", "slug": "polar_bear" },
+ { "emoji": "🐨", "name": "koala", "slug": "koala" },
+ { "emoji": "🐼", "name": "panda", "slug": "panda" },
+ { "emoji": "🦥", "name": "sloth", "slug": "sloth" },
+ { "emoji": "🦦", "name": "otter", "slug": "otter" },
+ { "emoji": "🦨", "name": "skunk", "slug": "skunk" },
+ { "emoji": "🦘", "name": "kangaroo", "slug": "kangaroo" },
+ { "emoji": "🦡", "name": "badger", "slug": "badger" },
+ { "emoji": "🐾", "name": "paw prints", "slug": "paw_prints" },
+ { "emoji": "🦃", "name": "turkey", "slug": "turkey" },
+ { "emoji": "🐔", "name": "chicken", "slug": "chicken" },
+ { "emoji": "🐓", "name": "rooster", "slug": "rooster" },
+ { "emoji": "🐣", "name": "hatching chick", "slug": "hatching_chick" },
+ { "emoji": "🐤", "name": "baby chick", "slug": "baby_chick" },
{
"emoji": "🐥",
"name": "front-facing baby chick",
"slug": "front_facing_baby_chick"
},
- {
- "emoji": "🐦",
- "name": "bird",
- "slug": "bird"
- },
- {
- "emoji": "🐧",
- "name": "penguin",
- "slug": "penguin"
- },
- {
- "emoji": "🕊️",
- "name": "dove",
- "slug": "dove"
- },
- {
- "emoji": "🦅",
- "name": "eagle",
- "slug": "eagle"
- },
- {
- "emoji": "🦆",
- "name": "duck",
- "slug": "duck"
- },
- {
- "emoji": "🦢",
- "name": "swan",
- "slug": "swan"
- },
- {
- "emoji": "🦉",
- "name": "owl",
- "slug": "owl"
- },
- {
- "emoji": "🦤",
- "name": "dodo",
- "slug": "dodo"
- },
- {
- "emoji": "🪶",
- "name": "feather",
- "slug": "feather"
- },
- {
- "emoji": "🦩",
- "name": "flamingo",
- "slug": "flamingo"
- },
- {
- "emoji": "🦚",
- "name": "peacock",
- "slug": "peacock"
- },
- {
- "emoji": "🦜",
- "name": "parrot",
- "slug": "parrot"
- },
- {
- "emoji": "🐸",
- "name": "frog",
- "slug": "frog"
- },
- {
- "emoji": "🐊",
- "name": "crocodile",
- "slug": "crocodile"
- },
- {
- "emoji": "🐢",
- "name": "turtle",
- "slug": "turtle"
- },
- {
- "emoji": "🦎",
- "name": "lizard",
- "slug": "lizard"
- },
- {
- "emoji": "🐍",
- "name": "snake",
- "slug": "snake"
- },
- {
- "emoji": "🐲",
- "name": "dragon face",
- "slug": "dragon_face"
- },
- {
- "emoji": "🐉",
- "name": "dragon",
- "slug": "dragon"
- },
- {
- "emoji": "🦕",
- "name": "sauropod",
- "slug": "sauropod"
- },
- {
- "emoji": "🦖",
- "name": "T-Rex",
- "slug": "t_rex"
- },
- {
- "emoji": "🐳",
- "name": "spouting whale",
- "slug": "spouting_whale"
- },
- {
- "emoji": "🐋",
- "name": "whale",
- "slug": "whale"
- },
- {
- "emoji": "🐬",
- "name": "dolphin",
- "slug": "dolphin"
- },
- {
- "emoji": "🦭",
- "name": "seal",
- "slug": "seal"
- },
- {
- "emoji": "🐟",
- "name": "fish",
- "slug": "fish"
- },
- {
- "emoji": "🐠",
- "name": "tropical fish",
- "slug": "tropical_fish"
- },
- {
- "emoji": "🐡",
- "name": "blowfish",
- "slug": "blowfish"
- },
- {
- "emoji": "🦈",
- "name": "shark",
- "slug": "shark"
- },
- {
- "emoji": "🐙",
- "name": "octopus",
- "slug": "octopus"
- },
- {
- "emoji": "🐚",
- "name": "spiral shell",
- "slug": "spiral_shell"
- },
- {
- "emoji": "🪸",
- "name": "coral",
- "slug": "coral"
- },
- {
- "emoji": "🐌",
- "name": "snail",
- "slug": "snail"
- },
- {
- "emoji": "🦋",
- "name": "butterfly",
- "slug": "butterfly"
- },
- {
- "emoji": "🐛",
- "name": "bug",
- "slug": "bug"
- },
- {
- "emoji": "🐜",
- "name": "ant",
- "slug": "ant"
- },
- {
- "emoji": "🐝",
- "name": "honeybee",
- "slug": "honeybee"
- },
- {
- "emoji": "🪲",
- "name": "beetle",
- "slug": "beetle"
- },
- {
- "emoji": "🐞",
- "name": "lady beetle",
- "slug": "lady_beetle"
- },
- {
- "emoji": "🦗",
- "name": "cricket",
- "slug": "cricket"
- },
- {
- "emoji": "🪳",
- "name": "cockroach",
- "slug": "cockroach"
- },
- {
- "emoji": "🕷️",
- "name": "spider",
- "slug": "spider"
- },
- {
- "emoji": "🕸️",
- "name": "spider web",
- "slug": "spider_web"
- },
- {
- "emoji": "🦂",
- "name": "scorpion",
- "slug": "scorpion"
- },
- {
- "emoji": "🦟",
- "name": "mosquito",
- "slug": "mosquito"
- },
- {
- "emoji": "🪰",
- "name": "fly",
- "slug": "fly"
- },
- {
- "emoji": "🪱",
- "name": "worm",
- "slug": "worm"
- },
- {
- "emoji": "🦠",
- "name": "microbe",
- "slug": "microbe"
- },
- {
- "emoji": "💐",
- "name": "bouquet",
- "slug": "bouquet"
- },
- {
- "emoji": "🌸",
- "name": "cherry blossom",
- "slug": "cherry_blossom"
- },
- {
- "emoji": "💮",
- "name": "white flower",
- "slug": "white_flower"
- },
- {
- "emoji": "🪷",
- "name": "lotus",
- "slug": "lotus"
- },
- {
- "emoji": "🏵️",
- "name": "rosette",
- "slug": "rosette"
- },
- {
- "emoji": "🌹",
- "name": "rose",
- "slug": "rose"
- },
- {
- "emoji": "🥀",
- "name": "wilted flower",
- "slug": "wilted_flower"
- },
- {
- "emoji": "🌺",
- "name": "hibiscus",
- "slug": "hibiscus"
- },
- {
- "emoji": "🌻",
- "name": "sunflower",
- "slug": "sunflower"
- },
- {
- "emoji": "🌼",
- "name": "blossom",
- "slug": "blossom"
- },
- {
- "emoji": "🌷",
- "name": "tulip",
- "slug": "tulip"
- },
- {
- "emoji": "🌱",
- "name": "seedling",
- "slug": "seedling"
- },
- {
- "emoji": "🪴",
- "name": "potted plant",
- "slug": "potted_plant"
- },
- {
- "emoji": "🌲",
- "name": "evergreen tree",
- "slug": "evergreen_tree"
- },
- {
- "emoji": "🌳",
- "name": "deciduous tree",
- "slug": "deciduous_tree"
- },
- {
- "emoji": "🌴",
- "name": "palm tree",
- "slug": "palm_tree"
- },
- {
- "emoji": "🌵",
- "name": "cactus",
- "slug": "cactus"
- },
- {
- "emoji": "🌾",
- "name": "sheaf of rice",
- "slug": "sheaf_of_rice"
- },
- {
- "emoji": "🌿",
- "name": "herb",
- "slug": "herb"
- },
- {
- "emoji": "☘️",
- "name": "shamrock",
- "slug": "shamrock"
- },
- {
- "emoji": "🍀",
- "name": "four leaf clover",
- "slug": "four_leaf_clover"
- },
- {
- "emoji": "🍁",
- "name": "maple leaf",
- "slug": "maple_leaf"
- },
- {
- "emoji": "🍂",
- "name": "fallen leaf",
- "slug": "fallen_leaf"
- },
+ { "emoji": "🐦", "name": "bird", "slug": "bird" },
+ { "emoji": "🐧", "name": "penguin", "slug": "penguin" },
+ { "emoji": "🕊️", "name": "dove", "slug": "dove" },
+ { "emoji": "🦅", "name": "eagle", "slug": "eagle" },
+ { "emoji": "🦆", "name": "duck", "slug": "duck" },
+ { "emoji": "🦢", "name": "swan", "slug": "swan" },
+ { "emoji": "🦉", "name": "owl", "slug": "owl" },
+ { "emoji": "🦤", "name": "dodo", "slug": "dodo" },
+ { "emoji": "🪶", "name": "feather", "slug": "feather" },
+ { "emoji": "🦩", "name": "flamingo", "slug": "flamingo" },
+ { "emoji": "🦚", "name": "peacock", "slug": "peacock" },
+ { "emoji": "🦜", "name": "parrot", "slug": "parrot" },
+ { "emoji": "🐸", "name": "frog", "slug": "frog" },
+ { "emoji": "🐊", "name": "crocodile", "slug": "crocodile" },
+ { "emoji": "🐢", "name": "turtle", "slug": "turtle" },
+ { "emoji": "🦎", "name": "lizard", "slug": "lizard" },
+ { "emoji": "🐍", "name": "snake", "slug": "snake" },
+ { "emoji": "🐲", "name": "dragon face", "slug": "dragon_face" },
+ { "emoji": "🐉", "name": "dragon", "slug": "dragon" },
+ { "emoji": "🦕", "name": "sauropod", "slug": "sauropod" },
+ { "emoji": "🦖", "name": "T-Rex", "slug": "t_rex" },
+ { "emoji": "🐳", "name": "spouting whale", "slug": "spouting_whale" },
+ { "emoji": "🐋", "name": "whale", "slug": "whale" },
+ { "emoji": "🐬", "name": "dolphin", "slug": "dolphin" },
+ { "emoji": "🦭", "name": "seal", "slug": "seal" },
+ { "emoji": "🐟", "name": "fish", "slug": "fish" },
+ { "emoji": "🐠", "name": "tropical fish", "slug": "tropical_fish" },
+ { "emoji": "🐡", "name": "blowfish", "slug": "blowfish" },
+ { "emoji": "🦈", "name": "shark", "slug": "shark" },
+ { "emoji": "🐙", "name": "octopus", "slug": "octopus" },
+ { "emoji": "🐚", "name": "spiral shell", "slug": "spiral_shell" },
+ { "emoji": "🪸", "name": "coral", "slug": "coral" },
+ { "emoji": "🐌", "name": "snail", "slug": "snail" },
+ { "emoji": "🦋", "name": "butterfly", "slug": "butterfly" },
+ { "emoji": "🐛", "name": "bug", "slug": "bug" },
+ { "emoji": "🐜", "name": "ant", "slug": "ant" },
+ { "emoji": "🐝", "name": "honeybee", "slug": "honeybee" },
+ { "emoji": "🪲", "name": "beetle", "slug": "beetle" },
+ { "emoji": "🐞", "name": "lady beetle", "slug": "lady_beetle" },
+ { "emoji": "🦗", "name": "cricket", "slug": "cricket" },
+ { "emoji": "🪳", "name": "cockroach", "slug": "cockroach" },
+ { "emoji": "🕷️", "name": "spider", "slug": "spider" },
+ { "emoji": "🕸️", "name": "spider web", "slug": "spider_web" },
+ { "emoji": "🦂", "name": "scorpion", "slug": "scorpion" },
+ { "emoji": "🦟", "name": "mosquito", "slug": "mosquito" },
+ { "emoji": "🪰", "name": "fly", "slug": "fly" },
+ { "emoji": "🪱", "name": "worm", "slug": "worm" },
+ { "emoji": "🦠", "name": "microbe", "slug": "microbe" },
+ { "emoji": "💐", "name": "bouquet", "slug": "bouquet" },
+ { "emoji": "🌸", "name": "cherry blossom", "slug": "cherry_blossom" },
+ { "emoji": "💮", "name": "white flower", "slug": "white_flower" },
+ { "emoji": "🪷", "name": "lotus", "slug": "lotus" },
+ { "emoji": "🏵️", "name": "rosette", "slug": "rosette" },
+ { "emoji": "🌹", "name": "rose", "slug": "rose" },
+ { "emoji": "🥀", "name": "wilted flower", "slug": "wilted_flower" },
+ { "emoji": "🌺", "name": "hibiscus", "slug": "hibiscus" },
+ { "emoji": "🌻", "name": "sunflower", "slug": "sunflower" },
+ { "emoji": "🌼", "name": "blossom", "slug": "blossom" },
+ { "emoji": "🌷", "name": "tulip", "slug": "tulip" },
+ { "emoji": "🌱", "name": "seedling", "slug": "seedling" },
+ { "emoji": "🪴", "name": "potted plant", "slug": "potted_plant" },
+ { "emoji": "🌲", "name": "evergreen tree", "slug": "evergreen_tree" },
+ { "emoji": "🌳", "name": "deciduous tree", "slug": "deciduous_tree" },
+ { "emoji": "🌴", "name": "palm tree", "slug": "palm_tree" },
+ { "emoji": "🌵", "name": "cactus", "slug": "cactus" },
+ { "emoji": "🌾", "name": "sheaf of rice", "slug": "sheaf_of_rice" },
+ { "emoji": "🌿", "name": "herb", "slug": "herb" },
+ { "emoji": "☘️", "name": "shamrock", "slug": "shamrock" },
+ { "emoji": "🍀", "name": "four leaf clover", "slug": "four_leaf_clover" },
+ { "emoji": "🍁", "name": "maple leaf", "slug": "maple_leaf" },
+ { "emoji": "🍂", "name": "fallen leaf", "slug": "fallen_leaf" },
{
"emoji": "🍃",
"name": "leaf fluttering in wind",
"slug": "leaf_fluttering_in_wind"
},
- {
- "emoji": "🪹",
- "name": "empty nest",
- "slug": "empty_nest"
- },
- {
- "emoji": "🪺",
- "name": "nest with eggs",
- "slug": "nest_with_eggs"
- },
- {
- "emoji": "🍄",
- "name": "mushroom",
- "slug": "mushroom"
- }
+ { "emoji": "🪹", "name": "empty nest", "slug": "empty_nest" },
+ { "emoji": "🪺", "name": "nest with eggs", "slug": "nest_with_eggs" },
+ { "emoji": "🍄", "name": "mushroom", "slug": "mushroom" }
]
},
{
"name": "Food & Drink",
"slug": "food_drink",
"emojis": [
- {
- "emoji": "🍇",
- "name": "grapes",
- "slug": "grapes_fruit"
- },
- {
- "emoji": "🍈",
- "name": "melon",
- "slug": "melon_fruit"
- },
- {
- "emoji": "🍉",
- "name": "watermelon",
- "slug": "watermelon_fruit"
- },
- {
- "emoji": "🍊",
- "name": "tangerine",
- "slug": "tangerine_fruit"
- },
- {
- "emoji": "🍋",
- "name": "lemon",
- "slug": "lemon_fruit"
- },
- {
- "emoji": "🍌",
- "name": "banana",
- "slug": "banana_fruit"
- },
- {
- "emoji": "🍍",
- "name": "pineapple",
- "slug": "pineapple_fruit"
- },
- {
- "emoji": "🥭",
- "name": "mango",
- "slug": "mango_fruit"
- },
- {
- "emoji": "🍎",
- "name": "red apple",
- "slug": "red_apple"
- },
- {
- "emoji": "🍏",
- "name": "green apple",
- "slug": "green_apple"
- },
- {
- "emoji": "🍐",
- "name": "pear",
- "slug": "pear_fruit"
- },
- {
- "emoji": "🍑",
- "name": "peach",
- "slug": "peach_fruit"
- },
- {
- "emoji": "🍒",
- "name": "cherries",
- "slug": "cherries_fruit"
- },
- {
- "emoji": "🍓",
- "name": "strawberry",
- "slug": "strawberry_fruit"
- },
- {
- "emoji": "🫐",
- "name": "blueberries",
- "slug": "blueberries_blue"
- },
- {
- "emoji": "🥝",
- "name": "kiwi fruit",
- "slug": "kiwi_fruit_fruit"
- },
- {
- "emoji": "🍅",
- "name": "tomato",
- "slug": "tomato_fruit"
- },
- {
- "emoji": "🫒",
- "name": "olive",
- "slug": "olive"
- },
- {
- "emoji": "🥥",
- "name": "coconut",
- "slug": "coconut_palm"
- },
- {
- "emoji": "🥑",
- "name": "avocado",
- "slug": "avocado_fruit"
- },
- {
- "emoji": "🍆",
- "name": "eggplant",
- "slug": "eggplant"
- },
- {
- "emoji": "🥔",
- "name": "potato",
- "slug": "potato"
- },
- {
- "emoji": "🥕",
- "name": "carrot",
- "slug": "carrot"
- },
- {
- "emoji": "🌽",
- "name": "ear of corn",
- "slug": "ear_of_corn"
- },
- {
- "emoji": "🌶️",
- "name": "hot pepper",
- "slug": "hot_pepper"
- },
- {
- "emoji": "🫑",
- "name": "bell pepper",
- "slug": "bell_pepper"
- },
- {
- "emoji": "🥒",
- "name": "cucumber",
- "slug": "cucumber"
- },
- {
- "emoji": "🥬",
- "name": "leafy green",
- "slug": "leafy_green"
- },
- {
- "emoji": "🥦",
- "name": "broccoli",
- "slug": "broccoli"
- },
- {
- "emoji": "🧄",
- "name": "garlic",
- "slug": "garlic"
- },
- {
- "emoji": "🧅",
- "name": "onion",
- "slug": "onion"
- },
- {
- "emoji": "🥜",
- "name": "peanuts",
- "slug": "peanuts"
- },
- {
- "emoji": "🫘",
- "name": "beans",
- "slug": "beans"
- },
- {
- "emoji": "🌰",
- "name": "chestnut",
- "slug": "chestnut"
- },
- {
- "emoji": "🍞",
- "name": "bread",
- "slug": "bread_loaf"
- },
- {
- "emoji": "🥐",
- "name": "croissant",
- "slug": "croissant_breakfast"
- },
- {
- "emoji": "🥖",
- "name": "baguette bread",
- "slug": "baguette_bread"
- },
- {
- "emoji": "🫓",
- "name": "flatbread",
- "slug": "flatbread"
- },
- {
- "emoji": "🥨",
- "name": "pretzel",
- "slug": "pretzel"
- },
- {
- "emoji": "🥯",
- "name": "bagel",
- "slug": "bagel"
- },
- {
- "emoji": "🥞",
- "name": "pancakes",
- "slug": "pancakes"
- },
- {
- "emoji": "🧇",
- "name": "waffle",
- "slug": "waffle"
- },
- {
- "emoji": "🧀",
- "name": "cheese wedge",
- "slug": "cheese_wedge"
- },
- {
- "emoji": "🍖",
- "name": "meat on bone",
- "slug": "meat_on_bone"
- },
- {
- "emoji": "🍗",
- "name": "poultry leg",
- "slug": "poultry_leg_chicken"
- },
- {
- "emoji": "🥩",
- "name": "cut of meat",
- "slug": "cut_of_meat"
- },
- {
- "emoji": "🥓",
- "name": "bacon",
- "slug": "bacon"
- },
- {
- "emoji": "🍔",
- "name": "hamburger",
- "slug": "hamburger"
- },
- {
- "emoji": "🍟",
- "name": "french fries",
- "slug": "french_fries"
- },
- {
- "emoji": "🍕",
- "name": "pizza",
- "slug": "pizza"
- },
- {
- "emoji": "🌭",
- "name": "hot dog",
- "slug": "hot_dog"
- },
- {
- "emoji": "🥪",
- "name": "sandwich",
- "slug": "sandwich"
- },
- {
- "emoji": "🌮",
- "name": "taco",
- "slug": "taco"
- },
- {
- "emoji": "🌯",
- "name": "burrito",
- "slug": "burrito"
- },
- {
- "emoji": "🫔",
- "name": "tamale",
- "slug": "tamale"
- },
+ { "emoji": "🍇", "name": "grapes", "slug": "grapes_fruit" },
+ { "emoji": "🍈", "name": "melon", "slug": "melon_fruit" },
+ { "emoji": "🍉", "name": "watermelon", "slug": "watermelon_fruit" },
+ { "emoji": "🍊", "name": "tangerine", "slug": "tangerine_fruit" },
+ { "emoji": "🍋", "name": "lemon", "slug": "lemon_fruit" },
+ { "emoji": "🍌", "name": "banana", "slug": "banana_fruit" },
+ { "emoji": "🍍", "name": "pineapple", "slug": "pineapple_fruit" },
+ { "emoji": "🥭", "name": "mango", "slug": "mango_fruit" },
+ { "emoji": "🍎", "name": "red apple", "slug": "red_apple" },
+ { "emoji": "🍏", "name": "green apple", "slug": "green_apple" },
+ { "emoji": "🍐", "name": "pear", "slug": "pear_fruit" },
+ { "emoji": "🍑", "name": "peach", "slug": "peach_fruit" },
+ { "emoji": "🍒", "name": "cherries", "slug": "cherries_fruit" },
+ { "emoji": "🍓", "name": "strawberry", "slug": "strawberry_fruit" },
+ { "emoji": "🫐", "name": "blueberries", "slug": "blueberries_blue" },
+ { "emoji": "🥝", "name": "kiwi fruit", "slug": "kiwi_fruit_fruit" },
+ { "emoji": "🍅", "name": "tomato", "slug": "tomato_fruit" },
+ { "emoji": "🫒", "name": "olive", "slug": "olive" },
+ { "emoji": "🥥", "name": "coconut", "slug": "coconut_palm" },
+ { "emoji": "🥑", "name": "avocado", "slug": "avocado_fruit" },
+ { "emoji": "🍆", "name": "eggplant", "slug": "eggplant" },
+ { "emoji": "🥔", "name": "potato", "slug": "potato" },
+ { "emoji": "🥕", "name": "carrot", "slug": "carrot" },
+ { "emoji": "🌽", "name": "ear of corn", "slug": "ear_of_corn" },
+ { "emoji": "🌶️", "name": "hot pepper", "slug": "hot_pepper" },
+ { "emoji": "🫑", "name": "bell pepper", "slug": "bell_pepper" },
+ { "emoji": "🥒", "name": "cucumber", "slug": "cucumber" },
+ { "emoji": "🥬", "name": "leafy green", "slug": "leafy_green" },
+ { "emoji": "🥦", "name": "broccoli", "slug": "broccoli" },
+ { "emoji": "🧄", "name": "garlic", "slug": "garlic" },
+ { "emoji": "🧅", "name": "onion", "slug": "onion" },
+ { "emoji": "🥜", "name": "peanuts", "slug": "peanuts" },
+ { "emoji": "🫘", "name": "beans", "slug": "beans" },
+ { "emoji": "🌰", "name": "chestnut", "slug": "chestnut" },
+ { "emoji": "🍞", "name": "bread", "slug": "bread_loaf" },
+ { "emoji": "🥐", "name": "croissant", "slug": "croissant_breakfast" },
+ { "emoji": "🥖", "name": "baguette bread", "slug": "baguette_bread" },
+ { "emoji": "🫓", "name": "flatbread", "slug": "flatbread" },
+ { "emoji": "🥨", "name": "pretzel", "slug": "pretzel" },
+ { "emoji": "🥯", "name": "bagel", "slug": "bagel" },
+ { "emoji": "🥞", "name": "pancakes", "slug": "pancakes" },
+ { "emoji": "🧇", "name": "waffle", "slug": "waffle" },
+ { "emoji": "🧀", "name": "cheese wedge", "slug": "cheese_wedge" },
+ { "emoji": "🍖", "name": "meat on bone", "slug": "meat_on_bone" },
+ { "emoji": "🍗", "name": "poultry leg", "slug": "poultry_leg_chicken" },
+ { "emoji": "🥩", "name": "cut of meat", "slug": "cut_of_meat" },
+ { "emoji": "🥓", "name": "bacon", "slug": "bacon" },
+ { "emoji": "🍔", "name": "hamburger", "slug": "hamburger" },
+ { "emoji": "🍟", "name": "french fries", "slug": "french_fries" },
+ { "emoji": "🍕", "name": "pizza", "slug": "pizza" },
+ { "emoji": "🌭", "name": "hot dog", "slug": "hot_dog" },
+ { "emoji": "🥪", "name": "sandwich", "slug": "sandwich" },
+ { "emoji": "🌮", "name": "taco", "slug": "taco" },
+ { "emoji": "🌯", "name": "burrito", "slug": "burrito" },
+ { "emoji": "🫔", "name": "tamale", "slug": "tamale" },
{
"emoji": "🥙",
"name": "stuffed flatbread",
"slug": "stuffed_flatbread"
},
- {
- "emoji": "🧆",
- "name": "falafel",
- "slug": "falafel"
- },
- {
- "emoji": "🥚",
- "name": "egg",
- "slug": "egg"
- },
- {
- "emoji": "🍳",
- "name": "cooking",
- "slug": "cooking"
- },
+ { "emoji": "🧆", "name": "falafel", "slug": "falafel" },
+ { "emoji": "🥚", "name": "egg", "slug": "egg" },
+ { "emoji": "🍳", "name": "cooking", "slug": "cooking" },
{
"emoji": "🥘",
"name": "shallow pan of food",
"slug": "shallow_pan_of_food"
},
- {
- "emoji": "🍲",
- "name": "pot of food",
- "slug": "pot_of_food"
- },
- {
- "emoji": "🫕",
- "name": "fondue",
- "slug": "fondue"
- },
- {
- "emoji": "🥣",
- "name": "bowl with spoon",
- "slug": "bowl_with_spoon"
- },
- {
- "emoji": "🥗",
- "name": "green salad",
- "slug": "green_salad"
- },
- {
- "emoji": "🍿",
- "name": "popcorn",
- "slug": "popcorn"
- },
- {
- "emoji": "🧈",
- "name": "butter",
- "slug": "butter"
- },
- {
- "emoji": "🧂",
- "name": "salt",
- "slug": "salt"
- },
- {
- "emoji": "🥫",
- "name": "canned food",
- "slug": "canned_food"
- },
- {
- "emoji": "🍱",
- "name": "bento box",
- "slug": "bento_box"
- },
- {
- "emoji": "🍘",
- "name": "rice cracker",
- "slug": "rice_cracker"
- },
- {
- "emoji": "🍙",
- "name": "rice ball",
- "slug": "rice_ball"
- },
- {
- "emoji": "🍚",
- "name": "cooked rice",
- "slug": "cooked_rice"
- },
- {
- "emoji": "🍛",
- "name": "curry rice",
- "slug": "curry_rice"
- },
- {
- "emoji": "🍜",
- "name": "steaming bowl",
- "slug": "steaming_bowl"
- },
- {
- "emoji": "🍝",
- "name": "spaghetti",
- "slug": "spaghetti"
- },
+ { "emoji": "🍲", "name": "pot of food", "slug": "pot_of_food" },
+ { "emoji": "🫕", "name": "fondue", "slug": "fondue" },
+ { "emoji": "🥣", "name": "bowl with spoon", "slug": "bowl_with_spoon" },
+ { "emoji": "🥗", "name": "green salad", "slug": "green_salad" },
+ { "emoji": "🍿", "name": "popcorn", "slug": "popcorn" },
+ { "emoji": "🧈", "name": "butter", "slug": "butter" },
+ { "emoji": "🧂", "name": "salt", "slug": "salt" },
+ { "emoji": "🥫", "name": "canned food", "slug": "canned_food" },
+ { "emoji": "🍱", "name": "bento box", "slug": "bento_box" },
+ { "emoji": "🍘", "name": "rice cracker", "slug": "rice_cracker" },
+ { "emoji": "🍙", "name": "rice ball", "slug": "rice_ball" },
+ { "emoji": "🍚", "name": "cooked rice", "slug": "cooked_rice" },
+ { "emoji": "🍛", "name": "curry rice", "slug": "curry_rice" },
+ { "emoji": "🍜", "name": "steaming bowl", "slug": "steaming_bowl" },
+ { "emoji": "🍝", "name": "spaghetti", "slug": "spaghetti" },
{
"emoji": "🍠",
"name": "roasted sweet potato",
"slug": "roasted_sweet_potato"
},
- {
- "emoji": "🍢",
- "name": "oden",
- "slug": "oden"
- },
- {
- "emoji": "🍣",
- "name": "sushi",
- "slug": "sushi"
- },
- {
- "emoji": "🍤",
- "name": "fried shrimp",
- "slug": "fried_shrimp"
- },
+ { "emoji": "🍢", "name": "oden", "slug": "oden" },
+ { "emoji": "🍣", "name": "sushi", "slug": "sushi" },
+ { "emoji": "🍤", "name": "fried shrimp", "slug": "fried_shrimp" },
{
"emoji": "🍥",
"name": "fish cake with swirl",
"slug": "fish_cake_with_swirl"
},
- {
- "emoji": "🥮",
- "name": "moon cake",
- "slug": "moon_cake"
- },
- {
- "emoji": "🍡",
- "name": "dango",
- "slug": "dango"
- },
- {
- "emoji": "🥟",
- "name": "dumpling",
- "slug": "dumpling"
- },
- {
- "emoji": "🥠",
- "name": "fortune cookie",
- "slug": "fortune_cookie"
- },
- {
- "emoji": "🥡",
- "name": "takeout box",
- "slug": "takeout_box"
- },
- {
- "emoji": "🦀",
- "name": "crab",
- "slug": "crab"
- },
- {
- "emoji": "🦞",
- "name": "lobster",
- "slug": "lobster"
- },
- {
- "emoji": "🦐",
- "name": "shrimp",
- "slug": "shrimp"
- },
- {
- "emoji": "🦑",
- "name": "squid",
- "slug": "squid"
- },
- {
- "emoji": "🦪",
- "name": "oyster",
- "slug": "oyster"
- },
- {
- "emoji": "🍦",
- "name": "soft ice cream",
- "slug": "soft_ice_cream"
- },
- {
- "emoji": "🍧",
- "name": "shaved ice",
- "slug": "shaved_ice"
- },
- {
- "emoji": "🍨",
- "name": "ice cream",
- "slug": "ice_cream"
- },
- {
- "emoji": "🍩",
- "name": "doughnut",
- "slug": "doughnut"
- },
- {
- "emoji": "🍪",
- "name": "cookie",
- "slug": "cookie"
- },
- {
- "emoji": "🎂",
- "name": "birthday cake",
- "slug": "birthday_cake"
- },
- {
- "emoji": "🍰",
- "name": "shortcake",
- "slug": "shortcake"
- },
- {
- "emoji": "🧁",
- "name": "cupcake",
- "slug": "cupcake"
- },
- {
- "emoji": "🥧",
- "name": "pie",
- "slug": "pie"
- },
- {
- "emoji": "🍫",
- "name": "chocolate bar",
- "slug": "chocolate_bar"
- },
- {
- "emoji": "🍬",
- "name": "candy",
- "slug": "candy"
- },
- {
- "emoji": "🍭",
- "name": "lollipop",
- "slug": "lollipop"
- },
- {
- "emoji": "🍮",
- "name": "custard",
- "slug": "custard"
- },
- {
- "emoji": "🍯",
- "name": "honey pot",
- "slug": "honey_pot"
- },
- {
- "emoji": "🍼",
- "name": "baby bottle",
- "slug": "baby_bottle"
- },
- {
- "emoji": "🥛",
- "name": "glass of milk",
- "slug": "glass_of_milk"
- },
- {
- "emoji": "☕",
- "name": "hot beverage",
- "slug": "hot_beverage"
- },
- {
- "emoji": "🫖",
- "name": "teapot",
- "slug": "teapot"
- },
+ { "emoji": "🥮", "name": "moon cake", "slug": "moon_cake" },
+ { "emoji": "🍡", "name": "dango", "slug": "dango" },
+ { "emoji": "🥟", "name": "dumpling", "slug": "dumpling" },
+ { "emoji": "🥠", "name": "fortune cookie", "slug": "fortune_cookie" },
+ { "emoji": "🥡", "name": "takeout box", "slug": "takeout_box" },
+ { "emoji": "🦀", "name": "crab", "slug": "crab" },
+ { "emoji": "🦞", "name": "lobster", "slug": "lobster" },
+ { "emoji": "🦐", "name": "shrimp", "slug": "shrimp" },
+ { "emoji": "🦑", "name": "squid", "slug": "squid" },
+ { "emoji": "🦪", "name": "oyster", "slug": "oyster" },
+ { "emoji": "🍦", "name": "soft ice cream", "slug": "soft_ice_cream" },
+ { "emoji": "🍧", "name": "shaved ice", "slug": "shaved_ice" },
+ { "emoji": "🍨", "name": "ice cream", "slug": "ice_cream" },
+ { "emoji": "🍩", "name": "doughnut", "slug": "doughnut" },
+ { "emoji": "🍪", "name": "cookie", "slug": "cookie" },
+ { "emoji": "🎂", "name": "birthday cake", "slug": "birthday_cake" },
+ { "emoji": "🍰", "name": "shortcake", "slug": "shortcake" },
+ { "emoji": "🧁", "name": "cupcake", "slug": "cupcake" },
+ { "emoji": "🥧", "name": "pie", "slug": "pie" },
+ { "emoji": "🍫", "name": "chocolate bar", "slug": "chocolate_bar" },
+ { "emoji": "🍬", "name": "candy", "slug": "candy" },
+ { "emoji": "🍭", "name": "lollipop", "slug": "lollipop" },
+ { "emoji": "🍮", "name": "custard", "slug": "custard" },
+ { "emoji": "🍯", "name": "honey pot", "slug": "honey_pot" },
+ { "emoji": "🍼", "name": "baby bottle", "slug": "baby_bottle" },
+ { "emoji": "🥛", "name": "glass of milk", "slug": "glass_of_milk" },
+ { "emoji": "☕️", "name": "hot beverage", "slug": "hot_beverage" },
+ { "emoji": "🫖", "name": "teapot", "slug": "teapot" },
{
"emoji": "🍵",
"name": "teacup without handle",
"slug": "teacup_without_handle"
},
- {
- "emoji": "🍶",
- "name": "sake",
- "slug": "sake"
- },
+ { "emoji": "🍶", "name": "sake", "slug": "sake" },
{
"emoji": "🍾",
"name": "bottle with popping cork",
"slug": "bottle_with_popping_cork"
},
- {
- "emoji": "🍷",
- "name": "wine glass",
- "slug": "wine_glass"
- },
- {
- "emoji": "🍸",
- "name": "cocktail glass",
- "slug": "cocktail_glass"
- },
- {
- "emoji": "🍹",
- "name": "tropical drink",
- "slug": "tropical_drink"
- },
- {
- "emoji": "🍺",
- "name": "beer mug",
- "slug": "beer_mug"
- },
+ { "emoji": "🍷", "name": "wine glass", "slug": "wine_glass" },
+ { "emoji": "🍸", "name": "cocktail glass", "slug": "cocktail_glass" },
+ { "emoji": "🍹", "name": "tropical drink", "slug": "tropical_drink" },
+ { "emoji": "🍺", "name": "beer mug", "slug": "beer_mug" },
{
"emoji": "🍻",
"name": "clinking beer mugs",
"slug": "clinking_beer_mugs"
},
- {
- "emoji": "🥂",
- "name": "clinking glasses",
- "slug": "clinking_glasses"
- },
- {
- "emoji": "🥃",
- "name": "tumbler glass",
- "slug": "tumbler_glass"
- },
- {
- "emoji": "🫗",
- "name": "pouring liquid",
- "slug": "pouring_liquid"
- },
- {
- "emoji": "🥤",
- "name": "cup with straw",
- "slug": "cup_with_straw"
- },
- {
- "emoji": "🧋",
- "name": "bubble tea",
- "slug": "bubble_tea"
- },
- {
- "emoji": "🧃",
- "name": "beverage box",
- "slug": "beverage_box"
- },
- {
- "emoji": "🧉",
- "name": "mate",
- "slug": "mate"
- },
- {
- "emoji": "🧊",
- "name": "ice",
- "slug": "ice"
- },
- {
- "emoji": "🥢",
- "name": "chopsticks",
- "slug": "chopsticks"
- },
+ { "emoji": "🥂", "name": "clinking glasses", "slug": "clinking_glasses" },
+ { "emoji": "🥃", "name": "tumbler glass", "slug": "tumbler_glass" },
+ { "emoji": "🫗", "name": "pouring liquid", "slug": "pouring_liquid" },
+ { "emoji": "🥤", "name": "cup with straw", "slug": "cup_with_straw" },
+ { "emoji": "🧋", "name": "bubble tea", "slug": "bubble_tea" },
+ { "emoji": "🧃", "name": "beverage box", "slug": "beverage_box" },
+ { "emoji": "🧉", "name": "mate", "slug": "mate" },
+ { "emoji": "🧊", "name": "ice", "slug": "ice" },
+ { "emoji": "🥢", "name": "chopsticks", "slug": "chopsticks" },
{
"emoji": "🍽️",
"name": "fork and knife with plate",
"slug": "fork_and_knife_with_plate"
},
- {
- "emoji": "🍴",
- "name": "fork and knife",
- "slug": "fork_and_knife"
- },
- {
- "emoji": "🥄",
- "name": "spoon",
- "slug": "spoon"
- },
- {
- "emoji": "🔪",
- "name": "kitchen knife",
- "slug": "kitchen_knife"
- },
- {
- "emoji": "🫙",
- "name": "jar",
- "slug": "jar"
- },
- {
- "emoji": "🏺",
- "name": "amphora",
- "slug": "amphora"
- }
+ { "emoji": "🍴", "name": "fork and knife", "slug": "fork_and_knife" },
+ { "emoji": "🥄", "name": "spoon", "slug": "spoon" },
+ { "emoji": "🔪", "name": "kitchen knife", "slug": "kitchen_knife" },
+ { "emoji": "🫙", "name": "jar", "slug": "jar" },
+ { "emoji": "🏺", "name": "amphora", "slug": "amphora" }
]
},
{
@@ -4032,16 +2168,8 @@
"name": "globe with meridians",
"slug": "globe_with_meridians_world_earth"
},
- {
- "emoji": "🗺️",
- "name": "world map",
- "slug": "world_map"
- },
- {
- "emoji": "🗾",
- "name": "map of Japan",
- "slug": "map_of_japan"
- },
+ { "emoji": "🗺️", "name": "world map", "slug": "world_map" },
+ { "emoji": "🗾", "name": "map of Japan", "slug": "map_of_japan" },
{
"emoji": "🧭",
"name": "compass",
@@ -4052,51 +2180,19 @@
"name": "snow-capped mountain",
"slug": "snow_capped_mountain"
},
- {
- "emoji": "⛰️",
- "name": "mountain",
- "slug": "mountain"
- },
- {
- "emoji": "🌋",
- "name": "volcano",
- "slug": "volcano"
- },
- {
- "emoji": "🗻",
- "name": "mount fuji",
- "slug": "mount_fuji"
- },
- {
- "emoji": "🏕️",
- "name": "camping",
- "slug": "camping"
- },
+ { "emoji": "⛰️", "name": "mountain", "slug": "mountain" },
+ { "emoji": "🌋", "name": "volcano", "slug": "volcano" },
+ { "emoji": "🗻", "name": "mount fuji", "slug": "mount_fuji" },
+ { "emoji": "🏕️", "name": "camping", "slug": "camping" },
{
"emoji": "🏖️",
"name": "beach with umbrella",
"slug": "beach_with_umbrella"
},
- {
- "emoji": "🏜️",
- "name": "desert",
- "slug": "desert"
- },
- {
- "emoji": "🏝️",
- "name": "desert island",
- "slug": "desert_island"
- },
- {
- "emoji": "🏞️",
- "name": "national park",
- "slug": "national_park"
- },
- {
- "emoji": "🏟️",
- "name": "stadium",
- "slug": "stadium"
- },
+ { "emoji": "🏜️", "name": "desert", "slug": "desert" },
+ { "emoji": "🏝️", "name": "desert island", "slug": "desert_island" },
+ { "emoji": "🏞️", "name": "national park", "slug": "national_park" },
+ { "emoji": "🏟️", "name": "stadium", "slug": "stadium" },
{
"emoji": "🏛️",
"name": "classical building",
@@ -4107,356 +2203,104 @@
"name": "building construction",
"slug": "building_construction"
},
- {
- "emoji": "🧱",
- "name": "brick",
- "slug": "brick"
- },
- {
- "emoji": "🪨",
- "name": "rock",
- "slug": "rock"
- },
- {
- "emoji": "🪵",
- "name": "wood",
- "slug": "wood"
- },
- {
- "emoji": "🛖",
- "name": "hut",
- "slug": "hut"
- },
- {
- "emoji": "🏘️",
- "name": "houses",
- "slug": "houses"
- },
- {
- "emoji": "🏚️",
- "name": "derelict house",
- "slug": "derelict_house"
- },
- {
- "emoji": "🏠",
- "name": "house",
- "slug": "house"
- },
+ { "emoji": "🧱", "name": "brick", "slug": "brick" },
+ { "emoji": "🪨", "name": "rock", "slug": "rock" },
+ { "emoji": "🪵", "name": "wood", "slug": "wood" },
+ { "emoji": "🛖", "name": "hut", "slug": "hut" },
+ { "emoji": "🏘️", "name": "houses", "slug": "houses" },
+ { "emoji": "🏚️", "name": "derelict house", "slug": "derelict_house" },
+ { "emoji": "🏠", "name": "house", "slug": "house" },
{
"emoji": "🏡",
"name": "house with garden",
"slug": "house_with_garden"
},
- {
- "emoji": "🏢",
- "name": "office building",
- "slug": "office_building"
- },
+ { "emoji": "🏢", "name": "office building", "slug": "office_building" },
{
"emoji": "🏣",
"name": "Japanese post office",
"slug": "japanese_post_office"
},
- {
- "emoji": "🏤",
- "name": "post office",
- "slug": "post_office"
- },
- {
- "emoji": "🏥",
- "name": "hospital",
- "slug": "hospital"
- },
- {
- "emoji": "🏦",
- "name": "bank",
- "slug": "bank"
- },
- {
- "emoji": "🏨",
- "name": "hotel",
- "slug": "hotel"
- },
- {
- "emoji": "🏩",
- "name": "love hotel",
- "slug": "love_hotel"
- },
+ { "emoji": "🏤", "name": "post office", "slug": "post_office" },
+ { "emoji": "🏥", "name": "hospital", "slug": "hospital" },
+ { "emoji": "🏦", "name": "bank", "slug": "bank" },
+ { "emoji": "🏨", "name": "hotel", "slug": "hotel" },
+ { "emoji": "🏩", "name": "love hotel", "slug": "love_hotel" },
{
"emoji": "🏪",
"name": "convenience store",
"slug": "convenience_store"
},
- {
- "emoji": "🏫",
- "name": "school",
- "slug": "school"
- },
- {
- "emoji": "🏬",
- "name": "department store",
- "slug": "department_store"
- },
- {
- "emoji": "🏭",
- "name": "factory",
- "slug": "factory"
- },
- {
- "emoji": "🏯",
- "name": "Japanese castle",
- "slug": "japanese_castle"
- },
- {
- "emoji": "🏰",
- "name": "castle",
- "slug": "castle"
- },
- {
- "emoji": "💒",
- "name": "wedding",
- "slug": "wedding"
- },
- {
- "emoji": "🗼",
- "name": "Tokyo tower",
- "slug": "tokyo_tower"
- },
+ { "emoji": "🏫", "name": "school", "slug": "school" },
+ { "emoji": "🏬", "name": "department store", "slug": "department_store" },
+ { "emoji": "🏭", "name": "factory", "slug": "factory" },
+ { "emoji": "🏯", "name": "Japanese castle", "slug": "japanese_castle" },
+ { "emoji": "🏰", "name": "castle", "slug": "castle" },
+ { "emoji": "💒", "name": "wedding", "slug": "wedding" },
+ { "emoji": "🗼", "name": "Tokyo tower", "slug": "tokyo_tower" },
{
"emoji": "🗽",
"name": "Statue of Liberty",
"slug": "statue_of_liberty"
},
- {
- "emoji": "⛪",
- "name": "church",
- "slug": "church"
- },
- {
- "emoji": "🕌",
- "name": "mosque",
- "slug": "mosque"
- },
- {
- "emoji": "🛕",
- "name": "hindu temple",
- "slug": "hindu_temple"
- },
- {
- "emoji": "🕍",
- "name": "synagogue",
- "slug": "synagogue"
- },
- {
- "emoji": "⛩️",
- "name": "shinto shrine",
- "slug": "shinto_shrine"
- },
- {
- "emoji": "🕋",
- "name": "kaaba",
- "slug": "kaaba"
- },
- {
- "emoji": "⛲",
- "name": "fountain",
- "slug": "fountain"
- },
- {
- "emoji": "⛺",
- "name": "tent",
- "slug": "tent"
- },
- {
- "emoji": "🌁",
- "name": "foggy",
- "slug": "foggy"
- },
- {
- "emoji": "🌃",
- "name": "night with stars",
- "slug": "night_with_stars"
- },
- {
- "emoji": "🏙️",
- "name": "cityscape",
- "slug": "cityscape"
- },
+ { "emoji": "⛪️", "name": "church", "slug": "church" },
+ { "emoji": "🕌", "name": "mosque", "slug": "mosque" },
+ { "emoji": "🛕", "name": "hindu temple", "slug": "hindu_temple" },
+ { "emoji": "🕍", "name": "synagogue", "slug": "synagogue" },
+ { "emoji": "⛩️", "name": "shinto shrine", "slug": "shinto_shrine" },
+ { "emoji": "🕋", "name": "kaaba", "slug": "kaaba" },
+ { "emoji": "⛲️", "name": "fountain", "slug": "fountain" },
+ { "emoji": "⛺️", "name": "tent", "slug": "tent" },
+ { "emoji": "🌁", "name": "foggy", "slug": "foggy" },
+ { "emoji": "🌃", "name": "night with stars", "slug": "night_with_stars" },
+ { "emoji": "🏙️", "name": "cityscape", "slug": "cityscape" },
{
"emoji": "🌄",
"name": "sunrise over mountains",
"slug": "sunrise_over_mountains"
},
- {
- "emoji": "🌅",
- "name": "sunrise",
- "slug": "sunrise"
- },
+ { "emoji": "🌅", "name": "sunrise", "slug": "sunrise" },
{
"emoji": "🌆",
"name": "cityscape at dusk",
"slug": "cityscape_at_dusk"
},
- {
- "emoji": "🌇",
- "name": "sunset",
- "slug": "sunset"
- },
- {
- "emoji": "🌉",
- "name": "bridge at night",
- "slug": "bridge_at_night"
- },
- {
- "emoji": "♨️",
- "name": "hot springs",
- "slug": "hot_springs"
- },
- {
- "emoji": "🎠",
- "name": "carousel horse",
- "slug": "carousel_horse"
- },
- {
- "emoji": "🛝",
- "name": "playground slide",
- "slug": "playground_slide"
- },
- {
- "emoji": "🎡",
- "name": "ferris wheel",
- "slug": "ferris_wheel"
- },
- {
- "emoji": "🎢",
- "name": "roller coaster",
- "slug": "roller_coaster"
- },
- {
- "emoji": "💈",
- "name": "barber pole",
- "slug": "barber_pole"
- },
- {
- "emoji": "🎪",
- "name": "circus tent",
- "slug": "circus_tent"
- },
- {
- "emoji": "🚂",
- "name": "locomotive",
- "slug": "locomotive"
- },
- {
- "emoji": "🚃",
- "name": "railway car",
- "slug": "railway_car"
- },
- {
- "emoji": "🚄",
- "name": "high-speed train",
- "slug": "high_speed_train"
- },
- {
- "emoji": "🚅",
- "name": "bullet train",
- "slug": "bullet_train"
- },
- {
- "emoji": "🚆",
- "name": "train",
- "slug": "train"
- },
- {
- "emoji": "🚇",
- "name": "metro",
- "slug": "metro"
- },
- {
- "emoji": "🚈",
- "name": "light rail",
- "slug": "light_rail"
- },
- {
- "emoji": "🚉",
- "name": "station",
- "slug": "station"
- },
- {
- "emoji": "🚊",
- "name": "tram",
- "slug": "tram"
- },
- {
- "emoji": "🚝",
- "name": "monorail",
- "slug": "monorail"
- },
- {
- "emoji": "🚞",
- "name": "mountain railway",
- "slug": "mountain_railway"
- },
- {
- "emoji": "🚋",
- "name": "tram car",
- "slug": "tram_car"
- },
- {
- "emoji": "🚌",
- "name": "bus",
- "slug": "bus"
- },
- {
- "emoji": "🚍",
- "name": "oncoming bus",
- "slug": "oncoming_bus"
- },
- {
- "emoji": "🚎",
- "name": "trolleybus",
- "slug": "trolleybus"
- },
- {
- "emoji": "🚐",
- "name": "minibus",
- "slug": "minibus"
- },
- {
- "emoji": "🚑",
- "name": "ambulance",
- "slug": "ambulance"
- },
- {
- "emoji": "🚒",
- "name": "fire engine",
- "slug": "fire_engine"
- },
- {
- "emoji": "🚓",
- "name": "police car",
- "slug": "police_car"
- },
+ { "emoji": "🌇", "name": "sunset", "slug": "sunset" },
+ { "emoji": "🌉", "name": "bridge at night", "slug": "bridge_at_night" },
+ { "emoji": "♨️", "name": "hot springs", "slug": "hot_springs" },
+ { "emoji": "🎠", "name": "carousel horse", "slug": "carousel_horse" },
+ { "emoji": "🛝", "name": "playground slide", "slug": "playground_slide" },
+ { "emoji": "🎡", "name": "ferris wheel", "slug": "ferris_wheel" },
+ { "emoji": "🎢", "name": "roller coaster", "slug": "roller_coaster" },
+ { "emoji": "💈", "name": "barber pole", "slug": "barber_pole" },
+ { "emoji": "🎪", "name": "circus tent", "slug": "circus_tent" },
+ { "emoji": "🚂", "name": "locomotive", "slug": "locomotive" },
+ { "emoji": "🚃", "name": "railway car", "slug": "railway_car" },
+ { "emoji": "🚄", "name": "high-speed train", "slug": "high_speed_train" },
+ { "emoji": "🚅", "name": "bullet train", "slug": "bullet_train" },
+ { "emoji": "🚆", "name": "train", "slug": "train" },
+ { "emoji": "🚇", "name": "metro", "slug": "metro" },
+ { "emoji": "🚈", "name": "light rail", "slug": "light_rail" },
+ { "emoji": "🚉", "name": "station", "slug": "station" },
+ { "emoji": "🚊", "name": "tram", "slug": "tram" },
+ { "emoji": "🚝", "name": "monorail", "slug": "monorail" },
+ { "emoji": "🚞", "name": "mountain railway", "slug": "mountain_railway" },
+ { "emoji": "🚋", "name": "tram car", "slug": "tram_car" },
+ { "emoji": "🚌", "name": "bus", "slug": "bus" },
+ { "emoji": "🚍", "name": "oncoming bus", "slug": "oncoming_bus" },
+ { "emoji": "🚎", "name": "trolleybus", "slug": "trolleybus" },
+ { "emoji": "🚐", "name": "minibus", "slug": "minibus" },
+ { "emoji": "🚑", "name": "ambulance", "slug": "ambulance" },
+ { "emoji": "🚒", "name": "fire engine", "slug": "fire_engine" },
+ { "emoji": "🚓", "name": "police car", "slug": "police_car" },
{
"emoji": "🚔",
"name": "oncoming police car",
"slug": "oncoming_police_car"
},
- {
- "emoji": "🚕",
- "name": "taxi",
- "slug": "taxi"
- },
- {
- "emoji": "🚖",
- "name": "oncoming taxi",
- "slug": "oncoming_taxi"
- },
- {
- "emoji": "🚗",
- "name": "automobile",
- "slug": "automobile"
- },
+ { "emoji": "🚕", "name": "taxi", "slug": "taxi" },
+ { "emoji": "🚖", "name": "oncoming taxi", "slug": "oncoming_taxi" },
+ { "emoji": "🚗", "name": "automobile", "slug": "automobile" },
{
"emoji": "🚘",
"name": "oncoming automobile",
@@ -4467,41 +2311,17 @@
"name": "sport utility vehicle",
"slug": "sport_utility_vehicle"
},
- {
- "emoji": "🛻",
- "name": "pickup truck",
- "slug": "pickup_truck"
- },
- {
- "emoji": "🚚",
- "name": "delivery truck",
- "slug": "delivery_truck"
- },
+ { "emoji": "🛻", "name": "pickup truck", "slug": "pickup_truck" },
+ { "emoji": "🚚", "name": "delivery truck", "slug": "delivery_truck" },
{
"emoji": "🚛",
"name": "articulated lorry",
"slug": "articulated_lorry"
},
- {
- "emoji": "🚜",
- "name": "tractor",
- "slug": "tractor"
- },
- {
- "emoji": "🏎️",
- "name": "racing car",
- "slug": "racing_car"
- },
- {
- "emoji": "🏍️",
- "name": "motorcycle",
- "slug": "motorcycle"
- },
- {
- "emoji": "🛵",
- "name": "motor scooter",
- "slug": "motor_scooter"
- },
+ { "emoji": "🚜", "name": "tractor", "slug": "tractor" },
+ { "emoji": "🏎️", "name": "racing car", "slug": "racing_car" },
+ { "emoji": "🏍️", "name": "motorcycle", "slug": "motorcycle" },
+ { "emoji": "🛵", "name": "motor scooter", "slug": "motor_scooter" },
{
"emoji": "🦽",
"name": "manual wheelchair",
@@ -4512,66 +2332,18 @@
"name": "motorized wheelchair",
"slug": "motorized_wheelchair"
},
- {
- "emoji": "🛺",
- "name": "auto rickshaw",
- "slug": "auto_rickshaw"
- },
- {
- "emoji": "🚲",
- "name": "bicycle",
- "slug": "bicycle"
- },
- {
- "emoji": "🛴",
- "name": "kick scooter",
- "slug": "kick_scooter"
- },
- {
- "emoji": "🛹",
- "name": "skateboard",
- "slug": "skateboard"
- },
- {
- "emoji": "🛼",
- "name": "roller skate",
- "slug": "roller_skate"
- },
- {
- "emoji": "🚏",
- "name": "bus stop",
- "slug": "bus_stop"
- },
- {
- "emoji": "🛣️",
- "name": "motorway",
- "slug": "motorway"
- },
- {
- "emoji": "🛤️",
- "name": "railway track",
- "slug": "railway_track"
- },
- {
- "emoji": "🛢️",
- "name": "oil drum",
- "slug": "oil_drum"
- },
- {
- "emoji": "⛽",
- "name": "fuel pump",
- "slug": "fuel_pump"
- },
- {
- "emoji": "🛞",
- "name": "wheel",
- "slug": "wheel"
- },
- {
- "emoji": "🚨",
- "name": "police car light",
- "slug": "police_car_light"
- },
+ { "emoji": "🛺", "name": "auto rickshaw", "slug": "auto_rickshaw" },
+ { "emoji": "🚲", "name": "bicycle", "slug": "bicycle" },
+ { "emoji": "🛴", "name": "kick scooter", "slug": "kick_scooter" },
+ { "emoji": "🛹", "name": "skateboard", "slug": "skateboard" },
+ { "emoji": "🛼", "name": "roller skate", "slug": "roller_skate" },
+ { "emoji": "🚏", "name": "bus stop", "slug": "bus_stop" },
+ { "emoji": "🛣️", "name": "motorway", "slug": "motorway" },
+ { "emoji": "🛤️", "name": "railway track", "slug": "railway_track" },
+ { "emoji": "🛢️", "name": "oil drum", "slug": "oil_drum" },
+ { "emoji": "⛽️", "name": "fuel pump", "slug": "fuel_pump" },
+ { "emoji": "🛞", "name": "wheel", "slug": "wheel" },
+ { "emoji": "🚨", "name": "police car light", "slug": "police_car_light" },
{
"emoji": "🚥",
"name": "horizontal traffic light",
@@ -4582,96 +2354,28 @@
"name": "vertical traffic light",
"slug": "vertical_traffic_light"
},
- {
- "emoji": "🛑",
- "name": "stop sign",
- "slug": "stop_sign"
- },
- {
- "emoji": "🚧",
- "name": "construction",
- "slug": "construction"
- },
- {
- "emoji": "⚓",
- "name": "anchor",
- "slug": "anchor"
- },
- {
- "emoji": "🛟",
- "name": "ring buoy",
- "slug": "ring_buoy"
- },
- {
- "emoji": "⛵",
- "name": "sailboat",
- "slug": "sailboat"
- },
- {
- "emoji": "🛶",
- "name": "canoe",
- "slug": "canoe"
- },
- {
- "emoji": "🚤",
- "name": "speedboat",
- "slug": "speedboat"
- },
- {
- "emoji": "🛳️",
- "name": "passenger ship",
- "slug": "passenger_ship"
- },
- {
- "emoji": "⛴️",
- "name": "ferry",
- "slug": "ferry"
- },
- {
- "emoji": "🛥️",
- "name": "motor boat",
- "slug": "motor_boat"
- },
- {
- "emoji": "🚢",
- "name": "ship",
- "slug": "ship"
- },
- {
- "emoji": "✈️",
- "name": "airplane",
- "slug": "airplane"
- },
- {
- "emoji": "🛩️",
- "name": "small airplane",
- "slug": "small_airplane"
- },
+ { "emoji": "🛑", "name": "stop sign", "slug": "stop_sign" },
+ { "emoji": "🚧", "name": "construction", "slug": "construction" },
+ { "emoji": "⚓️", "name": "anchor", "slug": "anchor" },
+ { "emoji": "🛟", "name": "ring buoy", "slug": "ring_buoy" },
+ { "emoji": "⛵️", "name": "sailboat", "slug": "sailboat" },
+ { "emoji": "🛶", "name": "canoe", "slug": "canoe" },
+ { "emoji": "🚤", "name": "speedboat", "slug": "speedboat" },
+ { "emoji": "🛳️", "name": "passenger ship", "slug": "passenger_ship" },
+ { "emoji": "⛴️", "name": "ferry", "slug": "ferry" },
+ { "emoji": "🛥️", "name": "motor boat", "slug": "motor_boat" },
+ { "emoji": "🚢", "name": "ship", "slug": "ship" },
+ { "emoji": "✈️", "name": "airplane", "slug": "airplane" },
+ { "emoji": "🛩️", "name": "small airplane", "slug": "small_airplane" },
{
"emoji": "🛫",
"name": "airplane departure",
"slug": "airplane_departure"
},
- {
- "emoji": "🛬",
- "name": "airplane arrival",
- "slug": "airplane_arrival"
- },
- {
- "emoji": "🪂",
- "name": "parachute",
- "slug": "parachute"
- },
- {
- "emoji": "💺",
- "name": "seat",
- "slug": "seat"
- },
- {
- "emoji": "🚁",
- "name": "helicopter",
- "slug": "helicopter"
- },
+ { "emoji": "🛬", "name": "airplane arrival", "slug": "airplane_arrival" },
+ { "emoji": "🪂", "name": "parachute", "slug": "parachute" },
+ { "emoji": "💺", "name": "seat", "slug": "seat" },
+ { "emoji": "🚁", "name": "helicopter", "slug": "helicopter" },
{
"emoji": "🚟",
"name": "suspension railway",
@@ -4682,66 +2386,22 @@
"name": "mountain cableway",
"slug": "mountain_cableway"
},
+ { "emoji": "🚡", "name": "aerial tramway", "slug": "aerial_tramway" },
+ { "emoji": "🛰️", "name": "satellite", "slug": "satellite" },
+ { "emoji": "🚀", "name": "rocket", "slug": "rocket" },
+ { "emoji": "🛸", "name": "flying saucer", "slug": "flying_saucer" },
+ { "emoji": "🛎️", "name": "bellhop bell", "slug": "bellhop_bell" },
+ { "emoji": "🧳", "name": "luggage", "slug": "luggage" },
+ { "emoji": "⌛️", "name": "hourglass done", "slug": "hourglass_done" },
{
- "emoji": "🚡",
- "name": "aerial tramway",
- "slug": "aerial_tramway"
- },
- {
- "emoji": "🛰️",
- "name": "satellite",
- "slug": "satellite"
- },
- {
- "emoji": "🚀",
- "name": "rocket",
- "slug": "rocket"
- },
- {
- "emoji": "🛸",
- "name": "flying saucer",
- "slug": "flying_saucer"
- },
- {
- "emoji": "🛎️",
- "name": "bellhop bell",
- "slug": "bellhop_bell"
- },
- {
- "emoji": "🧳",
- "name": "luggage",
- "slug": "luggage"
- },
- {
- "emoji": "⌛",
- "name": "hourglass done",
- "slug": "hourglass_done"
- },
- {
- "emoji": "⏳",
+ "emoji": "⏳️",
"name": "hourglass not done",
"slug": "hourglass_not_done"
},
- {
- "emoji": "⌚",
- "name": "watch",
- "slug": "watch_clock"
- },
- {
- "emoji": "⏰",
- "name": "alarm clock",
- "slug": "alarm_clock"
- },
- {
- "emoji": "⏱️",
- "name": "stopwatch",
- "slug": "stopwatch"
- },
- {
- "emoji": "⏲️",
- "name": "timer clock",
- "slug": "timer_clock"
- },
+ { "emoji": "⌚️", "name": "watch", "slug": "watch_clock" },
+ { "emoji": "⏰️", "name": "alarm clock", "slug": "alarm_clock" },
+ { "emoji": "⏱️", "name": "stopwatch", "slug": "stopwatch" },
+ { "emoji": "⏲️", "name": "timer clock", "slug": "timer_clock" },
{
"emoji": "🕰️",
"name": "mantelpiece clock",
@@ -4757,106 +2417,54 @@
"name": "twelve-thirty",
"slug": "twelve_thirty_12_12:30"
},
- {
- "emoji": "🕐",
- "name": "one o’clock",
- "slug": "one_o_clock_00_1_1:00"
- },
- {
- "emoji": "🕜",
- "name": "one-thirty",
- "slug": "one_thirty_1_1:30"
- },
- {
- "emoji": "🕑",
- "name": "two o’clock",
- "slug": "two_o_clock_00_2_2:00"
- },
- {
- "emoji": "🕝",
- "name": "two-thirty",
- "slug": "two_thirty_2_2:30"
- },
+ { "emoji": "🕐", "name": "one o’clock", "slug": "one_o_clock_00_1_1:00" },
+ { "emoji": "🕜", "name": "one-thirty", "slug": "one_thirty_1_1:30" },
+ { "emoji": "🕑", "name": "two o’clock", "slug": "two_o_clock_00_2_2:00" },
+ { "emoji": "🕝", "name": "two-thirty", "slug": "two_thirty_2_2:30" },
{
"emoji": "🕒",
"name": "three o’clock",
"slug": "three_o_clock_00_3_3:00"
},
- {
- "emoji": "🕞",
- "name": "three-thirty",
- "slug": "three_thirty_3_3:30"
- },
+ { "emoji": "🕞", "name": "three-thirty", "slug": "three_thirty_3_3:30" },
{
"emoji": "🕓",
"name": "four o’clock",
"slug": "four_o_clock_00_4_4:00"
},
- {
- "emoji": "🕟",
- "name": "four-thirty",
- "slug": "four_thirty_4_4:30"
- },
+ { "emoji": "🕟", "name": "four-thirty", "slug": "four_thirty_4_4:30" },
{
"emoji": "🕔",
"name": "five o’clock",
"slug": "five_o_clock_00_5_5:00"
},
- {
- "emoji": "🕠",
- "name": "five-thirty",
- "slug": "five_thirty_5_5:30"
- },
- {
- "emoji": "🕕",
- "name": "six o’clock",
- "slug": "six_o_clock_00_6_6:00"
- },
- {
- "emoji": "🕡",
- "name": "six-thirty",
- "slug": "six_thirty_6_6:30"
- },
+ { "emoji": "🕠", "name": "five-thirty", "slug": "five_thirty_5_5:30" },
+ { "emoji": "🕕", "name": "six o’clock", "slug": "six_o_clock_00_6_6:00" },
+ { "emoji": "🕡", "name": "six-thirty", "slug": "six_thirty_6_6:30" },
{
"emoji": "🕖",
"name": "seven o’clock",
"slug": "seven_o_clock_00_7_7:00"
},
- {
- "emoji": "🕢",
- "name": "seven-thirty",
- "slug": "seven_thirty_7_7:30"
- },
+ { "emoji": "🕢", "name": "seven-thirty", "slug": "seven_thirty_7_7:30" },
{
"emoji": "🕗",
"name": "eight o’clock",
"slug": "eight_o_clock_00_8_8:00"
},
- {
- "emoji": "🕣",
- "name": "eight-thirty",
- "slug": "eight_thirty_8_8:30"
- },
+ { "emoji": "🕣", "name": "eight-thirty", "slug": "eight_thirty_8_8:30" },
{
"emoji": "🕘",
"name": "nine o’clock",
"slug": "nine_o_clock_00_9_9:00"
},
- {
- "emoji": "🕤",
- "name": "nine-thirty",
- "slug": "nine_thirty_9_9:30"
- },
+ { "emoji": "🕤", "name": "nine-thirty", "slug": "nine_thirty_9_9:30" },
{
"emoji": "🕙",
"name": "ten o’clock",
"slug": "ten_o_clock_00_10_10:00"
},
- {
- "emoji": "🕥",
- "name": "ten-thirty",
- "slug": "ten_thirty_10_10:30"
- },
+ { "emoji": "🕥", "name": "ten-thirty", "slug": "ten_thirty_10_10:30" },
{
"emoji": "🕚",
"name": "eleven o’clock",
@@ -4867,11 +2475,7 @@
"name": "eleven-thirty",
"slug": "eleven_thirty_11_11:30"
},
- {
- "emoji": "🌑",
- "name": "new moon",
- "slug": "new_moon_dark"
- },
+ { "emoji": "🌑", "name": "new moon", "slug": "new_moon_dark" },
{
"emoji": "🌒",
"name": "waxing crescent moon",
@@ -4887,11 +2491,7 @@
"name": "waxing gibbous moon",
"slug": "waxing_gibbous_moon"
},
- {
- "emoji": "🌕",
- "name": "full moon",
- "slug": "full_moon"
- },
+ { "emoji": "🌕", "name": "full moon", "slug": "full_moon" },
{
"emoji": "🌖",
"name": "waning gibbous moon",
@@ -4907,16 +2507,8 @@
"name": "waning crescent moon",
"slug": "waning_crescent_moon"
},
- {
- "emoji": "🌙",
- "name": "crescent moon",
- "slug": "crescent_moon"
- },
- {
- "emoji": "🌚",
- "name": "new moon face",
- "slug": "new_moon_face"
- },
+ { "emoji": "🌙", "name": "crescent moon", "slug": "crescent_moon" },
+ { "emoji": "🌚", "name": "new moon face", "slug": "new_moon_face" },
{
"emoji": "🌛",
"name": "first quarter moon face",
@@ -4927,58 +2519,18 @@
"name": "last quarter moon face",
"slug": "last_quarter_moon_face"
},
+ { "emoji": "🌡️", "name": "thermometer", "slug": "thermometer" },
+ { "emoji": "☀️", "name": "sun", "slug": "sun" },
+ { "emoji": "🌝", "name": "full moon face", "slug": "full_moon_face" },
+ { "emoji": "🌞", "name": "sun with face", "slug": "sun_with_face" },
+ { "emoji": "🪐", "name": "ringed planet", "slug": "ringed_planet" },
+ { "emoji": "⭐️", "name": "star", "slug": "star" },
+ { "emoji": "🌟", "name": "glowing star", "slug": "glowing_star" },
+ { "emoji": "🌠", "name": "shooting star", "slug": "shooting_star" },
+ { "emoji": "🌌", "name": "milky way", "slug": "milky_way" },
+ { "emoji": "☁️", "name": "cloud", "slug": "cloud" },
{
- "emoji": "🌡️",
- "name": "thermometer",
- "slug": "thermometer"
- },
- {
- "emoji": "☀️",
- "name": "sun",
- "slug": "sun"
- },
- {
- "emoji": "🌝",
- "name": "full moon face",
- "slug": "full_moon_face"
- },
- {
- "emoji": "🌞",
- "name": "sun with face",
- "slug": "sun_with_face"
- },
- {
- "emoji": "🪐",
- "name": "ringed planet",
- "slug": "ringed_planet"
- },
- {
- "emoji": "⭐",
- "name": "star",
- "slug": "star"
- },
- {
- "emoji": "🌟",
- "name": "glowing star",
- "slug": "glowing_star"
- },
- {
- "emoji": "🌠",
- "name": "shooting star",
- "slug": "shooting_star"
- },
- {
- "emoji": "🌌",
- "name": "milky way",
- "slug": "milky_way"
- },
- {
- "emoji": "☁️",
- "name": "cloud",
- "slug": "cloud"
- },
- {
- "emoji": "⛅",
+ "emoji": "⛅️",
"name": "sun behind cloud",
"slug": "sun_behind_cloud"
},
@@ -5002,58 +2554,22 @@
"name": "sun behind rain cloud",
"slug": "sun_behind_rain_cloud"
},
- {
- "emoji": "🌧️",
- "name": "cloud with rain",
- "slug": "cloud_with_rain"
- },
- {
- "emoji": "🌨️",
- "name": "cloud with snow",
- "slug": "cloud_with_snow"
- },
+ { "emoji": "🌧️", "name": "cloud with rain", "slug": "cloud_with_rain" },
+ { "emoji": "🌨️", "name": "cloud with snow", "slug": "cloud_with_snow" },
{
"emoji": "🌩️",
"name": "cloud with lightning",
"slug": "cloud_with_lightning"
},
+ { "emoji": "🌪️", "name": "tornado", "slug": "tornado" },
+ { "emoji": "🌫️", "name": "fog", "slug": "fog" },
+ { "emoji": "🌬️", "name": "wind face", "slug": "wind_face" },
+ { "emoji": "🌀", "name": "cyclone", "slug": "cyclone" },
+ { "emoji": "🌈", "name": "rainbow", "slug": "rainbow" },
+ { "emoji": "🌂", "name": "closed umbrella", "slug": "closed_umbrella" },
+ { "emoji": "☂️", "name": "umbrella", "slug": "umbrella" },
{
- "emoji": "🌪️",
- "name": "tornado",
- "slug": "tornado"
- },
- {
- "emoji": "🌫️",
- "name": "fog",
- "slug": "fog"
- },
- {
- "emoji": "🌬️",
- "name": "wind face",
- "slug": "wind_face"
- },
- {
- "emoji": "🌀",
- "name": "cyclone",
- "slug": "cyclone"
- },
- {
- "emoji": "🌈",
- "name": "rainbow",
- "slug": "rainbow"
- },
- {
- "emoji": "🌂",
- "name": "closed umbrella",
- "slug": "closed_umbrella"
- },
- {
- "emoji": "☂️",
- "name": "umbrella",
- "slug": "umbrella"
- },
- {
- "emoji": "☔",
+ "emoji": "☔️",
"name": "umbrella with rain drops",
"slug": "umbrella_with_rain_drops"
},
@@ -5062,46 +2578,18 @@
"name": "umbrella on ground",
"slug": "umbrella_on_ground"
},
+ { "emoji": "⚡️", "name": "high voltage", "slug": "high_voltage" },
+ { "emoji": "❄️", "name": "snowflake", "slug": "snowflake" },
+ { "emoji": "☃️", "name": "snowman", "slug": "snowman" },
{
- "emoji": "⚡",
- "name": "high voltage",
- "slug": "high_voltage"
- },
- {
- "emoji": "❄️",
- "name": "snowflake",
- "slug": "snowflake"
- },
- {
- "emoji": "☃️",
- "name": "snowman",
- "slug": "snowman"
- },
- {
- "emoji": "⛄",
+ "emoji": "⛄️",
"name": "snowman without snow",
"slug": "snowman_without_snow"
},
- {
- "emoji": "☄️",
- "name": "comet",
- "slug": "comet"
- },
- {
- "emoji": "🔥",
- "name": "fire",
- "slug": "fire"
- },
- {
- "emoji": "💧",
- "name": "droplet",
- "slug": "droplet"
- },
- {
- "emoji": "🌊",
- "name": "water wave",
- "slug": "water_wave"
- }
+ { "emoji": "☄️", "name": "comet", "slug": "comet" },
+ { "emoji": "🔥", "name": "fire", "slug": "fire" },
+ { "emoji": "💧", "name": "droplet", "slug": "droplet" },
+ { "emoji": "🌊", "name": "water wave", "slug": "water_wave" }
]
},
{
@@ -5113,36 +2601,12 @@
"name": "jack-o-lantern",
"slug": "jack_o_lantern_halloween_jack"
},
- {
- "emoji": "🎄",
- "name": "Christmas tree",
- "slug": "christmas_tree"
- },
- {
- "emoji": "🎆",
- "name": "fireworks",
- "slug": "fireworks_celebration"
- },
- {
- "emoji": "🎇",
- "name": "sparkler",
- "slug": "sparkler_celebration"
- },
- {
- "emoji": "🧨",
- "name": "firecracker",
- "slug": "firecracker_explosive"
- },
- {
- "emoji": "✨",
- "name": "sparkles",
- "slug": "sparkles_star"
- },
- {
- "emoji": "🎈",
- "name": "balloon",
- "slug": "balloon"
- },
+ { "emoji": "🎄", "name": "Christmas tree", "slug": "christmas_tree" },
+ { "emoji": "🎆", "name": "fireworks", "slug": "fireworks_celebration" },
+ { "emoji": "🎇", "name": "sparkler", "slug": "sparkler_celebration" },
+ { "emoji": "🧨", "name": "firecracker", "slug": "firecracker_explosive" },
+ { "emoji": "✨️", "name": "sparkles", "slug": "sparkles_star" },
+ { "emoji": "🎈", "name": "balloon", "slug": "balloon" },
{
"emoji": "🎉",
"name": "party popper",
@@ -5153,341 +2617,89 @@
"name": "confetti ball",
"slug": "confetti_ball_celebration"
},
- {
- "emoji": "🎋",
- "name": "tanabata tree",
- "slug": "tanabata_tree"
- },
- {
- "emoji": "🎍",
- "name": "pine decoration",
- "slug": "pine_decoration"
- },
- {
- "emoji": "🎎",
- "name": "Japanese dolls",
- "slug": "japanese_dolls"
- },
- {
- "emoji": "🎏",
- "name": "carp streamer",
- "slug": "carp_streamer"
- },
- {
- "emoji": "🎐",
- "name": "wind chime",
- "slug": "wind_chime"
- },
+ { "emoji": "🎋", "name": "tanabata tree", "slug": "tanabata_tree" },
+ { "emoji": "🎍", "name": "pine decoration", "slug": "pine_decoration" },
+ { "emoji": "🎎", "name": "Japanese dolls", "slug": "japanese_dolls" },
+ { "emoji": "🎏", "name": "carp streamer", "slug": "carp_streamer" },
+ { "emoji": "🎐", "name": "wind chime", "slug": "wind_chime" },
{
"emoji": "🎑",
"name": "moon viewing ceremony",
"slug": "moon_viewing_ceremony"
},
- {
- "emoji": "🧧",
- "name": "red envelope",
- "slug": "red_envelope"
- },
- {
- "emoji": "🎀",
- "name": "ribbon",
- "slug": "ribbon_celebration"
- },
- {
- "emoji": "🎁",
- "name": "wrapped gift",
- "slug": "wrapped_gift"
- },
- {
- "emoji": "🎗️",
- "name": "reminder ribbon",
- "slug": "reminder_ribbon"
- },
+ { "emoji": "🧧", "name": "red envelope", "slug": "red_envelope" },
+ { "emoji": "🎀", "name": "ribbon", "slug": "ribbon_celebration" },
+ { "emoji": "🎁", "name": "wrapped gift", "slug": "wrapped_gift" },
+ { "emoji": "🎗️", "name": "reminder ribbon", "slug": "reminder_ribbon" },
{
"emoji": "🎟️",
"name": "admission tickets",
"slug": "admission_tickets"
},
- {
- "emoji": "🎫",
- "name": "ticket",
- "slug": "ticket"
- },
- {
- "emoji": "🎖️",
- "name": "military medal",
- "slug": "military_medal"
- },
- {
- "emoji": "🏆",
- "name": "trophy",
- "slug": "trophy"
- },
- {
- "emoji": "🏅",
- "name": "sports medal",
- "slug": "sports_medal"
- },
- {
- "emoji": "🥇",
- "name": "1st place medal",
- "slug": "1st_place_medal"
- },
- {
- "emoji": "🥈",
- "name": "2nd place medal",
- "slug": "2nd_place_medal"
- },
- {
- "emoji": "🥉",
- "name": "3rd place medal",
- "slug": "3rd_place_medal"
- },
- {
- "emoji": "⚽",
- "name": "soccer ball",
- "slug": "soccer_ball"
- },
- {
- "emoji": "⚾",
- "name": "baseball",
- "slug": "baseball"
- },
- {
- "emoji": "🥎",
- "name": "softball",
- "slug": "softball"
- },
- {
- "emoji": "🏀",
- "name": "basketball",
- "slug": "basketball"
- },
- {
- "emoji": "🏐",
- "name": "volleyball",
- "slug": "volleyball"
- },
+ { "emoji": "🎫", "name": "ticket", "slug": "ticket" },
+ { "emoji": "🎖️", "name": "military medal", "slug": "military_medal" },
+ { "emoji": "🏆", "name": "trophy", "slug": "trophy" },
+ { "emoji": "🏅", "name": "sports medal", "slug": "sports_medal" },
+ { "emoji": "🥇", "name": "1st place medal", "slug": "1st_place_medal" },
+ { "emoji": "🥈", "name": "2nd place medal", "slug": "2nd_place_medal" },
+ { "emoji": "🥉", "name": "3rd place medal", "slug": "3rd_place_medal" },
+ { "emoji": "⚽️", "name": "soccer ball", "slug": "soccer_ball" },
+ { "emoji": "⚾️", "name": "baseball", "slug": "baseball" },
+ { "emoji": "🥎", "name": "softball", "slug": "softball" },
+ { "emoji": "🏀", "name": "basketball", "slug": "basketball" },
+ { "emoji": "🏐", "name": "volleyball", "slug": "volleyball" },
{
"emoji": "🏈",
"name": "american football",
"slug": "american_football"
},
- {
- "emoji": "🏉",
- "name": "rugby football",
- "slug": "rugby_football"
- },
- {
- "emoji": "🎾",
- "name": "tennis",
- "slug": "tennis"
- },
- {
- "emoji": "🥏",
- "name": "flying disc",
- "slug": "flying_disc"
- },
- {
- "emoji": "🎳",
- "name": "bowling",
- "slug": "bowling"
- },
- {
- "emoji": "🏏",
- "name": "cricket game",
- "slug": "cricket_game"
- },
- {
- "emoji": "🏑",
- "name": "field hockey",
- "slug": "field_hockey"
- },
- {
- "emoji": "🏒",
- "name": "ice hockey",
- "slug": "ice_hockey"
- },
- {
- "emoji": "🥍",
- "name": "lacrosse",
- "slug": "lacrosse"
- },
- {
- "emoji": "🏓",
- "name": "ping pong",
- "slug": "ping_pong"
- },
- {
- "emoji": "🏸",
- "name": "badminton",
- "slug": "badminton"
- },
- {
- "emoji": "🥊",
- "name": "boxing glove",
- "slug": "boxing_glove"
- },
+ { "emoji": "🏉", "name": "rugby football", "slug": "rugby_football" },
+ { "emoji": "🎾", "name": "tennis", "slug": "tennis" },
+ { "emoji": "🥏", "name": "flying disc", "slug": "flying_disc" },
+ { "emoji": "🎳", "name": "bowling", "slug": "bowling" },
+ { "emoji": "🏏", "name": "cricket game", "slug": "cricket_game" },
+ { "emoji": "🏑", "name": "field hockey", "slug": "field_hockey" },
+ { "emoji": "🏒", "name": "ice hockey", "slug": "ice_hockey" },
+ { "emoji": "🥍", "name": "lacrosse", "slug": "lacrosse" },
+ { "emoji": "🏓", "name": "ping pong", "slug": "ping_pong" },
+ { "emoji": "🏸", "name": "badminton", "slug": "badminton" },
+ { "emoji": "🥊", "name": "boxing glove", "slug": "boxing_glove" },
{
"emoji": "🥋",
"name": "martial arts uniform",
"slug": "martial_arts_uniform"
},
- {
- "emoji": "🥅",
- "name": "goal net",
- "slug": "goal_net"
- },
- {
- "emoji": "⛳",
- "name": "flag in hole",
- "slug": "flag_in_hole"
- },
- {
- "emoji": "⛸️",
- "name": "ice skate",
- "slug": "ice_skate"
- },
- {
- "emoji": "🎣",
- "name": "fishing pole",
- "slug": "fishing_pole"
- },
- {
- "emoji": "🤿",
- "name": "diving mask",
- "slug": "diving_mask"
- },
- {
- "emoji": "🎽",
- "name": "running shirt",
- "slug": "running_shirt"
- },
- {
- "emoji": "🎿",
- "name": "skis",
- "slug": "skis"
- },
- {
- "emoji": "🛷",
- "name": "sled",
- "slug": "sled"
- },
- {
- "emoji": "🥌",
- "name": "curling stone",
- "slug": "curling_stone"
- },
- {
- "emoji": "🎯",
- "name": "bullseye",
- "slug": "bullseye"
- },
- {
- "emoji": "🪀",
- "name": "yo-yo",
- "slug": "yo_yo"
- },
- {
- "emoji": "🪁",
- "name": "kite",
- "slug": "kite"
- },
- {
- "emoji": "🔫",
- "name": "water pistol",
- "slug": "water_pistol"
- },
- {
- "emoji": "🎱",
- "name": "pool 8 ball",
- "slug": "pool_8_ball"
- },
- {
- "emoji": "🔮",
- "name": "crystal ball",
- "slug": "crystal_ball"
- },
- {
- "emoji": "🪄",
- "name": "magic wand",
- "slug": "magic_wand"
- },
- {
- "emoji": "🎮",
- "name": "video game",
- "slug": "video_game"
- },
- {
- "emoji": "🕹️",
- "name": "joystick",
- "slug": "joystick"
- },
- {
- "emoji": "🎰",
- "name": "slot machine",
- "slug": "slot_machine"
- },
- {
- "emoji": "🎲",
- "name": "game die",
- "slug": "game_die"
- },
- {
- "emoji": "🧩",
- "name": "puzzle piece",
- "slug": "puzzle_piece"
- },
- {
- "emoji": "🧸",
- "name": "teddy bear",
- "slug": "teddy_bear"
- },
- {
- "emoji": "🪅",
- "name": "piñata",
- "slug": "pinata"
- },
- {
- "emoji": "🪩",
- "name": "mirror ball",
- "slug": "mirror_ball"
- },
- {
- "emoji": "🪆",
- "name": "nesting dolls",
- "slug": "nesting_dolls"
- },
- {
- "emoji": "♠️",
- "name": "spade suit",
- "slug": "spade_suit"
- },
- {
- "emoji": "♥️",
- "name": "heart suit",
- "slug": "heart_suit"
- },
- {
- "emoji": "♦️",
- "name": "diamond suit",
- "slug": "diamond_suit"
- },
- {
- "emoji": "♣️",
- "name": "club suit",
- "slug": "club_suit"
- },
- {
- "emoji": "♟️",
- "name": "chess pawn",
- "slug": "chess_pawn"
- },
- {
- "emoji": "🃏",
- "name": "joker",
- "slug": "joker"
- },
+ { "emoji": "🥅", "name": "goal net", "slug": "goal_net" },
+ { "emoji": "⛳️", "name": "flag in hole", "slug": "flag_in_hole" },
+ { "emoji": "⛸️", "name": "ice skate", "slug": "ice_skate" },
+ { "emoji": "🎣", "name": "fishing pole", "slug": "fishing_pole" },
+ { "emoji": "🤿", "name": "diving mask", "slug": "diving_mask" },
+ { "emoji": "🎽", "name": "running shirt", "slug": "running_shirt" },
+ { "emoji": "🎿", "name": "skis", "slug": "skis" },
+ { "emoji": "🛷", "name": "sled", "slug": "sled" },
+ { "emoji": "🥌", "name": "curling stone", "slug": "curling_stone" },
+ { "emoji": "🎯", "name": "bullseye", "slug": "bullseye" },
+ { "emoji": "🪀", "name": "yo-yo", "slug": "yo_yo" },
+ { "emoji": "🪁", "name": "kite", "slug": "kite" },
+ { "emoji": "🔫", "name": "water pistol", "slug": "water_pistol" },
+ { "emoji": "🎱", "name": "pool 8 ball", "slug": "pool_8_ball" },
+ { "emoji": "🔮", "name": "crystal ball", "slug": "crystal_ball" },
+ { "emoji": "🪄", "name": "magic wand", "slug": "magic_wand" },
+ { "emoji": "🎮", "name": "video game", "slug": "video_game" },
+ { "emoji": "🕹️", "name": "joystick", "slug": "joystick" },
+ { "emoji": "🎰", "name": "slot machine", "slug": "slot_machine" },
+ { "emoji": "🎲", "name": "game die", "slug": "game_die" },
+ { "emoji": "🧩", "name": "puzzle piece", "slug": "puzzle_piece" },
+ { "emoji": "🧸", "name": "teddy bear", "slug": "teddy_bear" },
+ { "emoji": "🪅", "name": "piñata", "slug": "pinata" },
+ { "emoji": "🪩", "name": "mirror ball", "slug": "mirror_ball" },
+ { "emoji": "🪆", "name": "nesting dolls", "slug": "nesting_dolls" },
+ { "emoji": "♠️", "name": "spade suit", "slug": "spade_suit" },
+ { "emoji": "♥️", "name": "heart suit", "slug": "heart_suit" },
+ { "emoji": "♦️", "name": "diamond suit", "slug": "diamond_suit" },
+ { "emoji": "♣️", "name": "club suit", "slug": "club_suit" },
+ { "emoji": "♟️", "name": "chess pawn", "slug": "chess_pawn" },
+ { "emoji": "🃏", "name": "joker", "slug": "joker" },
{
"emoji": "🀄",
"name": "mahjong red dragon",
@@ -5498,277 +2710,73 @@
"name": "flower playing cards",
"slug": "flower_playing_cards"
},
- {
- "emoji": "🎭",
- "name": "performing arts",
- "slug": "performing_arts"
- },
- {
- "emoji": "🖼️",
- "name": "framed picture",
- "slug": "framed_picture"
- },
- {
- "emoji": "🎨",
- "name": "artist palette",
- "slug": "artist_palette"
- },
- {
- "emoji": "🧵",
- "name": "thread",
- "slug": "thread"
- },
- {
- "emoji": "🪡",
- "name": "sewing needle",
- "slug": "sewing_needle"
- },
- {
- "emoji": "🧶",
- "name": "yarn",
- "slug": "yarn"
- },
- {
- "emoji": "🪢",
- "name": "knot",
- "slug": "knot"
- }
+ { "emoji": "🎭", "name": "performing arts", "slug": "performing_arts" },
+ { "emoji": "🖼️", "name": "framed picture", "slug": "framed_picture" },
+ { "emoji": "🎨", "name": "artist palette", "slug": "artist_palette" },
+ { "emoji": "🧵", "name": "thread", "slug": "thread" },
+ { "emoji": "🪡", "name": "sewing needle", "slug": "sewing_needle" },
+ { "emoji": "🧶", "name": "yarn", "slug": "yarn" },
+ { "emoji": "🪢", "name": "knot", "slug": "knot" }
]
},
{
"name": "Objects",
"slug": "objects",
"emojis": [
- {
- "emoji": "👓",
- "name": "glasses",
- "slug": "glasses"
- },
- {
- "emoji": "🕶️",
- "name": "sunglasses",
- "slug": "sunglasses"
- },
- {
- "emoji": "🥽",
- "name": "goggles",
- "slug": "goggles"
- },
- {
- "emoji": "🥼",
- "name": "lab coat",
- "slug": "lab_coat"
- },
- {
- "emoji": "🦺",
- "name": "safety vest",
- "slug": "safety_vest"
- },
- {
- "emoji": "👔",
- "name": "necktie",
- "slug": "necktie"
- },
- {
- "emoji": "👕",
- "name": "t-shirt",
- "slug": "t_shirt"
- },
- {
- "emoji": "👖",
- "name": "jeans",
- "slug": "jeans"
- },
- {
- "emoji": "🧣",
- "name": "scarf",
- "slug": "scarf"
- },
- {
- "emoji": "🧤",
- "name": "gloves",
- "slug": "gloves"
- },
- {
- "emoji": "🧥",
- "name": "coat",
- "slug": "coat"
- },
- {
- "emoji": "🧦",
- "name": "socks",
- "slug": "socks"
- },
- {
- "emoji": "👗",
- "name": "dress",
- "slug": "dress"
- },
- {
- "emoji": "👘",
- "name": "kimono",
- "slug": "kimono"
- },
- {
- "emoji": "🥻",
- "name": "sari",
- "slug": "sari"
- },
+ { "emoji": "👓", "name": "glasses", "slug": "glasses" },
+ { "emoji": "🕶️", "name": "sunglasses", "slug": "sunglasses" },
+ { "emoji": "🥽", "name": "goggles", "slug": "goggles" },
+ { "emoji": "🥼", "name": "lab coat", "slug": "lab_coat" },
+ { "emoji": "🦺", "name": "safety vest", "slug": "safety_vest" },
+ { "emoji": "👔", "name": "necktie", "slug": "necktie" },
+ { "emoji": "👕", "name": "t-shirt", "slug": "t_shirt" },
+ { "emoji": "👖", "name": "jeans", "slug": "jeans" },
+ { "emoji": "🧣", "name": "scarf", "slug": "scarf" },
+ { "emoji": "🧤", "name": "gloves", "slug": "gloves" },
+ { "emoji": "🧥", "name": "coat", "slug": "coat" },
+ { "emoji": "🧦", "name": "socks", "slug": "socks" },
+ { "emoji": "👗", "name": "dress", "slug": "dress" },
+ { "emoji": "👘", "name": "kimono", "slug": "kimono" },
+ { "emoji": "🥻", "name": "sari", "slug": "sari" },
{
"emoji": "🩱",
"name": "one-piece swimsuit",
"slug": "one_piece_swimsuit"
},
- {
- "emoji": "🩲",
- "name": "briefs",
- "slug": "briefs"
- },
- {
- "emoji": "🩳",
- "name": "shorts",
- "slug": "shorts"
- },
- {
- "emoji": "👙",
- "name": "bikini",
- "slug": "bikini"
- },
- {
- "emoji": "👚",
- "name": "woman’s clothes",
- "slug": "woman_s_clothes"
- },
- {
- "emoji": "👛",
- "name": "purse",
- "slug": "purse"
- },
- {
- "emoji": "👜",
- "name": "handbag",
- "slug": "handbag"
- },
- {
- "emoji": "👝",
- "name": "clutch bag",
- "slug": "clutch_bag"
- },
- {
- "emoji": "🛍️",
- "name": "shopping bags",
- "slug": "shopping_bags"
- },
- {
- "emoji": "🎒",
- "name": "backpack",
- "slug": "backpack"
- },
- {
- "emoji": "🩴",
- "name": "thong sandal",
- "slug": "thong_sandal"
- },
- {
- "emoji": "👞",
- "name": "man’s shoe",
- "slug": "man_s_shoe"
- },
- {
- "emoji": "👟",
- "name": "running shoe",
- "slug": "running_shoe"
- },
- {
- "emoji": "🥾",
- "name": "hiking boot",
- "slug": "hiking_boot"
- },
- {
- "emoji": "🥿",
- "name": "flat shoe",
- "slug": "flat_shoe"
- },
- {
- "emoji": "👠",
- "name": "high-heeled shoe",
- "slug": "high_heeled_shoe"
- },
- {
- "emoji": "👡",
- "name": "woman’s sandal",
- "slug": "woman_s_sandal"
- },
- {
- "emoji": "🩰",
- "name": "ballet shoes",
- "slug": "ballet_shoes"
- },
- {
- "emoji": "👢",
- "name": "woman’s boot",
- "slug": "woman_s_boot"
- },
- {
- "emoji": "👑",
- "name": "crown",
- "slug": "crown"
- },
- {
- "emoji": "👒",
- "name": "woman’s hat",
- "slug": "woman_s_hat"
- },
- {
- "emoji": "🎩",
- "name": "top hat",
- "slug": "top_hat"
- },
- {
- "emoji": "🎓",
- "name": "graduation cap",
- "slug": "graduation_cap"
- },
- {
- "emoji": "🧢",
- "name": "billed cap",
- "slug": "billed_cap"
- },
- {
- "emoji": "🪖",
- "name": "military helmet",
- "slug": "military_helmet"
- },
+ { "emoji": "🩲", "name": "briefs", "slug": "briefs" },
+ { "emoji": "🩳", "name": "shorts", "slug": "shorts" },
+ { "emoji": "👙", "name": "bikini", "slug": "bikini" },
+ { "emoji": "👚", "name": "woman’s clothes", "slug": "woman_s_clothes" },
+ { "emoji": "👛", "name": "purse", "slug": "purse" },
+ { "emoji": "👜", "name": "handbag", "slug": "handbag" },
+ { "emoji": "👝", "name": "clutch bag", "slug": "clutch_bag" },
+ { "emoji": "🛍️", "name": "shopping bags", "slug": "shopping_bags" },
+ { "emoji": "🎒", "name": "backpack", "slug": "backpack" },
+ { "emoji": "🩴", "name": "thong sandal", "slug": "thong_sandal" },
+ { "emoji": "👞", "name": "man’s shoe", "slug": "man_s_shoe" },
+ { "emoji": "👟", "name": "running shoe", "slug": "running_shoe" },
+ { "emoji": "🥾", "name": "hiking boot", "slug": "hiking_boot" },
+ { "emoji": "🥿", "name": "flat shoe", "slug": "flat_shoe" },
+ { "emoji": "👠", "name": "high-heeled shoe", "slug": "high_heeled_shoe" },
+ { "emoji": "👡", "name": "woman’s sandal", "slug": "woman_s_sandal" },
+ { "emoji": "🩰", "name": "ballet shoes", "slug": "ballet_shoes" },
+ { "emoji": "👢", "name": "woman’s boot", "slug": "woman_s_boot" },
+ { "emoji": "👑", "name": "crown", "slug": "crown" },
+ { "emoji": "👒", "name": "woman’s hat", "slug": "woman_s_hat" },
+ { "emoji": "🎩", "name": "top hat", "slug": "top_hat" },
+ { "emoji": "🎓", "name": "graduation cap", "slug": "graduation_cap" },
+ { "emoji": "🧢", "name": "billed cap", "slug": "billed_cap" },
+ { "emoji": "🪖", "name": "military helmet", "slug": "military_helmet" },
{
"emoji": "⛑️",
"name": "rescue worker’s helmet",
"slug": "rescue_worker_s_helmet"
},
- {
- "emoji": "📿",
- "name": "prayer beads",
- "slug": "prayer_beads"
- },
- {
- "emoji": "💄",
- "name": "lipstick",
- "slug": "lipstick"
- },
- {
- "emoji": "💍",
- "name": "ring",
- "slug": "ring"
- },
- {
- "emoji": "💎",
- "name": "gem stone",
- "slug": "gem_stone"
- },
- {
- "emoji": "🔇",
- "name": "muted speaker",
- "slug": "muted_speaker"
- },
+ { "emoji": "📿", "name": "prayer beads", "slug": "prayer_beads" },
+ { "emoji": "💄", "name": "lipstick", "slug": "lipstick" },
+ { "emoji": "💍", "name": "ring", "slug": "ring" },
+ { "emoji": "💎", "name": "gem stone", "slug": "gem_stone" },
+ { "emoji": "🔇", "name": "muted speaker", "slug": "muted_speaker" },
{
"emoji": "🔈",
"name": "speaker low volume",
@@ -5784,266 +2792,74 @@
"name": "speaker high volume",
"slug": "speaker_high_volume"
},
- {
- "emoji": "📢",
- "name": "loudspeaker",
- "slug": "loudspeaker"
- },
- {
- "emoji": "📣",
- "name": "megaphone",
- "slug": "megaphone"
- },
- {
- "emoji": "📯",
- "name": "postal horn",
- "slug": "postal_horn"
- },
- {
- "emoji": "🔔",
- "name": "bell",
- "slug": "bell"
- },
- {
- "emoji": "🔕",
- "name": "bell with slash",
- "slug": "bell_with_slash"
- },
- {
- "emoji": "🎼",
- "name": "musical score",
- "slug": "musical_score"
- },
- {
- "emoji": "🎵",
- "name": "musical note",
- "slug": "musical_note"
- },
- {
- "emoji": "🎶",
- "name": "musical notes",
- "slug": "musical_notes"
- },
+ { "emoji": "📢", "name": "loudspeaker", "slug": "loudspeaker" },
+ { "emoji": "📣", "name": "megaphone", "slug": "megaphone" },
+ { "emoji": "📯", "name": "postal horn", "slug": "postal_horn" },
+ { "emoji": "🔔", "name": "bell", "slug": "bell" },
+ { "emoji": "🔕", "name": "bell with slash", "slug": "bell_with_slash" },
+ { "emoji": "🎼", "name": "musical score", "slug": "musical_score" },
+ { "emoji": "🎵", "name": "musical note", "slug": "musical_note" },
+ { "emoji": "🎶", "name": "musical notes", "slug": "musical_notes" },
{
"emoji": "🎙️",
"name": "studio microphone",
"slug": "studio_microphone"
},
- {
- "emoji": "🎚️",
- "name": "level slider",
- "slug": "level_slider"
- },
- {
- "emoji": "🎛️",
- "name": "control knobs",
- "slug": "control_knobs"
- },
- {
- "emoji": "🎤",
- "name": "microphone",
- "slug": "microphone"
- },
- {
- "emoji": "🎧",
- "name": "headphone",
- "slug": "headphone"
- },
- {
- "emoji": "📻",
- "name": "radio",
- "slug": "radio"
- },
- {
- "emoji": "🎷",
- "name": "saxophone",
- "slug": "saxophone"
- },
- {
- "emoji": "🪗",
- "name": "accordion",
- "slug": "accordion"
- },
- {
- "emoji": "🎸",
- "name": "guitar",
- "slug": "guitar"
- },
- {
- "emoji": "🎹",
- "name": "musical keyboard",
- "slug": "musical_keyboard"
- },
- {
- "emoji": "🎺",
- "name": "trumpet",
- "slug": "trumpet"
- },
- {
- "emoji": "🎻",
- "name": "violin",
- "slug": "violin"
- },
- {
- "emoji": "🪕",
- "name": "banjo",
- "slug": "banjo"
- },
- {
- "emoji": "🥁",
- "name": "drum",
- "slug": "drum"
- },
- {
- "emoji": "🪘",
- "name": "long drum",
- "slug": "long_drum"
- },
- {
- "emoji": "📱",
- "name": "mobile phone",
- "slug": "mobile_phone"
- },
+ { "emoji": "🎚️", "name": "level slider", "slug": "level_slider" },
+ { "emoji": "🎛️", "name": "control knobs", "slug": "control_knobs" },
+ { "emoji": "🎤", "name": "microphone", "slug": "microphone" },
+ { "emoji": "🎧", "name": "headphone", "slug": "headphone" },
+ { "emoji": "📻", "name": "radio", "slug": "radio" },
+ { "emoji": "🎷", "name": "saxophone", "slug": "saxophone" },
+ { "emoji": "🪗", "name": "accordion", "slug": "accordion" },
+ { "emoji": "🎸", "name": "guitar", "slug": "guitar" },
+ { "emoji": "🎹", "name": "musical keyboard", "slug": "musical_keyboard" },
+ { "emoji": "🎺", "name": "trumpet", "slug": "trumpet" },
+ { "emoji": "🎻", "name": "violin", "slug": "violin" },
+ { "emoji": "🪕", "name": "banjo", "slug": "banjo" },
+ { "emoji": "🥁", "name": "drum", "slug": "drum" },
+ { "emoji": "🪘", "name": "long drum", "slug": "long_drum" },
+ { "emoji": "📱", "name": "mobile phone", "slug": "mobile_phone" },
{
"emoji": "📲",
"name": "mobile phone with arrow",
"slug": "mobile_phone_with_arrow"
},
- {
- "emoji": "☎️",
- "name": "telephone",
- "slug": "telephone"
- },
+ { "emoji": "☎️", "name": "telephone", "slug": "telephone" },
{
"emoji": "📞",
"name": "telephone receiver",
"slug": "telephone_receiver"
},
- {
- "emoji": "📟",
- "name": "pager",
- "slug": "pager"
- },
- {
- "emoji": "📠",
- "name": "fax machine",
- "slug": "fax_machine"
- },
- {
- "emoji": "🔋",
- "name": "battery",
- "slug": "battery"
- },
- {
- "emoji": "🪫",
- "name": "low battery",
- "slug": "low_battery"
- },
- {
- "emoji": "🔌",
- "name": "electric plug",
- "slug": "electric_plug"
- },
- {
- "emoji": "💻",
- "name": "laptop",
- "slug": "laptop"
- },
- {
- "emoji": "🖥️",
- "name": "desktop computer",
- "slug": "desktop_computer"
- },
- {
- "emoji": "🖨️",
- "name": "printer",
- "slug": "printer"
- },
- {
- "emoji": "⌨️",
- "name": "keyboard",
- "slug": "keyboard"
- },
- {
- "emoji": "🖱️",
- "name": "computer mouse",
- "slug": "computer_mouse"
- },
- {
- "emoji": "🖲️",
- "name": "trackball",
- "slug": "trackball"
- },
- {
- "emoji": "💽",
- "name": "computer disk",
- "slug": "computer_disk"
- },
- {
- "emoji": "💾",
- "name": "floppy disk",
- "slug": "floppy_disk"
- },
- {
- "emoji": "💿",
- "name": "optical disk",
- "slug": "optical_disk"
- },
- {
- "emoji": "📀",
- "name": "dvd",
- "slug": "dvd"
- },
- {
- "emoji": "🧮",
- "name": "abacus",
- "slug": "abacus"
- },
- {
- "emoji": "🎥",
- "name": "movie camera",
- "slug": "movie_camera"
- },
- {
- "emoji": "🎞️",
- "name": "film frames",
- "slug": "film_frames"
- },
- {
- "emoji": "📽️",
- "name": "film projector",
- "slug": "film_projector"
- },
- {
- "emoji": "🎬",
- "name": "clapper board",
- "slug": "clapper_board"
- },
- {
- "emoji": "📺",
- "name": "television",
- "slug": "television"
- },
- {
- "emoji": "📷",
- "name": "camera",
- "slug": "camera"
- },
+ { "emoji": "📟", "name": "pager", "slug": "pager" },
+ { "emoji": "📠", "name": "fax machine", "slug": "fax_machine" },
+ { "emoji": "🔋", "name": "battery", "slug": "battery" },
+ { "emoji": "🪫", "name": "low battery", "slug": "low_battery" },
+ { "emoji": "🔌", "name": "electric plug", "slug": "electric_plug" },
+ { "emoji": "💻", "name": "laptop", "slug": "laptop" },
+ { "emoji": "🖥️", "name": "desktop computer", "slug": "desktop_computer" },
+ { "emoji": "🖨️", "name": "printer", "slug": "printer" },
+ { "emoji": "⌨️", "name": "keyboard", "slug": "keyboard" },
+ { "emoji": "🖱️", "name": "computer mouse", "slug": "computer_mouse" },
+ { "emoji": "🖲️", "name": "trackball", "slug": "trackball" },
+ { "emoji": "💽", "name": "computer disk", "slug": "computer_disk" },
+ { "emoji": "💾", "name": "floppy disk", "slug": "floppy_disk" },
+ { "emoji": "💿", "name": "optical disk", "slug": "optical_disk" },
+ { "emoji": "📀", "name": "dvd", "slug": "dvd" },
+ { "emoji": "🧮", "name": "abacus", "slug": "abacus" },
+ { "emoji": "🎥", "name": "movie camera", "slug": "movie_camera" },
+ { "emoji": "🎞️", "name": "film frames", "slug": "film_frames" },
+ { "emoji": "📽️", "name": "film projector", "slug": "film_projector" },
+ { "emoji": "🎬", "name": "clapper board", "slug": "clapper_board" },
+ { "emoji": "📺", "name": "television", "slug": "television" },
+ { "emoji": "📷", "name": "camera", "slug": "camera" },
{
"emoji": "📸",
"name": "camera with flash",
"slug": "camera_with_flash"
},
- {
- "emoji": "📹",
- "name": "video camera",
- "slug": "video_camera"
- },
- {
- "emoji": "📼",
- "name": "videocassette",
- "slug": "videocassette"
- },
+ { "emoji": "📹", "name": "video camera", "slug": "video_camera" },
+ { "emoji": "📼", "name": "videocassette", "slug": "videocassette" },
{
"emoji": "🔍",
"name": "magnifying glass tilted left",
@@ -6054,176 +2870,56 @@
"name": "magnifying glass tilted right",
"slug": "magnifying_glass_tilted_right"
},
- {
- "emoji": "🕯️",
- "name": "candle",
- "slug": "candle"
- },
- {
- "emoji": "💡",
- "name": "light bulb",
- "slug": "light_bulb"
- },
- {
- "emoji": "🔦",
- "name": "flashlight",
- "slug": "flashlight"
- },
+ { "emoji": "🕯️", "name": "candle", "slug": "candle" },
+ { "emoji": "💡", "name": "light bulb", "slug": "light_bulb" },
+ { "emoji": "🔦", "name": "flashlight", "slug": "flashlight" },
{
"emoji": "🏮",
"name": "red paper lantern",
"slug": "red_paper_lantern"
},
- {
- "emoji": "🪔",
- "name": "diya lamp",
- "slug": "diya_lamp"
- },
+ { "emoji": "🪔", "name": "diya lamp", "slug": "diya_lamp" },
{
"emoji": "📔",
"name": "notebook with decorative cover",
"slug": "notebook_with_decorative_cover"
},
- {
- "emoji": "📕",
- "name": "closed book",
- "slug": "closed_book"
- },
- {
- "emoji": "📖",
- "name": "open book",
- "slug": "open_book"
- },
- {
- "emoji": "📗",
- "name": "green book",
- "slug": "green_book"
- },
- {
- "emoji": "📘",
- "name": "blue book",
- "slug": "blue_book"
- },
- {
- "emoji": "📙",
- "name": "orange book",
- "slug": "orange_book"
- },
- {
- "emoji": "📚",
- "name": "books",
- "slug": "books"
- },
- {
- "emoji": "📓",
- "name": "notebook",
- "slug": "notebook"
- },
- {
- "emoji": "📒",
- "name": "ledger",
- "slug": "ledger"
- },
- {
- "emoji": "📃",
- "name": "page with curl",
- "slug": "page_with_curl"
- },
- {
- "emoji": "📜",
- "name": "scroll",
- "slug": "scroll"
- },
- {
- "emoji": "📄",
- "name": "page facing up",
- "slug": "page_facing_up"
- },
- {
- "emoji": "📰",
- "name": "newspaper",
- "slug": "newspaper"
- },
+ { "emoji": "📕", "name": "closed book", "slug": "closed_book" },
+ { "emoji": "📖", "name": "open book", "slug": "open_book" },
+ { "emoji": "📗", "name": "green book", "slug": "green_book" },
+ { "emoji": "📘", "name": "blue book", "slug": "blue_book" },
+ { "emoji": "📙", "name": "orange book", "slug": "orange_book" },
+ { "emoji": "📚", "name": "books", "slug": "books" },
+ { "emoji": "📓", "name": "notebook", "slug": "notebook" },
+ { "emoji": "📒", "name": "ledger", "slug": "ledger" },
+ { "emoji": "📃", "name": "page with curl", "slug": "page_with_curl" },
+ { "emoji": "📜", "name": "scroll", "slug": "scroll" },
+ { "emoji": "📄", "name": "page facing up", "slug": "page_facing_up" },
+ { "emoji": "📰", "name": "newspaper", "slug": "newspaper" },
{
"emoji": "🗞️",
"name": "rolled-up newspaper",
"slug": "rolled_up_newspaper"
},
- {
- "emoji": "📑",
- "name": "bookmark tabs",
- "slug": "bookmark_tabs"
- },
- {
- "emoji": "🔖",
- "name": "bookmark",
- "slug": "bookmark"
- },
- {
- "emoji": "🏷️",
- "name": "label",
- "slug": "label"
- },
- {
- "emoji": "💰",
- "name": "money bag",
- "slug": "money_bag"
- },
- {
- "emoji": "🪙",
- "name": "coin",
- "slug": "coin"
- },
- {
- "emoji": "💴",
- "name": "yen banknote",
- "slug": "yen_banknote"
- },
- {
- "emoji": "💵",
- "name": "dollar banknote",
- "slug": "dollar_banknote"
- },
- {
- "emoji": "💶",
- "name": "euro banknote",
- "slug": "euro_banknote"
- },
- {
- "emoji": "💷",
- "name": "pound banknote",
- "slug": "pound_banknote"
- },
- {
- "emoji": "💸",
- "name": "money with wings",
- "slug": "money_with_wings"
- },
- {
- "emoji": "💳",
- "name": "credit card",
- "slug": "credit_card"
- },
- {
- "emoji": "🧾",
- "name": "receipt",
- "slug": "receipt"
- },
+ { "emoji": "📑", "name": "bookmark tabs", "slug": "bookmark_tabs" },
+ { "emoji": "🔖", "name": "bookmark", "slug": "bookmark" },
+ { "emoji": "🏷️", "name": "label", "slug": "label" },
+ { "emoji": "💰", "name": "money bag", "slug": "money_bag" },
+ { "emoji": "🪙", "name": "coin", "slug": "coin" },
+ { "emoji": "💴", "name": "yen banknote", "slug": "yen_banknote" },
+ { "emoji": "💵", "name": "dollar banknote", "slug": "dollar_banknote" },
+ { "emoji": "💶", "name": "euro banknote", "slug": "euro_banknote" },
+ { "emoji": "💷", "name": "pound banknote", "slug": "pound_banknote" },
+ { "emoji": "💸", "name": "money with wings", "slug": "money_with_wings" },
+ { "emoji": "💳", "name": "credit card", "slug": "credit_card" },
+ { "emoji": "🧾", "name": "receipt", "slug": "receipt" },
{
"emoji": "💹",
"name": "chart increasing with yen",
"slug": "chart_increasing_with_yen"
},
- {
- "emoji": "✉️",
- "name": "envelope",
- "slug": "envelope_email_letter"
- },
- {
- "emoji": "📧",
- "name": "e-mail",
- "slug": "e_mail_email"
- },
+ { "emoji": "✉️", "name": "envelope", "slug": "envelope_email_letter" },
+ { "emoji": "📧", "name": "e-mail", "slug": "e_mail_email" },
{
"emoji": "📨",
"name": "incoming envelope",
@@ -6234,21 +2930,9 @@
"name": "envelope with arrow",
"slug": "envelope_with_arrow_email_letter"
},
- {
- "emoji": "📤",
- "name": "outbox tray",
- "slug": "outbox_tray_sent"
- },
- {
- "emoji": "📥",
- "name": "inbox tray",
- "slug": "inbox_tray_receive"
- },
- {
- "emoji": "📦",
- "name": "package",
- "slug": "package_parcel_box"
- },
+ { "emoji": "📤", "name": "outbox tray", "slug": "outbox_tray_sent" },
+ { "emoji": "📥", "name": "inbox tray", "slug": "inbox_tray_receive" },
+ { "emoji": "📦", "name": "package", "slug": "package_parcel_box" },
{
"emoji": "📫",
"name": "closed mailbox with raised flag",
@@ -6269,211 +2953,63 @@
"name": "open mailbox with lowered flag",
"slug": "open_mailbox_with_lowered_flag"
},
- {
- "emoji": "📮",
- "name": "postbox",
- "slug": "postbox"
- },
+ { "emoji": "📮", "name": "postbox", "slug": "postbox" },
{
"emoji": "🗳️",
"name": "ballot box with ballot",
"slug": "ballot_box_with_ballot"
},
- {
- "emoji": "✏️",
- "name": "pencil",
- "slug": "pencil"
- },
- {
- "emoji": "✒️",
- "name": "black nib",
- "slug": "black_nib"
- },
- {
- "emoji": "🖋️",
- "name": "fountain pen",
- "slug": "fountain_pen"
- },
- {
- "emoji": "🖊️",
- "name": "pen",
- "slug": "pen"
- },
- {
- "emoji": "🖌️",
- "name": "paintbrush",
- "slug": "paintbrush"
- },
- {
- "emoji": "🖍️",
- "name": "crayon",
- "slug": "crayon"
- },
- {
- "emoji": "📝",
- "name": "memo",
- "slug": "memo"
- },
- {
- "emoji": "💼",
- "name": "briefcase",
- "slug": "briefcase"
- },
- {
- "emoji": "📁",
- "name": "file folder",
- "slug": "file_folder"
- },
- {
- "emoji": "📂",
- "name": "open file folder",
- "slug": "open_file_folder"
- },
+ { "emoji": "✏️", "name": "pencil", "slug": "pencil" },
+ { "emoji": "✒️", "name": "black nib", "slug": "black_nib" },
+ { "emoji": "🖋️", "name": "fountain pen", "slug": "fountain_pen" },
+ { "emoji": "🖊️", "name": "pen", "slug": "pen" },
+ { "emoji": "🖌️", "name": "paintbrush", "slug": "paintbrush" },
+ { "emoji": "🖍️", "name": "crayon", "slug": "crayon" },
+ { "emoji": "📝", "name": "memo", "slug": "memo" },
+ { "emoji": "💼", "name": "briefcase", "slug": "briefcase" },
+ { "emoji": "📁", "name": "file folder", "slug": "file_folder" },
+ { "emoji": "📂", "name": "open file folder", "slug": "open_file_folder" },
{
"emoji": "🗂️",
"name": "card index dividers",
"slug": "card_index_dividers"
},
- {
- "emoji": "📅",
- "name": "calendar",
- "slug": "calendar"
- },
+ { "emoji": "📅", "name": "calendar", "slug": "calendar" },
{
"emoji": "📆",
"name": "tear-off calendar",
"slug": "tear_off_calendar"
},
- {
- "emoji": "🗒️",
- "name": "spiral notepad",
- "slug": "spiral_notepad"
- },
- {
- "emoji": "🗓️",
- "name": "spiral calendar",
- "slug": "spiral_calendar"
- },
- {
- "emoji": "📇",
- "name": "card index",
- "slug": "card_index"
- },
- {
- "emoji": "📈",
- "name": "chart increasing",
- "slug": "chart_increasing"
- },
- {
- "emoji": "📉",
- "name": "chart decreasing",
- "slug": "chart_decreasing"
- },
- {
- "emoji": "📊",
- "name": "bar chart",
- "slug": "bar_chart"
- },
- {
- "emoji": "📋",
- "name": "clipboard",
- "slug": "clipboard"
- },
- {
- "emoji": "📌",
- "name": "pushpin",
- "slug": "pushpin"
- },
- {
- "emoji": "📍",
- "name": "round pushpin",
- "slug": "round_pushpin"
- },
- {
- "emoji": "📎",
- "name": "paperclip",
- "slug": "paperclip"
- },
+ { "emoji": "🗒️", "name": "spiral notepad", "slug": "spiral_notepad" },
+ { "emoji": "🗓️", "name": "spiral calendar", "slug": "spiral_calendar" },
+ { "emoji": "📇", "name": "card index", "slug": "card_index" },
+ { "emoji": "📈", "name": "chart increasing", "slug": "chart_increasing" },
+ { "emoji": "📉", "name": "chart decreasing", "slug": "chart_decreasing" },
+ { "emoji": "📊", "name": "bar chart", "slug": "bar_chart" },
+ { "emoji": "📋", "name": "clipboard", "slug": "clipboard" },
+ { "emoji": "📌", "name": "pushpin", "slug": "pushpin" },
+ { "emoji": "📍", "name": "round pushpin", "slug": "round_pushpin" },
+ { "emoji": "📎", "name": "paperclip", "slug": "paperclip" },
{
"emoji": "🖇️",
"name": "linked paperclips",
"slug": "linked_paperclips"
},
- {
- "emoji": "📏",
- "name": "straight ruler",
- "slug": "straight_ruler"
- },
- {
- "emoji": "📐",
- "name": "triangular ruler",
- "slug": "triangular_ruler"
- },
- {
- "emoji": "✂️",
- "name": "scissors",
- "slug": "scissors"
- },
- {
- "emoji": "🗃️",
- "name": "card file box",
- "slug": "card_file_box"
- },
- {
- "emoji": "🗄️",
- "name": "file cabinet",
- "slug": "file_cabinet"
- },
- {
- "emoji": "🗑️",
- "name": "wastebasket",
- "slug": "wastebasket"
- },
- {
- "emoji": "🔒",
- "name": "locked",
- "slug": "locked"
- },
- {
- "emoji": "🔓",
- "name": "unlocked",
- "slug": "unlocked"
- },
- {
- "emoji": "🔏",
- "name": "locked with pen",
- "slug": "locked_with_pen"
- },
- {
- "emoji": "🔐",
- "name": "locked with key",
- "slug": "locked_with_key"
- },
- {
- "emoji": "🔑",
- "name": "key",
- "slug": "key"
- },
- {
- "emoji": "🗝️",
- "name": "old key",
- "slug": "old_key"
- },
- {
- "emoji": "🔨",
- "name": "hammer",
- "slug": "hammer_tool"
- },
- {
- "emoji": "🪓",
- "name": "axe",
- "slug": "axe"
- },
- {
- "emoji": "⛏️",
- "name": "pick",
- "slug": "pick_tool"
- },
+ { "emoji": "📏", "name": "straight ruler", "slug": "straight_ruler" },
+ { "emoji": "📐", "name": "triangular ruler", "slug": "triangular_ruler" },
+ { "emoji": "✂️", "name": "scissors", "slug": "scissors" },
+ { "emoji": "🗃️", "name": "card file box", "slug": "card_file_box" },
+ { "emoji": "🗄️", "name": "file cabinet", "slug": "file_cabinet" },
+ { "emoji": "🗑️", "name": "wastebasket", "slug": "wastebasket" },
+ { "emoji": "🔒", "name": "locked", "slug": "locked" },
+ { "emoji": "🔓", "name": "unlocked", "slug": "unlocked" },
+ { "emoji": "🔏", "name": "locked with pen", "slug": "locked_with_pen" },
+ { "emoji": "🔐", "name": "locked with key", "slug": "locked_with_key" },
+ { "emoji": "🔑", "name": "key", "slug": "key" },
+ { "emoji": "🗝️", "name": "old key", "slug": "old_key" },
+ { "emoji": "🔨", "name": "hammer", "slug": "hammer_tool" },
+ { "emoji": "🪓", "name": "axe", "slug": "axe" },
+ { "emoji": "⛏️", "name": "pick", "slug": "pick_tool" },
{
"emoji": "⚒️",
"name": "hammer and pick",
@@ -6484,341 +3020,89 @@
"name": "hammer and wrench",
"slug": "hammer_and_wrench_tool"
},
- {
- "emoji": "🗡️",
- "name": "dagger",
- "slug": "dagger_knife_weapon"
- },
+ { "emoji": "🗡️", "name": "dagger", "slug": "dagger_knife_weapon" },
{
"emoji": "⚔️",
"name": "crossed swords",
"slug": "crossed_swords_weapon"
},
- {
- "emoji": "💣",
- "name": "bomb",
- "slug": "bomb_comic"
- },
- {
- "emoji": "🪃",
- "name": "boomerang",
- "slug": "boomerang"
- },
- {
- "emoji": "🏹",
- "name": "bow and arrow",
- "slug": "bow_and_arrow"
- },
- {
- "emoji": "🛡️",
- "name": "shield",
- "slug": "shield"
- },
- {
- "emoji": "🪚",
- "name": "carpentry saw",
- "slug": "carpentry_saw"
- },
- {
- "emoji": "🔧",
- "name": "wrench",
- "slug": "wrench_tool"
- },
- {
- "emoji": "🪛",
- "name": "screwdriver",
- "slug": "screwdriver_tool"
- },
- {
- "emoji": "🔩",
- "name": "nut and bolt",
- "slug": "nut_and_bolt_tool"
- },
- {
- "emoji": "⚙️",
- "name": "gear",
- "slug": "gear_tool"
- },
- {
- "emoji": "🗜️",
- "name": "clamp",
- "slug": "clamp_tool"
- },
+ { "emoji": "💣", "name": "bomb", "slug": "bomb_comic" },
+ { "emoji": "🪃", "name": "boomerang", "slug": "boomerang" },
+ { "emoji": "🏹", "name": "bow and arrow", "slug": "bow_and_arrow" },
+ { "emoji": "🛡️", "name": "shield", "slug": "shield" },
+ { "emoji": "🪚", "name": "carpentry saw", "slug": "carpentry_saw" },
+ { "emoji": "🔧", "name": "wrench", "slug": "wrench_tool" },
+ { "emoji": "🪛", "name": "screwdriver", "slug": "screwdriver_tool" },
+ { "emoji": "🔩", "name": "nut and bolt", "slug": "nut_and_bolt_tool" },
+ { "emoji": "⚙️", "name": "gear", "slug": "gear_tool" },
+ { "emoji": "🗜️", "name": "clamp", "slug": "clamp_tool" },
{
"emoji": "⚖️",
"name": "balance scale",
"slug": "balance_scale_justice"
},
- {
- "emoji": "🦯",
- "name": "white cane",
- "slug": "white_cane"
- },
- {
- "emoji": "🔗",
- "name": "link",
- "slug": "link"
- },
- {
- "emoji": "⛓️",
- "name": "chains",
- "slug": "chains"
- },
- {
- "emoji": "🪝",
- "name": "hook",
- "slug": "hook"
- },
- {
- "emoji": "🧰",
- "name": "toolbox",
- "slug": "toolbox"
- },
- {
- "emoji": "🧲",
- "name": "magnet",
- "slug": "magnet"
- },
- {
- "emoji": "🪜",
- "name": "ladder",
- "slug": "ladder"
- },
- {
- "emoji": "⚗️",
- "name": "alembic",
- "slug": "alembic"
- },
- {
- "emoji": "🧪",
- "name": "test tube",
- "slug": "test_tube"
- },
- {
- "emoji": "🧫",
- "name": "petri dish",
- "slug": "petri_dish"
- },
- {
- "emoji": "🧬",
- "name": "dna",
- "slug": "dna"
- },
- {
- "emoji": "🔬",
- "name": "microscope",
- "slug": "microscope"
- },
- {
- "emoji": "🔭",
- "name": "telescope",
- "slug": "telescope"
- },
+ { "emoji": "🦯", "name": "white cane", "slug": "white_cane" },
+ { "emoji": "🔗", "name": "link", "slug": "link" },
+ { "emoji": "⛓️", "name": "chains", "slug": "chains" },
+ { "emoji": "🪝", "name": "hook", "slug": "hook" },
+ { "emoji": "🧰", "name": "toolbox", "slug": "toolbox" },
+ { "emoji": "🧲", "name": "magnet", "slug": "magnet" },
+ { "emoji": "🪜", "name": "ladder", "slug": "ladder" },
+ { "emoji": "⚗️", "name": "alembic", "slug": "alembic" },
+ { "emoji": "🧪", "name": "test tube", "slug": "test_tube" },
+ { "emoji": "🧫", "name": "petri dish", "slug": "petri_dish" },
+ { "emoji": "🧬", "name": "dna", "slug": "dna" },
+ { "emoji": "🔬", "name": "microscope", "slug": "microscope" },
+ { "emoji": "🔭", "name": "telescope", "slug": "telescope" },
{
"emoji": "📡",
"name": "satellite antenna",
"slug": "satellite_antenna"
},
- {
- "emoji": "💉",
- "name": "syringe",
- "slug": "syringe"
- },
- {
- "emoji": "🩸",
- "name": "drop of blood",
- "slug": "drop_of_blood"
- },
- {
- "emoji": "💊",
- "name": "pill",
- "slug": "pill_medical_medicine_sick"
- },
- {
- "emoji": "🩹",
- "name": "adhesive bandage",
- "slug": "adhesive_bandage"
- },
- {
- "emoji": "🩼",
- "name": "crutch",
- "slug": "crutch"
- },
- {
- "emoji": "🩺",
- "name": "stethoscope",
- "slug": "stethoscope"
- },
- {
- "emoji": "🩻",
- "name": "x-ray",
- "slug": "x_ray"
- },
- {
- "emoji": "🚪",
- "name": "door",
- "slug": "door"
- },
- {
- "emoji": "🛗",
- "name": "elevator",
- "slug": "elevator"
- },
- {
- "emoji": "🪞",
- "name": "mirror",
- "slug": "mirror"
- },
- {
- "emoji": "🪟",
- "name": "window",
- "slug": "window"
- },
- {
- "emoji": "🛏️",
- "name": "bed",
- "slug": "bed"
- },
- {
- "emoji": "🛋️",
- "name": "couch and lamp",
- "slug": "couch_and_lamp"
- },
- {
- "emoji": "🪑",
- "name": "chair",
- "slug": "chair"
- },
- {
- "emoji": "🚽",
- "name": "toilet",
- "slug": "toilet"
- },
- {
- "emoji": "🪠",
- "name": "plunger",
- "slug": "plunger"
- },
- {
- "emoji": "🚿",
- "name": "shower",
- "slug": "shower"
- },
- {
- "emoji": "🛁",
- "name": "bathtub",
- "slug": "bathtub"
- },
- {
- "emoji": "🪤",
- "name": "mouse trap",
- "slug": "mouse_trap"
- },
- {
- "emoji": "🪒",
- "name": "razor",
- "slug": "razor"
- },
- {
- "emoji": "🧴",
- "name": "lotion bottle",
- "slug": "lotion_bottle"
- },
- {
- "emoji": "🧷",
- "name": "safety pin",
- "slug": "safety_pin"
- },
- {
- "emoji": "🧹",
- "name": "broom",
- "slug": "broom"
- },
- {
- "emoji": "🧺",
- "name": "basket",
- "slug": "basket"
- },
- {
- "emoji": "🧻",
- "name": "roll of paper",
- "slug": "roll_of_paper"
- },
- {
- "emoji": "🪣",
- "name": "bucket",
- "slug": "bucket"
- },
- {
- "emoji": "🧼",
- "name": "soap",
- "slug": "soap"
- },
- {
- "emoji": "🫧",
- "name": "bubbles",
- "slug": "bubbles"
- },
- {
- "emoji": "🪥",
- "name": "toothbrush",
- "slug": "toothbrush"
- },
- {
- "emoji": "🧽",
- "name": "sponge",
- "slug": "sponge"
- },
+ { "emoji": "💉", "name": "syringe", "slug": "syringe" },
+ { "emoji": "🩸", "name": "drop of blood", "slug": "drop_of_blood" },
+ { "emoji": "💊", "name": "pill", "slug": "pill_medical_medicine_sick" },
+ { "emoji": "🩹", "name": "adhesive bandage", "slug": "adhesive_bandage" },
+ { "emoji": "🩼", "name": "crutch", "slug": "crutch" },
+ { "emoji": "🩺", "name": "stethoscope", "slug": "stethoscope" },
+ { "emoji": "🩻", "name": "x-ray", "slug": "x_ray" },
+ { "emoji": "🚪", "name": "door", "slug": "door" },
+ { "emoji": "🛗", "name": "elevator", "slug": "elevator" },
+ { "emoji": "🪞", "name": "mirror", "slug": "mirror" },
+ { "emoji": "🪟", "name": "window", "slug": "window" },
+ { "emoji": "🛏️", "name": "bed", "slug": "bed" },
+ { "emoji": "🛋️", "name": "couch and lamp", "slug": "couch_and_lamp" },
+ { "emoji": "🪑", "name": "chair", "slug": "chair" },
+ { "emoji": "🚽", "name": "toilet", "slug": "toilet" },
+ { "emoji": "🪠", "name": "plunger", "slug": "plunger" },
+ { "emoji": "🚿", "name": "shower", "slug": "shower" },
+ { "emoji": "🛁", "name": "bathtub", "slug": "bathtub" },
+ { "emoji": "🪤", "name": "mouse trap", "slug": "mouse_trap" },
+ { "emoji": "🪒", "name": "razor", "slug": "razor" },
+ { "emoji": "🧴", "name": "lotion bottle", "slug": "lotion_bottle" },
+ { "emoji": "🧷", "name": "safety pin", "slug": "safety_pin" },
+ { "emoji": "🧹", "name": "broom", "slug": "broom" },
+ { "emoji": "🧺", "name": "basket", "slug": "basket" },
+ { "emoji": "🧻", "name": "roll of paper", "slug": "roll_of_paper" },
+ { "emoji": "🪣", "name": "bucket", "slug": "bucket" },
+ { "emoji": "🧼", "name": "soap", "slug": "soap" },
+ { "emoji": "🫧", "name": "bubbles", "slug": "bubbles" },
+ { "emoji": "🪥", "name": "toothbrush", "slug": "toothbrush" },
+ { "emoji": "🧽", "name": "sponge", "slug": "sponge" },
{
"emoji": "🧯",
"name": "fire extinguisher",
"slug": "fire_extinguisher"
},
- {
- "emoji": "🛒",
- "name": "shopping cart",
- "slug": "shopping_cart"
- },
- {
- "emoji": "🚬",
- "name": "cigarette",
- "slug": "cigarette"
- },
- {
- "emoji": "⚰️",
- "name": "coffin",
- "slug": "coffin"
- },
- {
- "emoji": "🪦",
- "name": "headstone",
- "slug": "headstone"
- },
- {
- "emoji": "⚱️",
- "name": "funeral urn",
- "slug": "funeral_urn"
- },
- {
- "emoji": "🧿",
- "name": "nazar amulet",
- "slug": "nazar_amulet"
- },
- {
- "emoji": "🪬",
- "name": "hamsa",
- "slug": "hamsa"
- },
- {
- "emoji": "🗿",
- "name": "moai",
- "slug": "moai"
- },
- {
- "emoji": "🪧",
- "name": "placard",
- "slug": "placard"
- },
+ { "emoji": "🛒", "name": "shopping cart", "slug": "shopping_cart" },
+ { "emoji": "🚬", "name": "cigarette", "slug": "cigarette" },
+ { "emoji": "⚰️", "name": "coffin", "slug": "coffin" },
+ { "emoji": "🪦", "name": "headstone", "slug": "headstone" },
+ { "emoji": "⚱️", "name": "funeral urn", "slug": "funeral_urn" },
+ { "emoji": "🧿", "name": "nazar amulet", "slug": "nazar_amulet" },
+ { "emoji": "🪬", "name": "hamsa", "slug": "hamsa" },
+ { "emoji": "🗿", "name": "moai", "slug": "moai" },
+ { "emoji": "🪧", "name": "placard", "slug": "placard" },
{
"emoji": "🪪",
"name": "identification card",
@@ -6840,176 +3124,60 @@
"name": "litter in bin sign",
"slug": "litter_in_bin_sign"
},
+ { "emoji": "🚰", "name": "potable water", "slug": "potable_water" },
{
- "emoji": "🚰",
- "name": "potable water",
- "slug": "potable_water"
- },
- {
- "emoji": "♿",
+ "emoji": "♿️",
"name": "wheelchair symbol",
"slug": "wheelchair_symbol"
},
- {
- "emoji": "🚹",
- "name": "men’s room",
- "slug": "men_s_room_bathroom"
- },
+ { "emoji": "🚹", "name": "men’s room", "slug": "men_s_room_bathroom" },
{
"emoji": "🚺",
"name": "women’s room",
"slug": "women_s_room_bathroom"
},
- {
- "emoji": "🚻",
- "name": "restroom",
- "slug": "restroom"
- },
- {
- "emoji": "🚼",
- "name": "baby symbol",
- "slug": "baby_symbol"
- },
- {
- "emoji": "🚾",
- "name": "water closet",
- "slug": "water_closet"
- },
- {
- "emoji": "🛂",
- "name": "passport control",
- "slug": "passport_control"
- },
- {
- "emoji": "🛃",
- "name": "customs",
- "slug": "customs"
- },
- {
- "emoji": "🛄",
- "name": "baggage claim",
- "slug": "baggage_claim"
- },
- {
- "emoji": "🛅",
- "name": "left luggage",
- "slug": "left_luggage"
- },
- {
- "emoji": "⚠️",
- "name": "warning",
- "slug": "warning"
- },
+ { "emoji": "🚻", "name": "restroom", "slug": "restroom" },
+ { "emoji": "🚼", "name": "baby symbol", "slug": "baby_symbol" },
+ { "emoji": "🚾", "name": "water closet", "slug": "water_closet" },
+ { "emoji": "🛂", "name": "passport control", "slug": "passport_control" },
+ { "emoji": "🛃", "name": "customs", "slug": "customs" },
+ { "emoji": "🛄", "name": "baggage claim", "slug": "baggage_claim" },
+ { "emoji": "🛅", "name": "left luggage", "slug": "left_luggage" },
+ { "emoji": "⚠️", "name": "warning", "slug": "warning" },
{
"emoji": "🚸",
"name": "children crossing",
"slug": "children_crossing"
},
- {
- "emoji": "⛔",
- "name": "no entry",
- "slug": "no_entry"
- },
- {
- "emoji": "🚫",
- "name": "prohibited",
- "slug": "prohibited"
- },
- {
- "emoji": "🚳",
- "name": "no bicycles",
- "slug": "no_bicycles"
- },
- {
- "emoji": "🚭",
- "name": "no smoking",
- "slug": "no_smoking"
- },
- {
- "emoji": "🚯",
- "name": "no littering",
- "slug": "no_littering"
- },
+ { "emoji": "⛔️", "name": "no entry", "slug": "no_entry" },
+ { "emoji": "🚫", "name": "prohibited", "slug": "prohibited" },
+ { "emoji": "🚳", "name": "no bicycles", "slug": "no_bicycles" },
+ { "emoji": "🚭", "name": "no smoking", "slug": "no_smoking" },
+ { "emoji": "🚯", "name": "no littering", "slug": "no_littering" },
{
"emoji": "🚱",
"name": "non-potable water",
"slug": "non_potable_water"
},
- {
- "emoji": "🚷",
- "name": "no pedestrians",
- "slug": "no_pedestrians"
- },
- {
- "emoji": "📵",
- "name": "no mobile phones",
- "slug": "no_mobile_phones"
- },
+ { "emoji": "🚷", "name": "no pedestrians", "slug": "no_pedestrians" },
+ { "emoji": "📵", "name": "no mobile phones", "slug": "no_mobile_phones" },
{
"emoji": "🔞",
"name": "no one under eighteen",
"slug": "no_one_under_eighteen_18_age_limit_prohibited"
},
- {
- "emoji": "☢️",
- "name": "radioactive",
- "slug": "radioactive"
- },
- {
- "emoji": "☣️",
- "name": "biohazard",
- "slug": "biohazard"
- },
- {
- "emoji": "⬆️",
- "name": "up arrow",
- "slug": "up_arrow"
- },
- {
- "emoji": "↗️",
- "name": "up-right arrow",
- "slug": "up_right_arrow"
- },
- {
- "emoji": "➡️",
- "name": "right arrow",
- "slug": "right_arrow"
- },
- {
- "emoji": "↘️",
- "name": "down-right arrow",
- "slug": "down_right_arrow"
- },
- {
- "emoji": "⬇️",
- "name": "down arrow",
- "slug": "down_arrow"
- },
- {
- "emoji": "↙️",
- "name": "down-left arrow",
- "slug": "down_left_arrow"
- },
- {
- "emoji": "⬅️",
- "name": "left arrow",
- "slug": "left_arrow"
- },
- {
- "emoji": "↖️",
- "name": "up-left arrow",
- "slug": "up_left_arrow"
- },
- {
- "emoji": "↕️",
- "name": "up-down arrow",
- "slug": "up_down_arrow"
- },
- {
- "emoji": "↔️",
- "name": "left-right arrow",
- "slug": "left_right_arrow"
- },
+ { "emoji": "☢️", "name": "radioactive", "slug": "radioactive" },
+ { "emoji": "☣️", "name": "biohazard", "slug": "biohazard" },
+ { "emoji": "⬆️", "name": "up arrow", "slug": "up_arrow" },
+ { "emoji": "↗️", "name": "up-right arrow", "slug": "up_right_arrow" },
+ { "emoji": "➡️", "name": "right arrow", "slug": "right_arrow" },
+ { "emoji": "↘️", "name": "down-right arrow", "slug": "down_right_arrow" },
+ { "emoji": "⬇️", "name": "down arrow", "slug": "down_arrow" },
+ { "emoji": "↙️", "name": "down-left arrow", "slug": "down_left_arrow" },
+ { "emoji": "⬅️", "name": "left arrow", "slug": "left_arrow" },
+ { "emoji": "↖️", "name": "up-left arrow", "slug": "up_left_arrow" },
+ { "emoji": "↕️", "name": "up-down arrow", "slug": "up_down_arrow" },
+ { "emoji": "↔️", "name": "left-right arrow", "slug": "left_right_arrow" },
{
"emoji": "↩️",
"name": "right arrow curving left",
@@ -7040,178 +3208,58 @@
"name": "counterclockwise arrows button",
"slug": "counterclockwise_arrows_button"
},
- {
- "emoji": "🔙",
- "name": "BACK arrow",
- "slug": "back_arrow"
- },
- {
- "emoji": "🔚",
- "name": "END arrow",
- "slug": "end_arrow"
- },
- {
- "emoji": "🔛",
- "name": "ON! arrow",
- "slug": "on_arrow"
- },
- {
- "emoji": "🔜",
- "name": "SOON arrow",
- "slug": "soon_arrow"
- },
- {
- "emoji": "🔝",
- "name": "TOP arrow",
- "slug": "top_arrow"
- },
- {
- "emoji": "🛐",
- "name": "place of worship",
- "slug": "place_of_worship"
- },
- {
- "emoji": "⚛️",
- "name": "atom symbol",
- "slug": "atom_symbol"
- },
- {
- "emoji": "🕉️",
- "name": "om",
- "slug": "om"
- },
- {
- "emoji": "✡️",
- "name": "star of David",
- "slug": "star_of_david"
- },
- {
- "emoji": "☸️",
- "name": "wheel of dharma",
- "slug": "wheel_of_dharma"
- },
- {
- "emoji": "☯️",
- "name": "yin yang",
- "slug": "yin_yang"
- },
- {
- "emoji": "✝️",
- "name": "latin cross",
- "slug": "latin_cross"
- },
- {
- "emoji": "☦️",
- "name": "orthodox cross",
- "slug": "orthodox_cross"
- },
+ { "emoji": "🔙", "name": "BACK arrow", "slug": "back_arrow" },
+ { "emoji": "🔚", "name": "END arrow", "slug": "end_arrow" },
+ { "emoji": "🔛", "name": "ON! arrow", "slug": "on_arrow" },
+ { "emoji": "🔜", "name": "SOON arrow", "slug": "soon_arrow" },
+ { "emoji": "🔝", "name": "TOP arrow", "slug": "top_arrow" },
+ { "emoji": "🛐", "name": "place of worship", "slug": "place_of_worship" },
+ { "emoji": "⚛️", "name": "atom symbol", "slug": "atom_symbol" },
+ { "emoji": "🕉️", "name": "om", "slug": "om" },
+ { "emoji": "✡️", "name": "star of David", "slug": "star_of_david" },
+ { "emoji": "☸️", "name": "wheel of dharma", "slug": "wheel_of_dharma" },
+ { "emoji": "☯️", "name": "yin yang", "slug": "yin_yang" },
+ { "emoji": "✝️", "name": "latin cross", "slug": "latin_cross" },
+ { "emoji": "☦️", "name": "orthodox cross", "slug": "orthodox_cross" },
{
"emoji": "☪️",
"name": "star and crescent",
"slug": "star_and_crescent"
},
- {
- "emoji": "☮️",
- "name": "peace symbol",
- "slug": "peace_symbol"
- },
- {
- "emoji": "🕎",
- "name": "menorah",
- "slug": "menorah"
- },
+ { "emoji": "☮️", "name": "peace symbol", "slug": "peace_symbol" },
+ { "emoji": "🕎", "name": "menorah", "slug": "menorah" },
{
"emoji": "🔯",
"name": "dotted six-pointed star",
"slug": "dotted_six_pointed_star"
},
- {
- "emoji": "♈",
- "name": "Aries",
- "slug": "aries"
- },
- {
- "emoji": "♉",
- "name": "Taurus",
- "slug": "taurus"
- },
- {
- "emoji": "♊",
- "name": "Gemini",
- "slug": "gemini"
- },
- {
- "emoji": "♋",
- "name": "Cancer",
- "slug": "cancer"
- },
- {
- "emoji": "♌",
- "name": "Leo",
- "slug": "leo"
- },
- {
- "emoji": "♍",
- "name": "Virgo",
- "slug": "virgo"
- },
- {
- "emoji": "♎",
- "name": "Libra",
- "slug": "libra"
- },
- {
- "emoji": "♏",
- "name": "Scorpio",
- "slug": "scorpio"
- },
- {
- "emoji": "♐",
- "name": "Sagittarius",
- "slug": "sagittarius"
- },
- {
- "emoji": "♑",
- "name": "Capricorn",
- "slug": "capricorn"
- },
- {
- "emoji": "♒",
- "name": "Aquarius",
- "slug": "aquarius"
- },
- {
- "emoji": "♓",
- "name": "Pisces",
- "slug": "pisces"
- },
- {
- "emoji": "⛎",
- "name": "Ophiuchus",
- "slug": "ophiuchus"
- },
+ { "emoji": "♈️", "name": "Aries", "slug": "aries" },
+ { "emoji": "♉️", "name": "Taurus", "slug": "taurus" },
+ { "emoji": "♊️", "name": "Gemini", "slug": "gemini" },
+ { "emoji": "♋️", "name": "Cancer", "slug": "cancer" },
+ { "emoji": "♌️", "name": "Leo", "slug": "leo" },
+ { "emoji": "♍️", "name": "Virgo", "slug": "virgo" },
+ { "emoji": "♎️", "name": "Libra", "slug": "libra" },
+ { "emoji": "♏️", "name": "Scorpio", "slug": "scorpio" },
+ { "emoji": "♐️", "name": "Sagittarius", "slug": "sagittarius" },
+ { "emoji": "♑️", "name": "Capricorn", "slug": "capricorn" },
+ { "emoji": "♒️", "name": "Aquarius", "slug": "aquarius" },
+ { "emoji": "♓️", "name": "Pisces", "slug": "pisces" },
+ { "emoji": "⛎️", "name": "Ophiuchus", "slug": "ophiuchus" },
{
"emoji": "🔀",
"name": "shuffle tracks button",
"slug": "shuffle_tracks_button"
},
- {
- "emoji": "🔁",
- "name": "repeat button",
- "slug": "repeat_button"
- },
+ { "emoji": "🔁", "name": "repeat button", "slug": "repeat_button" },
{
"emoji": "🔂",
"name": "repeat single button",
"slug": "repeat_single_button"
},
+ { "emoji": "▶️", "name": "play button", "slug": "play_button" },
{
- "emoji": "▶️",
- "name": "play button",
- "slug": "play_button"
- },
- {
- "emoji": "⏩",
+ "emoji": "⏩️",
"name": "fast-forward button",
"slug": "fast_forward_button"
},
@@ -7225,13 +3273,9 @@
"name": "play or pause button",
"slug": "play_or_pause_button"
},
+ { "emoji": "◀️", "name": "reverse button", "slug": "reverse_button" },
{
- "emoji": "◀️",
- "name": "reverse button",
- "slug": "reverse_button"
- },
- {
- "emoji": "⏪",
+ "emoji": "⏪️",
"name": "fast reverse button",
"slug": "fast_reverse_button"
},
@@ -7240,121 +3284,41 @@
"name": "last track button",
"slug": "last_track_button"
},
+ { "emoji": "🔼", "name": "upwards button", "slug": "upwards_button" },
+ { "emoji": "⏫️", "name": "fast up button", "slug": "fast_up_button" },
+ { "emoji": "🔽", "name": "downwards button", "slug": "downwards_button" },
{
- "emoji": "🔼",
- "name": "upwards button",
- "slug": "upwards_button"
- },
- {
- "emoji": "⏫",
- "name": "fast up button",
- "slug": "fast_up_button"
- },
- {
- "emoji": "🔽",
- "name": "downwards button",
- "slug": "downwards_button"
- },
- {
- "emoji": "⏬",
+ "emoji": "⏬️",
"name": "fast down button",
"slug": "fast_down_button"
},
- {
- "emoji": "⏸️",
- "name": "pause button",
- "slug": "pause_button"
- },
- {
- "emoji": "⏹️",
- "name": "stop button",
- "slug": "stop_button"
- },
- {
- "emoji": "⏺️",
- "name": "record button",
- "slug": "record_button"
- },
- {
- "emoji": "⏏️",
- "name": "eject button",
- "slug": "eject_button"
- },
- {
- "emoji": "🎦",
- "name": "cinema",
- "slug": "cinema"
- },
- {
- "emoji": "🔅",
- "name": "dim button",
- "slug": "dim_button"
- },
- {
- "emoji": "🔆",
- "name": "bright button",
- "slug": "bright_button"
- },
- {
- "emoji": "📶",
- "name": "antenna bars",
- "slug": "antenna_bars"
- },
- {
- "emoji": "📳",
- "name": "vibration mode",
- "slug": "vibration_mode"
- },
- {
- "emoji": "📴",
- "name": "mobile phone off",
- "slug": "mobile_phone_off"
- },
- {
- "emoji": "♀️",
- "name": "female sign",
- "slug": "female_sign"
- },
- {
- "emoji": "♂️",
- "name": "male sign",
- "slug": "male_sign"
- },
+ { "emoji": "⏸️", "name": "pause button", "slug": "pause_button" },
+ { "emoji": "⏹️", "name": "stop button", "slug": "stop_button" },
+ { "emoji": "⏺️", "name": "record button", "slug": "record_button" },
+ { "emoji": "⏏️", "name": "eject button", "slug": "eject_button" },
+ { "emoji": "🎦", "name": "cinema", "slug": "cinema" },
+ { "emoji": "🔅", "name": "dim button", "slug": "dim_button" },
+ { "emoji": "🔆", "name": "bright button", "slug": "bright_button" },
+ { "emoji": "📶", "name": "antenna bars", "slug": "antenna_bars" },
+ { "emoji": "📳", "name": "vibration mode", "slug": "vibration_mode" },
+ { "emoji": "📴", "name": "mobile phone off", "slug": "mobile_phone_off" },
+ { "emoji": "♀️", "name": "female sign", "slug": "female_sign" },
+ { "emoji": "♂️", "name": "male sign", "slug": "male_sign" },
{
"emoji": "⚧️",
"name": "transgender symbol",
"slug": "transgender_symbol"
},
- {
- "emoji": "✖️",
- "name": "multiply",
- "slug": "multiply"
- },
- {
- "emoji": "➕",
- "name": "plus",
- "slug": "plus"
- },
- {
- "emoji": "➖",
- "name": "minus",
- "slug": "minus"
- },
- {
- "emoji": "➗",
- "name": "divide",
- "slug": "divide"
- },
+ { "emoji": "✖️", "name": "multiply", "slug": "multiply" },
+ { "emoji": "➕️", "name": "plus", "slug": "plus" },
+ { "emoji": "➖️", "name": "minus", "slug": "minus" },
+ { "emoji": "➗️", "name": "divide", "slug": "divide" },
{
"emoji": "🟰",
"name": "heavy equals sign",
"slug": "heavy_equals_sign"
},
- {
- "emoji": "♾️",
- "name": "infinity",
- "slug": "infinity"
- },
+ { "emoji": "♾️", "name": "infinity", "slug": "infinity" },
{
"emoji": "‼️",
"name": "double exclamation mark",
@@ -7366,30 +3330,26 @@
"slug": "exclamation_question_mark"
},
{
- "emoji": "❓",
+ "emoji": "❓️",
"name": "red question mark",
"slug": "red_question_mark"
},
{
- "emoji": "❔",
+ "emoji": "❔️",
"name": "white question mark",
"slug": "white_question_mark"
},
{
- "emoji": "❕",
+ "emoji": "❕️",
"name": "white exclamation mark",
"slug": "white_exclamation_mark"
},
{
- "emoji": "❗",
+ "emoji": "❗️",
"name": "red exclamation mark",
"slug": "red_exclamation_mark"
},
- {
- "emoji": "〰️",
- "name": "wavy dash",
- "slug": "wavy_dash"
- },
+ { "emoji": "〰️", "name": "wavy dash", "slug": "wavy_dash" },
{
"emoji": "💱",
"name": "currency exchange",
@@ -7400,43 +3360,23 @@
"name": "heavy dollar sign",
"slug": "heavy_dollar_sign"
},
- {
- "emoji": "⚕️",
- "name": "medical symbol",
- "slug": "medical_symbol"
- },
- {
- "emoji": "♻️",
- "name": "recycling symbol",
- "slug": "recycling_symbol"
- },
- {
- "emoji": "⚜️",
- "name": "fleur-de-lis",
- "slug": "fleur_de_lis"
- },
- {
- "emoji": "🔱",
- "name": "trident emblem",
- "slug": "trident_emblem"
- },
- {
- "emoji": "📛",
- "name": "name badge",
- "slug": "name_badge"
- },
+ { "emoji": "⚕️", "name": "medical symbol", "slug": "medical_symbol" },
+ { "emoji": "♻️", "name": "recycling symbol", "slug": "recycling_symbol" },
+ { "emoji": "⚜️", "name": "fleur-de-lis", "slug": "fleur_de_lis" },
+ { "emoji": "🔱", "name": "trident emblem", "slug": "trident_emblem" },
+ { "emoji": "📛", "name": "name badge", "slug": "name_badge" },
{
"emoji": "🔰",
"name": "Japanese symbol for beginner",
"slug": "japanese_symbol_for_beginner"
},
{
- "emoji": "⭕",
+ "emoji": "⭕️",
"name": "hollow red circle",
"slug": "hollow_red_circle"
},
{
- "emoji": "✅",
+ "emoji": "✅️",
"name": "check mark button",
"slug": "check_mark_button"
},
@@ -7445,28 +3385,16 @@
"name": "check box with check",
"slug": "check_box_with_check"
},
+ { "emoji": "✔️", "name": "check mark", "slug": "check_mark" },
+ { "emoji": "❌️", "name": "cross mark", "slug": "cross_mark" },
{
- "emoji": "✔️",
- "name": "check mark",
- "slug": "check_mark"
- },
- {
- "emoji": "❌",
- "name": "cross mark",
- "slug": "cross_mark"
- },
- {
- "emoji": "❎",
+ "emoji": "❎️",
"name": "cross mark button",
"slug": "cross_mark_button"
},
+ { "emoji": "➰️", "name": "curly loop", "slug": "curly_loop" },
{
- "emoji": "➰",
- "name": "curly loop",
- "slug": "curly_loop"
- },
- {
- "emoji": "➿",
+ "emoji": "➿️",
"name": "double curly loop",
"slug": "double_curly_loop"
},
@@ -7485,91 +3413,23 @@
"name": "eight-pointed star",
"slug": "eight_pointed_star"
},
- {
- "emoji": "❇️",
- "name": "sparkle",
- "slug": "sparkle"
- },
- {
- "emoji": "©️",
- "name": "copyright",
- "slug": "copyright"
- },
- {
- "emoji": "®️",
- "name": "registered",
- "slug": "registered"
- },
- {
- "emoji": "™️",
- "name": "trade mark",
- "slug": "trade_mark"
- },
- {
- "emoji": "#️⃣",
- "name": "keycap #",
- "slug": "keycap_number_sign"
- },
- {
- "emoji": "*️⃣",
- "name": "keycap *",
- "slug": "keycap_asterisk"
- },
- {
- "emoji": "0️⃣",
- "name": "keycap 0",
- "slug": "keycap_0"
- },
- {
- "emoji": "1️⃣",
- "name": "keycap 1",
- "slug": "keycap_1"
- },
- {
- "emoji": "2️⃣",
- "name": "keycap 2",
- "slug": "keycap_2"
- },
- {
- "emoji": "3️⃣",
- "name": "keycap 3",
- "slug": "keycap_3"
- },
- {
- "emoji": "4️⃣",
- "name": "keycap 4",
- "slug": "keycap_4"
- },
- {
- "emoji": "5️⃣",
- "name": "keycap 5",
- "slug": "keycap_5"
- },
- {
- "emoji": "6️⃣",
- "name": "keycap 6",
- "slug": "keycap_6"
- },
- {
- "emoji": "7️⃣",
- "name": "keycap 7",
- "slug": "keycap_7"
- },
- {
- "emoji": "8️⃣",
- "name": "keycap 8",
- "slug": "keycap_8"
- },
- {
- "emoji": "9️⃣",
- "name": "keycap 9",
- "slug": "keycap_9"
- },
- {
- "emoji": "🔟",
- "name": "keycap 10",
- "slug": "keycap_10"
- },
+ { "emoji": "❇️", "name": "sparkle", "slug": "sparkle" },
+ { "emoji": "©️", "name": "copyright", "slug": "copyright" },
+ { "emoji": "®️", "name": "registered", "slug": "registered" },
+ { "emoji": "™️", "name": "trade mark", "slug": "trade_mark" },
+ { "emoji": "#️⃣", "name": "keycap #", "slug": "keycap_number_sign" },
+ { "emoji": "*️⃣", "name": "keycap *", "slug": "keycap_asterisk" },
+ { "emoji": "0️⃣", "name": "keycap 0", "slug": "keycap_0" },
+ { "emoji": "1️⃣", "name": "keycap 1", "slug": "keycap_1" },
+ { "emoji": "2️⃣", "name": "keycap 2", "slug": "keycap_2" },
+ { "emoji": "3️⃣", "name": "keycap 3", "slug": "keycap_3" },
+ { "emoji": "4️⃣", "name": "keycap 4", "slug": "keycap_4" },
+ { "emoji": "5️⃣", "name": "keycap 5", "slug": "keycap_5" },
+ { "emoji": "6️⃣", "name": "keycap 6", "slug": "keycap_6" },
+ { "emoji": "7️⃣", "name": "keycap 7", "slug": "keycap_7" },
+ { "emoji": "8️⃣", "name": "keycap 8", "slug": "keycap_8" },
+ { "emoji": "9️⃣", "name": "keycap 9", "slug": "keycap_9" },
+ { "emoji": "🔟", "name": "keycap 10", "slug": "keycap_10" },
{
"emoji": "🔠",
"name": "input latin uppercase",
@@ -7580,106 +3440,30 @@
"name": "input latin lowercase",
"slug": "input_latin_lowercase"
},
- {
- "emoji": "🔢",
- "name": "input numbers",
- "slug": "input_numbers"
- },
- {
- "emoji": "🔣",
- "name": "input symbols",
- "slug": "input_symbols"
- },
+ { "emoji": "🔢", "name": "input numbers", "slug": "input_numbers" },
+ { "emoji": "🔣", "name": "input symbols", "slug": "input_symbols" },
{
"emoji": "🔤",
"name": "input latin letters",
"slug": "input_latin_letters"
},
- {
- "emoji": "🅰️",
- "name": "A button (blood type)",
- "slug": "a_button"
- },
- {
- "emoji": "🆎",
- "name": "AB button (blood type)",
- "slug": "ab_button"
- },
- {
- "emoji": "🅱️",
- "name": "B button (blood type)",
- "slug": "b_button"
- },
- {
- "emoji": "🆑",
- "name": "CL button",
- "slug": "cl_button"
- },
- {
- "emoji": "🆒",
- "name": "COOL button",
- "slug": "cool_button"
- },
- {
- "emoji": "🆓",
- "name": "FREE button",
- "slug": "free_button"
- },
- {
- "emoji": "ℹ️",
- "name": "information",
- "slug": "information"
- },
- {
- "emoji": "🆔",
- "name": "ID button",
- "slug": "id_button"
- },
- {
- "emoji": "Ⓜ️",
- "name": "circled M",
- "slug": "circled_m"
- },
- {
- "emoji": "🆕",
- "name": "NEW button",
- "slug": "new_button"
- },
- {
- "emoji": "🆖",
- "name": "NG button",
- "slug": "ng_button"
- },
- {
- "emoji": "🅾️",
- "name": "O button (blood type)",
- "slug": "o_button"
- },
- {
- "emoji": "🆗",
- "name": "OK button",
- "slug": "ok_button"
- },
- {
- "emoji": "🅿️",
- "name": "P button",
- "slug": "p_button"
- },
- {
- "emoji": "🆘",
- "name": "SOS button",
- "slug": "sos_button"
- },
- {
- "emoji": "🆙",
- "name": "UP! button",
- "slug": "up_button"
- },
- {
- "emoji": "🆚",
- "name": "VS button",
- "slug": "vs_button"
- },
+ { "emoji": "🅰️", "name": "A button (blood type)", "slug": "a_button" },
+ { "emoji": "🆎", "name": "AB button (blood type)", "slug": "ab_button" },
+ { "emoji": "🅱️", "name": "B button (blood type)", "slug": "b_button" },
+ { "emoji": "🆑", "name": "CL button", "slug": "cl_button" },
+ { "emoji": "🆒", "name": "COOL button", "slug": "cool_button" },
+ { "emoji": "🆓", "name": "FREE button", "slug": "free_button" },
+ { "emoji": "ℹ️", "name": "information", "slug": "information" },
+ { "emoji": "🆔", "name": "ID button", "slug": "id_button" },
+ { "emoji": "Ⓜ️", "name": "circled M", "slug": "circled_m" },
+ { "emoji": "🆕", "name": "NEW button", "slug": "new_button" },
+ { "emoji": "🆖", "name": "NG button", "slug": "ng_button" },
+ { "emoji": "🅾️", "name": "O button (blood type)", "slug": "o_button" },
+ { "emoji": "🆗", "name": "OK button", "slug": "ok_button" },
+ { "emoji": "🅿️", "name": "P button", "slug": "p_button" },
+ { "emoji": "🆘", "name": "SOS button", "slug": "sos_button" },
+ { "emoji": "🆙", "name": "UP! button", "slug": "up_button" },
+ { "emoji": "🆚", "name": "VS button", "slug": "vs_button" },
{
"emoji": "🈁",
"name": "Japanese “here” button",
@@ -7765,93 +3549,29 @@
"name": "Japanese “no vacancy” button",
"slug": "japanese_no_vacancy_button"
},
+ { "emoji": "🔴", "name": "red circle", "slug": "red_circle" },
+ { "emoji": "🟠", "name": "orange circle", "slug": "orange_circle" },
+ { "emoji": "🟡", "name": "yellow circle", "slug": "yellow_circle" },
+ { "emoji": "🟢", "name": "green circle", "slug": "green_circle" },
+ { "emoji": "🔵", "name": "blue circle", "slug": "blue_circle" },
+ { "emoji": "🟣", "name": "purple circle", "slug": "purple_circle" },
+ { "emoji": "🟤", "name": "brown circle", "slug": "brown_circle" },
+ { "emoji": "⚫️", "name": "black circle", "slug": "black_circle" },
+ { "emoji": "⚪️", "name": "white circle", "slug": "white_circle" },
+ { "emoji": "🟥", "name": "red square", "slug": "red_square" },
+ { "emoji": "🟧", "name": "orange square", "slug": "orange_square" },
+ { "emoji": "🟨", "name": "yellow square", "slug": "yellow_square" },
+ { "emoji": "🟩", "name": "green square", "slug": "green_square" },
+ { "emoji": "🟦", "name": "blue square", "slug": "blue_square" },
+ { "emoji": "🟪", "name": "purple square", "slug": "purple_square" },
+ { "emoji": "🟫", "name": "brown square", "slug": "brown_square" },
{
- "emoji": "🔴",
- "name": "red circle",
- "slug": "red_circle"
- },
- {
- "emoji": "🟠",
- "name": "orange circle",
- "slug": "orange_circle"
- },
- {
- "emoji": "🟡",
- "name": "yellow circle",
- "slug": "yellow_circle"
- },
- {
- "emoji": "🟢",
- "name": "green circle",
- "slug": "green_circle"
- },
- {
- "emoji": "🔵",
- "name": "blue circle",
- "slug": "blue_circle"
- },
- {
- "emoji": "🟣",
- "name": "purple circle",
- "slug": "purple_circle"
- },
- {
- "emoji": "🟤",
- "name": "brown circle",
- "slug": "brown_circle"
- },
- {
- "emoji": "⚫",
- "name": "black circle",
- "slug": "black_circle"
- },
- {
- "emoji": "⚪",
- "name": "white circle",
- "slug": "white_circle"
- },
- {
- "emoji": "🟥",
- "name": "red square",
- "slug": "red_square"
- },
- {
- "emoji": "🟧",
- "name": "orange square",
- "slug": "orange_square"
- },
- {
- "emoji": "🟨",
- "name": "yellow square",
- "slug": "yellow_square"
- },
- {
- "emoji": "🟩",
- "name": "green square",
- "slug": "green_square"
- },
- {
- "emoji": "🟦",
- "name": "blue square",
- "slug": "blue_square"
- },
- {
- "emoji": "🟪",
- "name": "purple square",
- "slug": "purple_square"
- },
- {
- "emoji": "🟫",
- "name": "brown square",
- "slug": "brown_square"
- },
- {
- "emoji": "⬛",
+ "emoji": "⬛️",
"name": "black large square",
"slug": "black_large_square"
},
{
- "emoji": "⬜",
+ "emoji": "⬜️",
"name": "white large square",
"slug": "white_large_square"
},
@@ -7866,12 +3586,12 @@
"slug": "white_medium_square"
},
{
- "emoji": "◾",
+ "emoji": "◾️",
"name": "black medium-small square",
"slug": "black_medium_small_square"
},
{
- "emoji": "◽",
+ "emoji": "◽️",
"name": "white medium-small square",
"slug": "white_medium_small_square"
},
@@ -7920,11 +3640,7 @@
"name": "diamond with a dot",
"slug": "diamond_with_a_dot"
},
- {
- "emoji": "🔘",
- "name": "radio button",
- "slug": "radio_button"
- },
+ { "emoji": "🔘", "name": "radio button", "slug": "radio_button" },
{
"emoji": "🔳",
"name": "white square button",
@@ -7956,226 +3672,94 @@
"name": "crossed flags",
"slug": "crossed_flags_celebration"
},
- {
- "emoji": "🏴",
- "name": "black flag",
- "slug": "black_flag_waving"
- },
- {
- "emoji": "🏳️",
- "name": "white flag",
- "slug": "white_flag_waving"
- },
- {
- "emoji": "🏳️🌈",
- "name": "rainbow flag",
- "slug": "rainbow_flag_pride"
- },
+ { "emoji": "🏴", "name": "black flag", "slug": "black_flag_waving" },
+ { "emoji": "🏳️", "name": "white flag", "slug": "white_flag_waving" },
+ { "emoji": "🏳️🌈", "name": "rainbow flag", "slug": "rainbow_flag_pride" },
{
"emoji": "🏳️⚧️",
"name": "transgender flag",
"slug": "transgender_flag_pink_white"
},
- {
- "emoji": "🏴☠️",
- "name": "pirate flag",
- "slug": "pirate_flag_treasure"
- },
+ { "emoji": "🏴☠️", "name": "pirate flag", "slug": "pirate_flag_treasure" },
{
"emoji": "🇦🇨",
"name": "flag Ascension Island",
"slug": "flag_ascension_island"
},
- {
- "emoji": "🇦🇩",
- "name": "flag Andorra",
- "slug": "flag_andorra"
- },
+ { "emoji": "🇦🇩", "name": "flag Andorra", "slug": "flag_andorra" },
{
"emoji": "🇦🇪",
"name": "flag United Arab Emirates",
"slug": "flag_united_arab_emirates"
},
- {
- "emoji": "🇦🇫",
- "name": "flag Afghanistan",
- "slug": "flag_afghanistan"
- },
+ { "emoji": "🇦🇫", "name": "flag Afghanistan", "slug": "flag_afghanistan" },
{
"emoji": "🇦🇬",
"name": "flag Antigua & Barbuda",
"slug": "flag_antigua_barbuda"
},
- {
- "emoji": "🇦🇮",
- "name": "flag Anguilla",
- "slug": "flag_anguilla"
- },
- {
- "emoji": "🇦🇱",
- "name": "flag Albania",
- "slug": "flag_albania"
- },
- {
- "emoji": "🇦🇲",
- "name": "flag Armenia",
- "slug": "flag_armenia"
- },
- {
- "emoji": "🇦🇴",
- "name": "flag Angola",
- "slug": "flag_angola"
- },
- {
- "emoji": "🇦🇶",
- "name": "flag Antarctica",
- "slug": "flag_antarctica"
- },
- {
- "emoji": "🇦🇷",
- "name": "flag Argentina",
- "slug": "flag_argentina"
- },
+ { "emoji": "🇦🇮", "name": "flag Anguilla", "slug": "flag_anguilla" },
+ { "emoji": "🇦🇱", "name": "flag Albania", "slug": "flag_albania" },
+ { "emoji": "🇦🇲", "name": "flag Armenia", "slug": "flag_armenia" },
+ { "emoji": "🇦🇴", "name": "flag Angola", "slug": "flag_angola" },
+ { "emoji": "🇦🇶", "name": "flag Antarctica", "slug": "flag_antarctica" },
+ { "emoji": "🇦🇷", "name": "flag Argentina", "slug": "flag_argentina" },
{
"emoji": "🇦🇸",
"name": "flag American Samoa",
"slug": "flag_american_samoa"
},
- {
- "emoji": "🇦🇹",
- "name": "flag Austria",
- "slug": "flag_austria"
- },
- {
- "emoji": "🇦🇺",
- "name": "flag Australia",
- "slug": "flag_australia"
- },
- {
- "emoji": "🇦🇼",
- "name": "flag Aruba",
- "slug": "flag_aruba"
- },
+ { "emoji": "🇦🇹", "name": "flag Austria", "slug": "flag_austria" },
+ { "emoji": "🇦🇺", "name": "flag Australia", "slug": "flag_australia" },
+ { "emoji": "🇦🇼", "name": "flag Aruba", "slug": "flag_aruba" },
{
"emoji": "🇦🇽",
"name": "flag Åland Islands",
"slug": "flag_aland_islands"
},
- {
- "emoji": "🇦🇿",
- "name": "flag Azerbaijan",
- "slug": "flag_azerbaijan"
- },
+ { "emoji": "🇦🇿", "name": "flag Azerbaijan", "slug": "flag_azerbaijan" },
{
"emoji": "🇧🇦",
"name": "flag Bosnia & Herzegovina",
"slug": "flag_bosnia_herzegovina"
},
- {
- "emoji": "🇧🇧",
- "name": "flag Barbados",
- "slug": "flag_barbados"
- },
- {
- "emoji": "🇧🇩",
- "name": "flag Bangladesh",
- "slug": "flag_bangladesh"
- },
- {
- "emoji": "🇧🇪",
- "name": "flag Belgium",
- "slug": "flag_belgium"
- },
+ { "emoji": "🇧🇧", "name": "flag Barbados", "slug": "flag_barbados" },
+ { "emoji": "🇧🇩", "name": "flag Bangladesh", "slug": "flag_bangladesh" },
+ { "emoji": "🇧🇪", "name": "flag Belgium", "slug": "flag_belgium" },
{
"emoji": "🇧🇫",
"name": "flag Burkina Faso",
"slug": "flag_burkina_faso"
},
- {
- "emoji": "🇧🇬",
- "name": "flag Bulgaria",
- "slug": "flag_bulgaria"
- },
- {
- "emoji": "🇧🇭",
- "name": "flag Bahrain",
- "slug": "flag_bahrain"
- },
- {
- "emoji": "🇧🇮",
- "name": "flag Burundi",
- "slug": "flag_burundi"
- },
- {
- "emoji": "🇧🇯",
- "name": "flag Benin",
- "slug": "flag_benin"
- },
+ { "emoji": "🇧🇬", "name": "flag Bulgaria", "slug": "flag_bulgaria" },
+ { "emoji": "🇧🇭", "name": "flag Bahrain", "slug": "flag_bahrain" },
+ { "emoji": "🇧🇮", "name": "flag Burundi", "slug": "flag_burundi" },
+ { "emoji": "🇧🇯", "name": "flag Benin", "slug": "flag_benin" },
{
"emoji": "🇧🇱",
"name": "flag St. Barthélemy",
"slug": "flag_st_barthelemy"
},
- {
- "emoji": "🇧🇲",
- "name": "flag Bermuda",
- "slug": "flag_bermuda"
- },
- {
- "emoji": "🇧🇳",
- "name": "flag Brunei",
- "slug": "flag_brunei"
- },
- {
- "emoji": "🇧🇴",
- "name": "flag Bolivia",
- "slug": "flag_bolivia"
- },
+ { "emoji": "🇧🇲", "name": "flag Bermuda", "slug": "flag_bermuda" },
+ { "emoji": "🇧🇳", "name": "flag Brunei", "slug": "flag_brunei" },
+ { "emoji": "🇧🇴", "name": "flag Bolivia", "slug": "flag_bolivia" },
{
"emoji": "🇧🇶",
"name": "flag Caribbean Netherlands",
"slug": "flag_caribbean_netherlands"
},
- {
- "emoji": "🇧🇷",
- "name": "flag Brazil",
- "slug": "flag_brazil"
- },
- {
- "emoji": "🇧🇸",
- "name": "flag Bahamas",
- "slug": "flag_bahamas"
- },
- {
- "emoji": "🇧🇹",
- "name": "flag Bhutan",
- "slug": "flag_bhutan"
- },
+ { "emoji": "🇧🇷", "name": "flag Brazil", "slug": "flag_brazil" },
+ { "emoji": "🇧🇸", "name": "flag Bahamas", "slug": "flag_bahamas" },
+ { "emoji": "🇧🇹", "name": "flag Bhutan", "slug": "flag_bhutan" },
{
"emoji": "🇧🇻",
"name": "flag Bouvet Island",
"slug": "flag_bouvet_island"
},
- {
- "emoji": "🇧🇼",
- "name": "flag Botswana",
- "slug": "flag_botswana"
- },
- {
- "emoji": "🇧🇾",
- "name": "flag Belarus",
- "slug": "flag_belarus"
- },
- {
- "emoji": "🇧🇿",
- "name": "flag Belize",
- "slug": "flag_belize"
- },
- {
- "emoji": "🇨🇦",
- "name": "flag Canada",
- "slug": "flag_canada"
- },
+ { "emoji": "🇧🇼", "name": "flag Botswana", "slug": "flag_botswana" },
+ { "emoji": "🇧🇾", "name": "flag Belarus", "slug": "flag_belarus" },
+ { "emoji": "🇧🇿", "name": "flag Belize", "slug": "flag_belize" },
+ { "emoji": "🇨🇦", "name": "flag Canada", "slug": "flag_canada" },
{
"emoji": "🇨🇨",
"name": "flag Cocos (Keeling) Islands",
@@ -8196,11 +3780,7 @@
"name": "flag Congo - Brazzaville",
"slug": "flag_congo_brazzaville"
},
- {
- "emoji": "🇨🇭",
- "name": "flag Switzerland",
- "slug": "flag_switzerland"
- },
+ { "emoji": "🇨🇭", "name": "flag Switzerland", "slug": "flag_switzerland" },
{
"emoji": "🇨🇮",
"name": "flag Côte d’Ivoire",
@@ -8211,271 +3791,115 @@
"name": "flag Cook Islands",
"slug": "flag_cook_islands"
},
- {
- "emoji": "🇨🇱",
- "name": "flag Chile",
- "slug": "flag_chile"
- },
- {
- "emoji": "🇨🇲",
- "name": "flag Cameroon",
- "slug": "flag_cameroon"
- },
- {
- "emoji": "🇨🇳",
- "name": "flag China",
- "slug": "flag_china"
- },
- {
- "emoji": "🇨🇴",
- "name": "flag Colombia",
- "slug": "flag_colombia"
- },
+ { "emoji": "🇨🇱", "name": "flag Chile", "slug": "flag_chile" },
+ { "emoji": "🇨🇲", "name": "flag Cameroon", "slug": "flag_cameroon" },
+ { "emoji": "🇨🇳", "name": "flag China", "slug": "flag_china" },
+ { "emoji": "🇨🇴", "name": "flag Colombia", "slug": "flag_colombia" },
{
"emoji": "🇨🇵",
"name": "flag Clipperton Island",
"slug": "flag_clipperton_island"
},
- {
- "emoji": "🇨🇷",
- "name": "flag Costa Rica",
- "slug": "flag_costa_rica"
- },
- {
- "emoji": "🇨🇺",
- "name": "flag Cuba",
- "slug": "flag_cuba"
- },
- {
- "emoji": "🇨🇻",
- "name": "flag Cape Verde",
- "slug": "flag_cape_verde"
- },
- {
- "emoji": "🇨🇼",
- "name": "flag Curaçao",
- "slug": "flag_curacao"
- },
+ { "emoji": "🇨🇷", "name": "flag Costa Rica", "slug": "flag_costa_rica" },
+ { "emoji": "🇨🇺", "name": "flag Cuba", "slug": "flag_cuba" },
+ { "emoji": "🇨🇻", "name": "flag Cape Verde", "slug": "flag_cape_verde" },
+ { "emoji": "🇨🇼", "name": "flag Curaçao", "slug": "flag_curacao" },
{
"emoji": "🇨🇽",
"name": "flag Christmas Island",
"slug": "flag_christmas_island"
},
- {
- "emoji": "🇨🇾",
- "name": "flag Cyprus",
- "slug": "flag_cyprus"
- },
- {
- "emoji": "🇨🇿",
- "name": "flag Czechia",
- "slug": "flag_czechia"
- },
- {
- "emoji": "🇩🇪",
- "name": "flag Germany",
- "slug": "flag_germany"
- },
+ { "emoji": "🇨🇾", "name": "flag Cyprus", "slug": "flag_cyprus" },
+ { "emoji": "🇨🇿", "name": "flag Czechia", "slug": "flag_czechia" },
+ { "emoji": "🇩🇪", "name": "flag Germany", "slug": "flag_germany" },
{
"emoji": "🇩🇬",
"name": "flag Diego Garcia",
"slug": "flag_diego_garcia"
},
- {
- "emoji": "🇩🇯",
- "name": "flag Djibouti",
- "slug": "flag_djibouti"
- },
- {
- "emoji": "🇩🇰",
- "name": "flag Denmark",
- "slug": "flag_denmark"
- },
- {
- "emoji": "🇩🇲",
- "name": "flag Dominica",
- "slug": "flag_dominica"
- },
+ { "emoji": "🇩🇯", "name": "flag Djibouti", "slug": "flag_djibouti" },
+ { "emoji": "🇩🇰", "name": "flag Denmark", "slug": "flag_denmark" },
+ { "emoji": "🇩🇲", "name": "flag Dominica", "slug": "flag_dominica" },
{
"emoji": "🇩🇴",
"name": "flag Dominican Republic",
"slug": "flag_dominican_republic"
},
- {
- "emoji": "🇩🇿",
- "name": "flag Algeria",
- "slug": "flag_algeria"
- },
+ { "emoji": "🇩🇿", "name": "flag Algeria", "slug": "flag_algeria" },
{
"emoji": "🇪🇦",
"name": "flag Ceuta & Melilla",
"slug": "flag_ceuta_melilla"
},
- {
- "emoji": "🇪🇨",
- "name": "flag Ecuador",
- "slug": "flag_ecuador"
- },
- {
- "emoji": "🇪🇪",
- "name": "flag Estonia",
- "slug": "flag_estonia"
- },
- {
- "emoji": "🇪🇬",
- "name": "flag Egypt",
- "slug": "flag_egypt"
- },
+ { "emoji": "🇪🇨", "name": "flag Ecuador", "slug": "flag_ecuador" },
+ { "emoji": "🇪🇪", "name": "flag Estonia", "slug": "flag_estonia" },
+ { "emoji": "🇪🇬", "name": "flag Egypt", "slug": "flag_egypt" },
{
"emoji": "🇪🇭",
"name": "flag Western Sahara",
"slug": "flag_western_sahara"
},
- {
- "emoji": "🇪🇷",
- "name": "flag Eritrea",
- "slug": "flag_eritrea"
- },
- {
- "emoji": "🇪🇸",
- "name": "flag Spain",
- "slug": "flag_spain"
- },
- {
- "emoji": "🇪🇹",
- "name": "flag Ethiopia",
- "slug": "flag_ethiopia"
- },
+ { "emoji": "🇪🇷", "name": "flag Eritrea", "slug": "flag_eritrea" },
+ { "emoji": "🇪🇸", "name": "flag Spain", "slug": "flag_spain" },
+ { "emoji": "🇪🇹", "name": "flag Ethiopia", "slug": "flag_ethiopia" },
{
"emoji": "🇪🇺",
"name": "flag European Union",
"slug": "flag_european_union"
},
- {
- "emoji": "🇫🇮",
- "name": "flag Finland",
- "slug": "flag_finland"
- },
- {
- "emoji": "🇫🇯",
- "name": "flag Fiji",
- "slug": "flag_fiji"
- },
+ { "emoji": "🇫🇮", "name": "flag Finland", "slug": "flag_finland" },
+ { "emoji": "🇫🇯", "name": "flag Fiji", "slug": "flag_fiji" },
{
"emoji": "🇫🇰",
"name": "flag Falkland Islands",
"slug": "flag_falkland_islands"
},
- {
- "emoji": "🇫🇲",
- "name": "flag Micronesia",
- "slug": "flag_micronesia"
- },
+ { "emoji": "🇫🇲", "name": "flag Micronesia", "slug": "flag_micronesia" },
{
"emoji": "🇫🇴",
"name": "flag Faroe Islands",
"slug": "flag_faroe_islands"
},
- {
- "emoji": "🇫🇷",
- "name": "flag France",
- "slug": "flag_france"
- },
- {
- "emoji": "🇬🇦",
- "name": "flag Gabon",
- "slug": "flag_gabon"
- },
+ { "emoji": "🇫🇷", "name": "flag France", "slug": "flag_france" },
+ { "emoji": "🇬🇦", "name": "flag Gabon", "slug": "flag_gabon" },
{
"emoji": "🇬🇧",
"name": "flag United Kingdom",
"slug": "flag_united_kingdom"
},
- {
- "emoji": "🇬🇩",
- "name": "flag Grenada",
- "slug": "flag_grenada"
- },
- {
- "emoji": "🇬🇪",
- "name": "flag Georgia",
- "slug": "flag_georgia"
- },
+ { "emoji": "🇬🇩", "name": "flag Grenada", "slug": "flag_grenada" },
+ { "emoji": "🇬🇪", "name": "flag Georgia", "slug": "flag_georgia" },
{
"emoji": "🇬🇫",
"name": "flag French Guiana",
"slug": "flag_french_guiana"
},
- {
- "emoji": "🇬🇬",
- "name": "flag Guernsey",
- "slug": "flag_guernsey"
- },
- {
- "emoji": "🇬🇭",
- "name": "flag Ghana",
- "slug": "flag_ghana"
- },
- {
- "emoji": "🇬🇮",
- "name": "flag Gibraltar",
- "slug": "flag_gibraltar"
- },
- {
- "emoji": "🇬🇱",
- "name": "flag Greenland",
- "slug": "flag_greenland"
- },
- {
- "emoji": "🇬🇲",
- "name": "flag Gambia",
- "slug": "flag_gambia"
- },
- {
- "emoji": "🇬🇳",
- "name": "flag Guinea",
- "slug": "flag_guinea"
- },
- {
- "emoji": "🇬🇵",
- "name": "flag Guadeloupe",
- "slug": "flag_guadeloupe"
- },
+ { "emoji": "🇬🇬", "name": "flag Guernsey", "slug": "flag_guernsey" },
+ { "emoji": "🇬🇭", "name": "flag Ghana", "slug": "flag_ghana" },
+ { "emoji": "🇬🇮", "name": "flag Gibraltar", "slug": "flag_gibraltar" },
+ { "emoji": "🇬🇱", "name": "flag Greenland", "slug": "flag_greenland" },
+ { "emoji": "🇬🇲", "name": "flag Gambia", "slug": "flag_gambia" },
+ { "emoji": "🇬🇳", "name": "flag Guinea", "slug": "flag_guinea" },
+ { "emoji": "🇬🇵", "name": "flag Guadeloupe", "slug": "flag_guadeloupe" },
{
"emoji": "🇬🇶",
"name": "flag Equatorial Guinea",
"slug": "flag_equatorial_guinea"
},
- {
- "emoji": "🇬🇷",
- "name": "flag Greece",
- "slug": "flag_greece"
- },
+ { "emoji": "🇬🇷", "name": "flag Greece", "slug": "flag_greece" },
{
"emoji": "🇬🇸",
"name": "flag South Georgia & South Sandwich Islands",
"slug": "flag_south_georgia_south_sandwich_islands"
},
- {
- "emoji": "🇬🇹",
- "name": "flag Guatemala",
- "slug": "flag_guatemala"
- },
- {
- "emoji": "🇬🇺",
- "name": "flag Guam",
- "slug": "flag_guam"
- },
+ { "emoji": "🇬🇹", "name": "flag Guatemala", "slug": "flag_guatemala" },
+ { "emoji": "🇬🇺", "name": "flag Guam", "slug": "flag_guam" },
{
"emoji": "🇬🇼",
"name": "flag Guinea-Bissau",
"slug": "flag_guinea_bissau"
},
- {
- "emoji": "🇬🇾",
- "name": "flag Guyana",
- "slug": "flag_guyana"
- },
+ { "emoji": "🇬🇾", "name": "flag Guyana", "slug": "flag_guyana" },
{
"emoji": "🇭🇰",
"name": "flag Hong Kong SAR China",
@@ -8486,241 +3910,73 @@
"name": "flag Heard & McDonald Islands",
"slug": "flag_heard_mcdonald_islands"
},
- {
- "emoji": "🇭🇳",
- "name": "flag Honduras",
- "slug": "flag_honduras"
- },
- {
- "emoji": "🇭🇷",
- "name": "flag Croatia",
- "slug": "flag_croatia"
- },
- {
- "emoji": "🇭🇹",
- "name": "flag Haiti",
- "slug": "flag_haiti"
- },
- {
- "emoji": "🇭🇺",
- "name": "flag Hungary",
- "slug": "flag_hungary"
- },
+ { "emoji": "🇭🇳", "name": "flag Honduras", "slug": "flag_honduras" },
+ { "emoji": "🇭🇷", "name": "flag Croatia", "slug": "flag_croatia" },
+ { "emoji": "🇭🇹", "name": "flag Haiti", "slug": "flag_haiti" },
+ { "emoji": "🇭🇺", "name": "flag Hungary", "slug": "flag_hungary" },
{
"emoji": "🇮🇨",
"name": "flag Canary Islands",
"slug": "flag_canary_islands"
},
- {
- "emoji": "🇮🇩",
- "name": "flag Indonesia",
- "slug": "flag_indonesia"
- },
- {
- "emoji": "🇮🇪",
- "name": "flag Ireland",
- "slug": "flag_ireland"
- },
- {
- "emoji": "🇮🇱",
- "name": "flag Israel",
- "slug": "flag_israel"
- },
- {
- "emoji": "🇮🇲",
- "name": "flag Isle of Man",
- "slug": "flag_isle_of_man"
- },
- {
- "emoji": "🇮🇳",
- "name": "flag India",
- "slug": "flag_india"
- },
+ { "emoji": "🇮🇩", "name": "flag Indonesia", "slug": "flag_indonesia" },
+ { "emoji": "🇮🇪", "name": "flag Ireland", "slug": "flag_ireland" },
+ { "emoji": "🇮🇱", "name": "flag Israel", "slug": "flag_israel" },
+ { "emoji": "🇮🇲", "name": "flag Isle of Man", "slug": "flag_isle_of_man" },
+ { "emoji": "🇮🇳", "name": "flag India", "slug": "flag_india" },
{
"emoji": "🇮🇴",
"name": "flag British Indian Ocean Territory",
"slug": "flag_british_indian_ocean_territory"
},
- {
- "emoji": "🇮🇶",
- "name": "flag Iraq",
- "slug": "flag_iraq"
- },
- {
- "emoji": "🇮🇷",
- "name": "flag Iran",
- "slug": "flag_iran"
- },
- {
- "emoji": "🇮🇸",
- "name": "flag Iceland",
- "slug": "flag_iceland"
- },
- {
- "emoji": "🇮🇹",
- "name": "flag Italy",
- "slug": "flag_italy"
- },
- {
- "emoji": "🇯🇪",
- "name": "flag Jersey",
- "slug": "flag_jersey"
- },
- {
- "emoji": "🇯🇲",
- "name": "flag Jamaica",
- "slug": "flag_jamaica"
- },
- {
- "emoji": "🇯🇴",
- "name": "flag Jordan",
- "slug": "flag_jordan"
- },
- {
- "emoji": "🇯🇵",
- "name": "flag Japan",
- "slug": "flag_japan"
- },
- {
- "emoji": "🇰🇪",
- "name": "flag Kenya",
- "slug": "flag_kenya"
- },
- {
- "emoji": "🇰🇬",
- "name": "flag Kyrgyzstan",
- "slug": "flag_kyrgyzstan"
- },
- {
- "emoji": "🇰🇭",
- "name": "flag Cambodia",
- "slug": "flag_cambodia"
- },
- {
- "emoji": "🇰🇮",
- "name": "flag Kiribati",
- "slug": "flag_kiribati"
- },
- {
- "emoji": "🇰🇲",
- "name": "flag Comoros",
- "slug": "flag_comoros"
- },
+ { "emoji": "🇮🇶", "name": "flag Iraq", "slug": "flag_iraq" },
+ { "emoji": "🇮🇷", "name": "flag Iran", "slug": "flag_iran" },
+ { "emoji": "🇮🇸", "name": "flag Iceland", "slug": "flag_iceland" },
+ { "emoji": "🇮🇹", "name": "flag Italy", "slug": "flag_italy" },
+ { "emoji": "🇯🇪", "name": "flag Jersey", "slug": "flag_jersey" },
+ { "emoji": "🇯🇲", "name": "flag Jamaica", "slug": "flag_jamaica" },
+ { "emoji": "🇯🇴", "name": "flag Jordan", "slug": "flag_jordan" },
+ { "emoji": "🇯🇵", "name": "flag Japan", "slug": "flag_japan" },
+ { "emoji": "🇰🇪", "name": "flag Kenya", "slug": "flag_kenya" },
+ { "emoji": "🇰🇬", "name": "flag Kyrgyzstan", "slug": "flag_kyrgyzstan" },
+ { "emoji": "🇰🇭", "name": "flag Cambodia", "slug": "flag_cambodia" },
+ { "emoji": "🇰🇮", "name": "flag Kiribati", "slug": "flag_kiribati" },
+ { "emoji": "🇰🇲", "name": "flag Comoros", "slug": "flag_comoros" },
{
"emoji": "🇰🇳",
"name": "flag St. Kitts & Nevis",
"slug": "flag_st_kitts_nevis"
},
- {
- "emoji": "🇰🇵",
- "name": "flag North Korea",
- "slug": "flag_north_korea"
- },
- {
- "emoji": "🇰🇷",
- "name": "flag South Korea",
- "slug": "flag_south_korea"
- },
- {
- "emoji": "🇰🇼",
- "name": "flag Kuwait",
- "slug": "flag_kuwait"
- },
+ { "emoji": "🇰🇵", "name": "flag North Korea", "slug": "flag_north_korea" },
+ { "emoji": "🇰🇷", "name": "flag South Korea", "slug": "flag_south_korea" },
+ { "emoji": "🇰🇼", "name": "flag Kuwait", "slug": "flag_kuwait" },
{
"emoji": "🇰🇾",
"name": "flag Cayman Islands",
"slug": "flag_cayman_islands"
},
- {
- "emoji": "🇰🇿",
- "name": "flag Kazakhstan",
- "slug": "flag_kazakhstan"
- },
- {
- "emoji": "🇱🇦",
- "name": "flag Laos",
- "slug": "flag_laos"
- },
- {
- "emoji": "🇱🇧",
- "name": "flag Lebanon",
- "slug": "flag_lebanon"
- },
- {
- "emoji": "🇱🇨",
- "name": "flag St. Lucia",
- "slug": "flag_st_lucia"
- },
+ { "emoji": "🇰🇿", "name": "flag Kazakhstan", "slug": "flag_kazakhstan" },
+ { "emoji": "🇱🇦", "name": "flag Laos", "slug": "flag_laos" },
+ { "emoji": "🇱🇧", "name": "flag Lebanon", "slug": "flag_lebanon" },
+ { "emoji": "🇱🇨", "name": "flag St. Lucia", "slug": "flag_st_lucia" },
{
"emoji": "🇱🇮",
"name": "flag Liechtenstein",
"slug": "flag_liechtenstein"
},
- {
- "emoji": "🇱🇰",
- "name": "flag Sri Lanka",
- "slug": "flag_sri_lanka"
- },
- {
- "emoji": "🇱🇷",
- "name": "flag Liberia",
- "slug": "flag_liberia"
- },
- {
- "emoji": "🇱🇸",
- "name": "flag Lesotho",
- "slug": "flag_lesotho"
- },
- {
- "emoji": "🇱🇹",
- "name": "flag Lithuania",
- "slug": "flag_lithuania"
- },
- {
- "emoji": "🇱🇺",
- "name": "flag Luxembourg",
- "slug": "flag_luxembourg"
- },
- {
- "emoji": "🇱🇻",
- "name": "flag Latvia",
- "slug": "flag_latvia"
- },
- {
- "emoji": "🇱🇾",
- "name": "flag Libya",
- "slug": "flag_libya"
- },
- {
- "emoji": "🇲🇦",
- "name": "flag Morocco",
- "slug": "flag_morocco"
- },
- {
- "emoji": "🇲🇨",
- "name": "flag Monaco",
- "slug": "flag_monaco"
- },
- {
- "emoji": "🇲🇩",
- "name": "flag Moldova",
- "slug": "flag_moldova"
- },
- {
- "emoji": "🇲🇪",
- "name": "flag Montenegro",
- "slug": "flag_montenegro"
- },
- {
- "emoji": "🇲🇫",
- "name": "flag St. Martin",
- "slug": "flag_st_martin"
- },
- {
- "emoji": "🇲🇬",
- "name": "flag Madagascar",
- "slug": "flag_madagascar"
- },
+ { "emoji": "🇱🇰", "name": "flag Sri Lanka", "slug": "flag_sri_lanka" },
+ { "emoji": "🇱🇷", "name": "flag Liberia", "slug": "flag_liberia" },
+ { "emoji": "🇱🇸", "name": "flag Lesotho", "slug": "flag_lesotho" },
+ { "emoji": "🇱🇹", "name": "flag Lithuania", "slug": "flag_lithuania" },
+ { "emoji": "🇱🇺", "name": "flag Luxembourg", "slug": "flag_luxembourg" },
+ { "emoji": "🇱🇻", "name": "flag Latvia", "slug": "flag_latvia" },
+ { "emoji": "🇱🇾", "name": "flag Libya", "slug": "flag_libya" },
+ { "emoji": "🇲🇦", "name": "flag Morocco", "slug": "flag_morocco" },
+ { "emoji": "🇲🇨", "name": "flag Monaco", "slug": "flag_monaco" },
+ { "emoji": "🇲🇩", "name": "flag Moldova", "slug": "flag_moldova" },
+ { "emoji": "🇲🇪", "name": "flag Montenegro", "slug": "flag_montenegro" },
+ { "emoji": "🇲🇫", "name": "flag St. Martin", "slug": "flag_st_martin" },
+ { "emoji": "🇲🇬", "name": "flag Madagascar", "slug": "flag_madagascar" },
{
"emoji": "🇲🇭",
"name": "flag Marshall Islands",
@@ -8731,21 +3987,9 @@
"name": "flag North Macedonia",
"slug": "flag_north_macedonia"
},
- {
- "emoji": "🇲🇱",
- "name": "flag Mali",
- "slug": "flag_mali"
- },
- {
- "emoji": "🇲🇲",
- "name": "flag Myanmar (Burma)",
- "slug": "flag_myanmar"
- },
- {
- "emoji": "🇲🇳",
- "name": "flag Mongolia",
- "slug": "flag_mongolia"
- },
+ { "emoji": "🇲🇱", "name": "flag Mali", "slug": "flag_mali" },
+ { "emoji": "🇲🇲", "name": "flag Myanmar (Burma)", "slug": "flag_myanmar" },
+ { "emoji": "🇲🇳", "name": "flag Mongolia", "slug": "flag_mongolia" },
{
"emoji": "🇲🇴",
"name": "flag Macao SAR China",
@@ -8756,131 +4000,39 @@
"name": "flag Northern Mariana Islands",
"slug": "flag_northern_mariana_islands"
},
- {
- "emoji": "🇲🇶",
- "name": "flag Martinique",
- "slug": "flag_martinique"
- },
- {
- "emoji": "🇲🇷",
- "name": "flag Mauritania",
- "slug": "flag_mauritania"
- },
- {
- "emoji": "🇲🇸",
- "name": "flag Montserrat",
- "slug": "flag_montserrat"
- },
- {
- "emoji": "🇲🇹",
- "name": "flag Malta",
- "slug": "flag_malta"
- },
- {
- "emoji": "🇲🇺",
- "name": "flag Mauritius",
- "slug": "flag_mauritius"
- },
- {
- "emoji": "🇲🇻",
- "name": "flag Maldives",
- "slug": "flag_maldives"
- },
- {
- "emoji": "🇲🇼",
- "name": "flag Malawi",
- "slug": "flag_malawi"
- },
- {
- "emoji": "🇲🇽",
- "name": "flag Mexico",
- "slug": "flag_mexico"
- },
- {
- "emoji": "🇲🇾",
- "name": "flag Malaysia",
- "slug": "flag_malaysia"
- },
- {
- "emoji": "🇲🇿",
- "name": "flag Mozambique",
- "slug": "flag_mozambique"
- },
- {
- "emoji": "🇳🇦",
- "name": "flag Namibia",
- "slug": "flag_namibia"
- },
+ { "emoji": "🇲🇶", "name": "flag Martinique", "slug": "flag_martinique" },
+ { "emoji": "🇲🇷", "name": "flag Mauritania", "slug": "flag_mauritania" },
+ { "emoji": "🇲🇸", "name": "flag Montserrat", "slug": "flag_montserrat" },
+ { "emoji": "🇲🇹", "name": "flag Malta", "slug": "flag_malta" },
+ { "emoji": "🇲🇺", "name": "flag Mauritius", "slug": "flag_mauritius" },
+ { "emoji": "🇲🇻", "name": "flag Maldives", "slug": "flag_maldives" },
+ { "emoji": "🇲🇼", "name": "flag Malawi", "slug": "flag_malawi" },
+ { "emoji": "🇲🇽", "name": "flag Mexico", "slug": "flag_mexico" },
+ { "emoji": "🇲🇾", "name": "flag Malaysia", "slug": "flag_malaysia" },
+ { "emoji": "🇲🇿", "name": "flag Mozambique", "slug": "flag_mozambique" },
+ { "emoji": "🇳🇦", "name": "flag Namibia", "slug": "flag_namibia" },
{
"emoji": "🇳🇨",
"name": "flag New Caledonia",
"slug": "flag_new_caledonia"
},
- {
- "emoji": "🇳🇪",
- "name": "flag Niger",
- "slug": "flag_niger"
- },
+ { "emoji": "🇳🇪", "name": "flag Niger", "slug": "flag_niger" },
{
"emoji": "🇳🇫",
"name": "flag Norfolk Island",
"slug": "flag_norfolk_island"
},
- {
- "emoji": "🇳🇬",
- "name": "flag Nigeria",
- "slug": "flag_nigeria"
- },
- {
- "emoji": "🇳🇮",
- "name": "flag Nicaragua",
- "slug": "flag_nicaragua"
- },
- {
- "emoji": "🇳🇱",
- "name": "flag Netherlands",
- "slug": "flag_netherlands"
- },
- {
- "emoji": "🇳🇴",
- "name": "flag Norway",
- "slug": "flag_norway"
- },
- {
- "emoji": "🇳🇵",
- "name": "flag Nepal",
- "slug": "flag_nepal"
- },
- {
- "emoji": "🇳🇷",
- "name": "flag Nauru",
- "slug": "flag_nauru"
- },
- {
- "emoji": "🇳🇺",
- "name": "flag Niue",
- "slug": "flag_niue"
- },
- {
- "emoji": "🇳🇿",
- "name": "flag New Zealand",
- "slug": "flag_new_zealand"
- },
- {
- "emoji": "🇴🇲",
- "name": "flag Oman",
- "slug": "flag_oman"
- },
- {
- "emoji": "🇵🇦",
- "name": "flag Panama",
- "slug": "flag_panama"
- },
- {
- "emoji": "🇵🇪",
- "name": "flag Peru",
- "slug": "flag_peru"
- },
+ { "emoji": "🇳🇬", "name": "flag Nigeria", "slug": "flag_nigeria" },
+ { "emoji": "🇳🇮", "name": "flag Nicaragua", "slug": "flag_nicaragua" },
+ { "emoji": "🇳🇱", "name": "flag Netherlands", "slug": "flag_netherlands" },
+ { "emoji": "🇳🇴", "name": "flag Norway", "slug": "flag_norway" },
+ { "emoji": "🇳🇵", "name": "flag Nepal", "slug": "flag_nepal" },
+ { "emoji": "🇳🇷", "name": "flag Nauru", "slug": "flag_nauru" },
+ { "emoji": "🇳🇺", "name": "flag Niue", "slug": "flag_niue" },
+ { "emoji": "🇳🇿", "name": "flag New Zealand", "slug": "flag_new_zealand" },
+ { "emoji": "🇴🇲", "name": "flag Oman", "slug": "flag_oman" },
+ { "emoji": "🇵🇦", "name": "flag Panama", "slug": "flag_panama" },
+ { "emoji": "🇵🇪", "name": "flag Peru", "slug": "flag_peru" },
{
"emoji": "🇵🇫",
"name": "flag French Polynesia",
@@ -8891,21 +4043,9 @@
"name": "flag Papua New Guinea",
"slug": "flag_papua_new_guinea"
},
- {
- "emoji": "🇵🇭",
- "name": "flag Philippines",
- "slug": "flag_philippines"
- },
- {
- "emoji": "🇵🇰",
- "name": "flag Pakistan",
- "slug": "flag_pakistan"
- },
- {
- "emoji": "🇵🇱",
- "name": "flag Poland",
- "slug": "flag_poland"
- },
+ { "emoji": "🇵🇭", "name": "flag Philippines", "slug": "flag_philippines" },
+ { "emoji": "🇵🇰", "name": "flag Pakistan", "slug": "flag_pakistan" },
+ { "emoji": "🇵🇱", "name": "flag Poland", "slug": "flag_poland" },
{
"emoji": "🇵🇲",
"name": "flag St. Pierre & Miquelon",
@@ -8916,61 +4056,21 @@
"name": "flag Pitcairn Islands",
"slug": "flag_pitcairn_islands"
},
- {
- "emoji": "🇵🇷",
- "name": "flag Puerto Rico",
- "slug": "flag_puerto_rico"
- },
+ { "emoji": "🇵🇷", "name": "flag Puerto Rico", "slug": "flag_puerto_rico" },
{
"emoji": "🇵🇸",
"name": "flag Palestinian Territories",
"slug": "flag_palestinian_territories"
},
- {
- "emoji": "🇵🇹",
- "name": "flag Portugal",
- "slug": "flag_portugal"
- },
- {
- "emoji": "🇵🇼",
- "name": "flag Palau",
- "slug": "flag_palau"
- },
- {
- "emoji": "🇵🇾",
- "name": "flag Paraguay",
- "slug": "flag_paraguay"
- },
- {
- "emoji": "🇶🇦",
- "name": "flag Qatar",
- "slug": "flag_qatar"
- },
- {
- "emoji": "🇷🇪",
- "name": "flag Réunion",
- "slug": "flag_reunion"
- },
- {
- "emoji": "🇷🇴",
- "name": "flag Romania",
- "slug": "flag_romania"
- },
- {
- "emoji": "🇷🇸",
- "name": "flag Serbia",
- "slug": "flag_serbia"
- },
- {
- "emoji": "🇷🇺",
- "name": "flag Russia",
- "slug": "flag_russia"
- },
- {
- "emoji": "🇷🇼",
- "name": "flag Rwanda",
- "slug": "flag_rwanda"
- },
+ { "emoji": "🇵🇹", "name": "flag Portugal", "slug": "flag_portugal" },
+ { "emoji": "🇵🇼", "name": "flag Palau", "slug": "flag_palau" },
+ { "emoji": "🇵🇾", "name": "flag Paraguay", "slug": "flag_paraguay" },
+ { "emoji": "🇶🇦", "name": "flag Qatar", "slug": "flag_qatar" },
+ { "emoji": "🇷🇪", "name": "flag Réunion", "slug": "flag_reunion" },
+ { "emoji": "🇷🇴", "name": "flag Romania", "slug": "flag_romania" },
+ { "emoji": "🇷🇸", "name": "flag Serbia", "slug": "flag_serbia" },
+ { "emoji": "🇷🇺", "name": "flag Russia", "slug": "flag_russia" },
+ { "emoji": "🇷🇼", "name": "flag Rwanda", "slug": "flag_rwanda" },
{
"emoji": "🇸🇦",
"name": "flag Saudi Arabia",
@@ -8981,101 +4081,41 @@
"name": "flag Solomon Islands",
"slug": "flag_solomon_islands"
},
- {
- "emoji": "🇸🇨",
- "name": "flag Seychelles",
- "slug": "flag_seychelles"
- },
- {
- "emoji": "🇸🇩",
- "name": "flag Sudan",
- "slug": "flag_sudan"
- },
- {
- "emoji": "🇸🇪",
- "name": "flag Sweden",
- "slug": "flag_sweden"
- },
- {
- "emoji": "🇸🇬",
- "name": "flag Singapore",
- "slug": "flag_singapore"
- },
- {
- "emoji": "🇸🇭",
- "name": "flag St. Helena",
- "slug": "flag_st_helena"
- },
- {
- "emoji": "🇸🇮",
- "name": "flag Slovenia",
- "slug": "flag_slovenia"
- },
+ { "emoji": "🇸🇨", "name": "flag Seychelles", "slug": "flag_seychelles" },
+ { "emoji": "🇸🇩", "name": "flag Sudan", "slug": "flag_sudan" },
+ { "emoji": "🇸🇪", "name": "flag Sweden", "slug": "flag_sweden" },
+ { "emoji": "🇸🇬", "name": "flag Singapore", "slug": "flag_singapore" },
+ { "emoji": "🇸🇭", "name": "flag St. Helena", "slug": "flag_st_helena" },
+ { "emoji": "🇸🇮", "name": "flag Slovenia", "slug": "flag_slovenia" },
{
"emoji": "🇸🇯",
"name": "flag Svalbard & Jan Mayen",
"slug": "flag_svalbard_jan_mayen"
},
- {
- "emoji": "🇸🇰",
- "name": "flag Slovakia",
- "slug": "flag_slovakia"
- },
+ { "emoji": "🇸🇰", "name": "flag Slovakia", "slug": "flag_slovakia" },
{
"emoji": "🇸🇱",
"name": "flag Sierra Leone",
"slug": "flag_sierra_leone"
},
- {
- "emoji": "🇸🇲",
- "name": "flag San Marino",
- "slug": "flag_san_marino"
- },
- {
- "emoji": "🇸🇳",
- "name": "flag Senegal",
- "slug": "flag_senegal"
- },
- {
- "emoji": "🇸🇴",
- "name": "flag Somalia",
- "slug": "flag_somalia"
- },
- {
- "emoji": "🇸🇷",
- "name": "flag Suriname",
- "slug": "flag_suriname"
- },
- {
- "emoji": "🇸🇸",
- "name": "flag South Sudan",
- "slug": "flag_south_sudan"
- },
+ { "emoji": "🇸🇲", "name": "flag San Marino", "slug": "flag_san_marino" },
+ { "emoji": "🇸🇳", "name": "flag Senegal", "slug": "flag_senegal" },
+ { "emoji": "🇸🇴", "name": "flag Somalia", "slug": "flag_somalia" },
+ { "emoji": "🇸🇷", "name": "flag Suriname", "slug": "flag_suriname" },
+ { "emoji": "🇸🇸", "name": "flag South Sudan", "slug": "flag_south_sudan" },
{
"emoji": "🇸🇹",
"name": "flag São Tomé & Príncipe",
"slug": "flag_sao_tome_principe"
},
- {
- "emoji": "🇸🇻",
- "name": "flag El Salvador",
- "slug": "flag_el_salvador"
- },
+ { "emoji": "🇸🇻", "name": "flag El Salvador", "slug": "flag_el_salvador" },
{
"emoji": "🇸🇽",
"name": "flag Sint Maarten",
"slug": "flag_sint_maarten"
},
- {
- "emoji": "🇸🇾",
- "name": "flag Syria",
- "slug": "flag_syria"
- },
- {
- "emoji": "🇸🇿",
- "name": "flag Eswatini",
- "slug": "flag_eswatini"
- },
+ { "emoji": "🇸🇾", "name": "flag Syria", "slug": "flag_syria" },
+ { "emoji": "🇸🇿", "name": "flag Eswatini", "slug": "flag_eswatini" },
{
"emoji": "🇹🇦",
"name": "flag Tristan da Cunha",
@@ -9086,91 +4126,35 @@
"name": "flag Turks & Caicos Islands",
"slug": "flag_turks_caicos_islands"
},
- {
- "emoji": "🇹🇩",
- "name": "flag Chad",
- "slug": "flag_chad"
- },
+ { "emoji": "🇹🇩", "name": "flag Chad", "slug": "flag_chad" },
{
"emoji": "🇹🇫",
"name": "flag French Southern Territories",
"slug": "flag_french_southern_territories"
},
- {
- "emoji": "🇹🇬",
- "name": "flag Togo",
- "slug": "flag_togo"
- },
- {
- "emoji": "🇹🇭",
- "name": "flag Thailand",
- "slug": "flag_thailand"
- },
- {
- "emoji": "🇹🇯",
- "name": "flag Tajikistan",
- "slug": "flag_tajikistan"
- },
- {
- "emoji": "🇹🇰",
- "name": "flag Tokelau",
- "slug": "flag_tokelau"
- },
- {
- "emoji": "🇹🇱",
- "name": "flag Timor-Leste",
- "slug": "flag_timor_leste"
- },
+ { "emoji": "🇹🇬", "name": "flag Togo", "slug": "flag_togo" },
+ { "emoji": "🇹🇭", "name": "flag Thailand", "slug": "flag_thailand" },
+ { "emoji": "🇹🇯", "name": "flag Tajikistan", "slug": "flag_tajikistan" },
+ { "emoji": "🇹🇰", "name": "flag Tokelau", "slug": "flag_tokelau" },
+ { "emoji": "🇹🇱", "name": "flag Timor-Leste", "slug": "flag_timor_leste" },
{
"emoji": "🇹🇲",
"name": "flag Turkmenistan",
"slug": "flag_turkmenistan"
},
- {
- "emoji": "🇹🇳",
- "name": "flag Tunisia",
- "slug": "flag_tunisia"
- },
- {
- "emoji": "🇹🇴",
- "name": "flag Tonga",
- "slug": "flag_tonga"
- },
- {
- "emoji": "🇹🇷",
- "name": "flag Turkey",
- "slug": "flag_turkey"
- },
+ { "emoji": "🇹🇳", "name": "flag Tunisia", "slug": "flag_tunisia" },
+ { "emoji": "🇹🇴", "name": "flag Tonga", "slug": "flag_tonga" },
+ { "emoji": "🇹🇷", "name": "flag Turkey", "slug": "flag_turkey" },
{
"emoji": "🇹🇹",
"name": "flag Trinidad & Tobago",
"slug": "flag_trinidad_tobago"
},
- {
- "emoji": "🇹🇻",
- "name": "flag Tuvalu",
- "slug": "flag_tuvalu"
- },
- {
- "emoji": "🇹🇼",
- "name": "flag Taiwan",
- "slug": "flag_taiwan"
- },
- {
- "emoji": "🇹🇿",
- "name": "flag Tanzania",
- "slug": "flag_tanzania"
- },
- {
- "emoji": "🇺🇦",
- "name": "flag Ukraine",
- "slug": "flag_ukraine"
- },
- {
- "emoji": "🇺🇬",
- "name": "flag Uganda",
- "slug": "flag_uganda"
- },
+ { "emoji": "🇹🇻", "name": "flag Tuvalu", "slug": "flag_tuvalu" },
+ { "emoji": "🇹🇼", "name": "flag Taiwan", "slug": "flag_taiwan" },
+ { "emoji": "🇹🇿", "name": "flag Tanzania", "slug": "flag_tanzania" },
+ { "emoji": "🇺🇦", "name": "flag Ukraine", "slug": "flag_ukraine" },
+ { "emoji": "🇺🇬", "name": "flag Uganda", "slug": "flag_uganda" },
{
"emoji": "🇺🇲",
"name": "flag U.S. Outlying Islands",
@@ -9186,16 +4170,8 @@
"name": "flag United States",
"slug": "flag_united_states"
},
- {
- "emoji": "🇺🇾",
- "name": "flag Uruguay",
- "slug": "flag_uruguay"
- },
- {
- "emoji": "🇺🇿",
- "name": "flag Uzbekistan",
- "slug": "flag_uzbekistan"
- },
+ { "emoji": "🇺🇾", "name": "flag Uruguay", "slug": "flag_uruguay" },
+ { "emoji": "🇺🇿", "name": "flag Uzbekistan", "slug": "flag_uzbekistan" },
{
"emoji": "🇻🇦",
"name": "flag Vatican City",
@@ -9206,11 +4182,7 @@
"name": "flag St. Vincent & Grenadines",
"slug": "flag_st_vincent_grenadines"
},
- {
- "emoji": "🇻🇪",
- "name": "flag Venezuela",
- "slug": "flag_venezuela"
- },
+ { "emoji": "🇻🇪", "name": "flag Venezuela", "slug": "flag_venezuela" },
{
"emoji": "🇻🇬",
"name": "flag British Virgin Islands",
@@ -9221,71 +4193,27 @@
"name": "flag U.S. Virgin Islands",
"slug": "flag_u_s_virgin_islands"
},
- {
- "emoji": "🇻🇳",
- "name": "flag Vietnam",
- "slug": "flag_vietnam"
- },
- {
- "emoji": "🇻🇺",
- "name": "flag Vanuatu",
- "slug": "flag_vanuatu"
- },
+ { "emoji": "🇻🇳", "name": "flag Vietnam", "slug": "flag_vietnam" },
+ { "emoji": "🇻🇺", "name": "flag Vanuatu", "slug": "flag_vanuatu" },
{
"emoji": "🇼🇫",
"name": "flag Wallis & Futuna",
"slug": "flag_wallis_futuna"
},
- {
- "emoji": "🇼🇸",
- "name": "flag Samoa",
- "slug": "flag_samoa"
- },
- {
- "emoji": "🇽🇰",
- "name": "flag Kosovo",
- "slug": "flag_kosovo"
- },
- {
- "emoji": "🇾🇪",
- "name": "flag Yemen",
- "slug": "flag_yemen"
- },
- {
- "emoji": "🇾🇹",
- "name": "flag Mayotte",
- "slug": "flag_mayotte"
- },
+ { "emoji": "🇼🇸", "name": "flag Samoa", "slug": "flag_samoa" },
+ { "emoji": "🇽🇰", "name": "flag Kosovo", "slug": "flag_kosovo" },
+ { "emoji": "🇾🇪", "name": "flag Yemen", "slug": "flag_yemen" },
+ { "emoji": "🇾🇹", "name": "flag Mayotte", "slug": "flag_mayotte" },
{
"emoji": "🇿🇦",
"name": "flag South Africa",
"slug": "flag_south_africa"
},
- {
- "emoji": "🇿🇲",
- "name": "flag Zambia",
- "slug": "flag_zambia"
- },
- {
- "emoji": "🇿🇼",
- "name": "flag Zimbabwe",
- "slug": "flag_zimbabwe"
- },
- {
- "emoji": "🏴",
- "name": "flag England",
- "slug": "flag_england"
- },
- {
- "emoji": "🏴",
- "name": "flag Scotland",
- "slug": "flag_scotland"
- },
- {
- "emoji": "🏴",
- "name": "flag Wales",
- "slug": "flag_wales"
- }
+ { "emoji": "🇿🇲", "name": "flag Zambia", "slug": "flag_zambia" },
+ { "emoji": "🇿🇼", "name": "flag Zimbabwe", "slug": "flag_zimbabwe" },
+ { "emoji": "🏴", "name": "flag England", "slug": "flag_england" },
+ { "emoji": "🏴", "name": "flag Scotland", "slug": "flag_scotland" },
+ { "emoji": "🏴", "name": "flag Wales", "slug": "flag_wales" }
]
}
]
diff --git a/app/javascript/shared/components/emoji/pickerHelper.js b/app/javascript/shared/components/emoji/pickerHelper.js
new file mode 100644
index 000000000..c77f102f2
--- /dev/null
+++ b/app/javascript/shared/components/emoji/pickerHelper.js
@@ -0,0 +1,93 @@
+import emojiGroups from 'shared/components/emoji/emojisGroup.json';
+
+// Recently used emojis persisted in localStorage.
+const RECENT_EMOJI_KEY = 'emoji-icon-picker.recent-emojis';
+const MAX_RECENT_EMOJIS = 16;
+
+const matchesSearch = (emoji, term) =>
+ emoji.slug.replaceAll('_', ' ').includes(term) ||
+ emoji.name.toLowerCase().includes(term);
+
+// Emoji sections for the search term; prepends "Frequently used" when idle.
+export const buildEmojiSections = (search, recentEmojis, frequentLabel) => {
+ const term = search.trim().toLowerCase();
+ if (term) {
+ return emojiGroups
+ .map(({ name, emojis }) => ({
+ name,
+ emojis: emojis.filter(e => matchesSearch(e, term)),
+ }))
+ .filter(group => group.emojis.length);
+ }
+ return [
+ ...(recentEmojis.length
+ ? [{ name: frequentLabel, emojis: recentEmojis }]
+ : []),
+ ...emojiGroups,
+ ];
+};
+
+// Samples an emoji's average color (via canvas) as a translucent hover tint. Cached per emoji.
+const emojiTintCache = new Map();
+const NEUTRAL_TINT = 'rgb(var(--slate-9) / 0.12)';
+
+const sampleEmojiTint = emoji => {
+ const canvas = Object.assign(document.createElement('canvas'), {
+ width: 16,
+ height: 16,
+ });
+ const ctx = canvas.getContext('2d', { willReadFrequently: true });
+ ctx.font = '14px serif';
+ ctx.textAlign = 'center';
+ ctx.textBaseline = 'middle';
+ ctx.fillText(emoji, 8, 9);
+
+ const { data } = ctx.getImageData(0, 0, 16, 16);
+ const pixels = Array.from(
+ { length: data.length / 4 },
+ (_, i) => i * 4
+ ).filter(i => data[i + 3] > 16);
+ if (!pixels.length) return NEUTRAL_TINT;
+
+ const avg = offset =>
+ Math.round(
+ pixels.reduce((sum, i) => sum + data[i + offset], 0) / pixels.length
+ );
+
+ return `rgba(${avg(0)}, ${avg(1)}, ${avg(2)}, 0.16)`;
+};
+
+export const getEmojiTint = emoji => {
+ if (!emojiTintCache.has(emoji)) {
+ let tint = NEUTRAL_TINT;
+ try {
+ tint = sampleEmojiTint(emoji);
+ } catch {
+ /* canvas unavailable */
+ }
+ emojiTintCache.set(emoji, tint);
+ }
+ return emojiTintCache.get(emoji);
+};
+
+export const getRecentEmojis = () => {
+ try {
+ const stored = JSON.parse(localStorage.getItem(RECENT_EMOJI_KEY) ?? '[]');
+ return Array.isArray(stored) ? stored : [];
+ } catch {
+ return [];
+ }
+};
+
+export const addRecentEmoji = emoji => {
+ const updated = [
+ emoji,
+ ...getRecentEmojis().filter(item => item.slug !== emoji.slug),
+ ].slice(0, MAX_RECENT_EMOJIS);
+ try {
+ localStorage.setItem(RECENT_EMOJI_KEY, JSON.stringify(updated));
+ } catch {
+ /* private mode; recents are best-effort */
+ }
+ return updated;
+};
diff --git a/app/javascript/v3/api/auth.js b/app/javascript/v3/api/auth.js
index 4c4ffb13e..e7f87824a 100644
--- a/app/javascript/v3/api/auth.js
+++ b/app/javascript/v3/api/auth.js
@@ -43,6 +43,15 @@ export const login = async ({
mfaToken: error.response.data.mfa_token,
};
}
+ if (
+ error.response?.status === 409 &&
+ error.response?.data?.sessions_limit_reached
+ ) {
+ return {
+ sessionsLimitReached: true,
+ sessions: error.response.data.sessions,
+ };
+ }
const loginError = new Error(parseAPIErrorResponse(error));
loginError.errorCode = error.response?.data?.error_code;
throw loginError;
diff --git a/app/javascript/v3/views/login/Index.vue b/app/javascript/v3/views/login/Index.vue
index 2f8cf70ef..54f6996a0 100644
--- a/app/javascript/v3/views/login/Index.vue
+++ b/app/javascript/v3/views/login/Index.vue
@@ -8,6 +8,8 @@ import { useVuelidate } from '@vuelidate/core';
import { SESSION_STORAGE_KEYS } from 'dashboard/constants/sessionStorage';
import SessionStorage from 'shared/helpers/sessionStorage';
import { useBranding } from 'shared/composables/useBranding';
+import AnalyticsHelper from 'dashboard/helper/AnalyticsHelper';
+import { SESSION_EVENTS } from 'dashboard/helper/AnalyticsHelper/events';
// components
import SimpleDivider from '../../components/Divider/SimpleDivider.vue';
@@ -17,6 +19,7 @@ import Spinner from 'shared/components/Spinner.vue';
import Icon from 'dashboard/components-next/icon/Icon.vue';
import NextButton from 'dashboard/components-next/button/Button.vue';
import MfaVerification from 'dashboard/components/auth/MfaVerification.vue';
+import SessionLimitOverlay from 'dashboard/components/auth/SessionLimitOverlay.vue';
const ERROR_MESSAGES = {
'no-account-found': 'LOGIN.OAUTH.NO_ACCOUNT_FOUND',
@@ -36,6 +39,7 @@ export default {
NextButton,
SimpleDivider,
MfaVerification,
+ SessionLimitOverlay,
Icon,
},
props: {
@@ -68,6 +72,8 @@ export default {
error: '',
mfaRequired: false,
mfaToken: null,
+ sessionsLimitReached: false,
+ limitedSessions: [],
};
},
validations() {
@@ -182,6 +188,15 @@ export default {
return;
}
+ // Check if sessions limit reached
+ if (result?.sessionsLimitReached) {
+ this.loginApi.showLoading = false;
+ this.sessionsLimitReached = true;
+ this.limitedSessions = result.sessions;
+ AnalyticsHelper.track(SESSION_EVENTS.LIMIT_HIT);
+ return;
+ }
+
this.handleImpersonation();
this.showAlertMessage(this.$t('LOGIN.API.SUCCESS_MESSAGE'));
})
@@ -224,6 +239,51 @@ export default {
this.mfaToken = null;
this.credentials.password = '';
},
+ retryLoginWithParams(extraParams) {
+ const credentials = {
+ email: this.email
+ ? decodeURIComponent(this.email)
+ : this.credentials.email,
+ password: this.credentials.password,
+ sso_auth_token: this.ssoAuthToken,
+ ssoAccountId: this.ssoAccountId,
+ ssoConversationId: this.ssoConversationId,
+ ...extraParams,
+ };
+
+ this.sessionsLimitReached = false;
+ this.limitedSessions = [];
+ this.loginApi.showLoading = true;
+ login(credentials)
+ .then(result => {
+ if (result?.sessionsLimitReached) {
+ this.loginApi.showLoading = false;
+ this.sessionsLimitReached = true;
+ this.limitedSessions = result.sessions;
+ AnalyticsHelper.track(SESSION_EVENTS.LIMIT_HIT);
+ return;
+ }
+ this.handleImpersonation();
+ this.showAlertMessage(this.$t('LOGIN.API.SUCCESS_MESSAGE'));
+ })
+ .catch(response => {
+ this.loginApi.hasErrored = true;
+ this.showAlertMessage(
+ response?.message || this.$t('LOGIN.API.UNAUTH')
+ );
+ });
+ },
+ handleSessionRevoke(sessionId) {
+ this.retryLoginWithParams({ revoke_session_id: sessionId });
+ },
+ handleSessionRevokeAll() {
+ this.retryLoginWithParams({ revoke_all_sessions: true });
+ },
+ handleSessionLimitCancel() {
+ this.sessionsLimitReached = false;
+ this.limitedSessions = [];
+ this.credentials.password = '';
+ },
},
};
@@ -255,8 +315,18 @@ export default {
+
+
+
-
+
+import { defineAsyncComponent } from 'vue';
import { mapGetters } from 'vuex';
import ChatAttachmentButton from 'widget/components/ChatAttachment.vue';
@@ -7,14 +8,16 @@ import { useAttachments } from '../composables/useAttachments';
import FluentIcon from 'shared/components/FluentIcon/Index.vue';
import ResizableTextArea from 'shared/components/ResizableTextArea.vue';
-import EmojiInput from 'shared/components/emoji/EmojiInput.vue';
+const EmojiPicker = defineAsyncComponent(
+ () => import('shared/components/emoji/EmojiPicker.vue')
+);
export default {
name: 'ChatInputWrap',
components: {
ChatAttachmentButton,
ChatSendButton,
- EmojiInput,
+ EmojiPicker,
FluentIcon,
ResizableTextArea,
},
@@ -110,6 +113,9 @@ export default {
emojiOnClick(emoji) {
this.userInput = `${this.userInput}${emoji} `;
},
+ onSelectEmoji({ value }) {
+ this.emojiOnClick(value);
+ },
onTypingOff() {
this.toggleTyping('off');
},
@@ -148,7 +154,7 @@ export default {
@focus="onFocus"
@blur="onBlur"
/>
-