From 13b98366932f18c69d813fd800ce9a4800f8e845 Mon Sep 17 00:00:00 2001
From: Wladimir Palant <374261+palant@users.noreply.github.com>
Date: Mon, 2 Dec 2024 13:08:50 +0100
Subject: [PATCH 1/3] Initial support for Unicode emojis
---
package-lock.json | 23 +++++++++
package.json | 1 +
src/components/compose.jsx | 75 ++++++++++++++++-----------
src/locales/en.po | 102 ++++++++++++++++++-------------------
vite.config.js | 64 ++++++++++++++++++++++-
5 files changed, 182 insertions(+), 83 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index 040f1b3c68..041e85ceb6 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -20,6 +20,7 @@
"@szhsin/react-menu": "~4.2.3",
"chroma-js": "~3.1.2",
"compare-versions": "~6.1.1",
+ "emojibase-data": "^15.3.2",
"fast-blurhash": "~1.1.4",
"fast-equals": "~5.0.1",
"fuse.js": "~7.0.0",
@@ -5538,6 +5539,28 @@
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"dev": true
},
+ "node_modules/emojibase": {
+ "version": "15.3.1",
+ "resolved": "https://registry.npmjs.org/emojibase/-/emojibase-15.3.1.tgz",
+ "integrity": "sha512-GNsjHnG2J3Ktg684Fs/vZR/6XpOSkZPMAv85EHrr6br2RN2cJNwdS4am/3YSK3y+/gOv2kmoK3GGdahXdMxg2g==",
+ "peer": true,
+ "funding": {
+ "type": "ko-fi",
+ "url": "https://ko-fi.com/milesjohnson"
+ }
+ },
+ "node_modules/emojibase-data": {
+ "version": "15.3.2",
+ "resolved": "https://registry.npmjs.org/emojibase-data/-/emojibase-data-15.3.2.tgz",
+ "integrity": "sha512-TpDyTDDTdqWIJixV5sTA6OQ0P0JfIIeK2tFRR3q56G9LK65ylAZ7z3KyBXokpvTTJ+mLUXQXbLNyVkjvnTLE+A==",
+ "funding": {
+ "type": "ko-fi",
+ "url": "https://ko-fi.com/milesjohnson"
+ },
+ "peerDependencies": {
+ "emojibase": "*"
+ }
+ },
"node_modules/entities": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
diff --git a/package.json b/package.json
index 8ec05e59ed..c22ce26776 100644
--- a/package.json
+++ b/package.json
@@ -28,6 +28,7 @@
"@szhsin/react-menu": "~4.2.3",
"chroma-js": "~3.1.2",
"compare-versions": "~6.1.1",
+ "emojibase-data": "^15.3.2",
"fast-blurhash": "~1.1.4",
"fast-equals": "~5.0.1",
"fuse.js": "~7.0.0",
diff --git a/src/components/compose.jsx b/src/components/compose.jsx
index d25d1ed9ea..6d0c3956c2 100644
--- a/src/components/compose.jsx
+++ b/src/components/compose.jsx
@@ -1786,11 +1786,20 @@ function autoResizeTextarea(textarea) {
}
}
-async function _getCustomEmojis(instance, masto) {
- const emojis = await masto.v1.customEmojis.list();
- const visibleEmojis = emojis.filter((e) => e.visibleInPicker);
+async function _getCustomEmojis(lang, instance, masto) {
+ const [unicodeEmojis, customEmojis] = await Promise.all([
+ fetch(`./assets/emojis/${lang}.json`).then((r) => r.json()),
+ masto.v1.customEmojis.list()
+ ]);
+
+ for (let emoji of unicodeEmojis.emojis)
+ if (emoji.group in unicodeEmojis.groups)
+ emoji.category = unicodeEmojis.groups[emoji.group].message;
+
+ const visibleEmojis = customEmojis.filter((e) => e.visibleInPicker)
+ .concat(unicodeEmojis.emojis);
const searcher = new Fuse(visibleEmojis, {
- keys: ['shortcode'],
+ keys: ['shortcode', 'label', 'tags'],
findAllMatches: true,
});
return [visibleEmojis, searcher];
@@ -1827,7 +1836,7 @@ const Textarea = forwardRef((props, ref) => {
// const customEmojis = useRef();
const searcherRef = useRef();
useEffect(() => {
- getCustomEmojis(instance, masto)
+ getCustomEmojis(i18n.locale, instance, masto)
.then((r) => {
const [emojis, searcher] = r;
searcherRef.current = searcher;
@@ -1866,13 +1875,15 @@ const Textarea = forwardRef((props, ref) => {
});
let html = '';
results.forEach(({ item: emoji }) => {
- const { shortcode, url } = emoji;
+ const { unicode, shortcode, url } = emoji;
+ const presentation = unicode ? unicode :
+ `
`;
html += `
-
-
- ${encodeHTML(shortcode)}
+
+ ${presentation}
+ ${encodeHTML(shortcode || "")}
`;
});
html += `${t`More…`}`;
@@ -1974,7 +1985,7 @@ const Textarea = forwardRef((props, ref) => {
const { key, item } = e.detail;
const { value, more } = item.dataset;
if (key === ':') {
- e.detail.value = value ? `:${value}:` : ''; // zero-width space
+ e.detail.value = value || ''; // zero-width space
if (more) {
// Prevent adding space after the above value
e.detail.continue = true;
@@ -3115,7 +3126,7 @@ function CustomEmojisModal({
setUIState('loading');
(async () => {
try {
- const [emojis, searcher] = await getCustomEmojis(instance, masto);
+ const [emojis, searcher] = await getCustomEmojis(i18n.locale, instance, masto);
console.log('emojis', emojis);
searcherRef.current = searcher;
setCustomEmojis(emojis);
@@ -3244,7 +3255,7 @@ function CustomEmojisModal({
e.preventDefault();
const emoji = matches[0];
if (emoji) {
- onSelectEmoji(`:${emoji.shortcode}:`);
+ onSelectEmoji(emoji.unicode ? emoji.unicode : `:${emoji.shortcode}:`);
}
}}
>
@@ -3270,7 +3281,7 @@ function CustomEmojisModal({
{
- onSelectEmoji(`:${emoji.shortcode}:`);
+ onSelectEmoji(emoji.unicode ? emoji.unicode : `:${emoji.shortcode}:`);
}}
showCode
/>
@@ -3370,23 +3381,25 @@ const CustomEmojiButton = memo(({ emoji, onClick, showCode }) => {
onPointerEnter={addEdges}
onFocus={addEdges}
>
-
- {!!emoji.staticUrl && (
- {emoji.unicode}> || (
+
+ {!!emoji.staticUrl && (
+
+ )}
+
- )}
-
-
+
+ )}
{showCode && (
<>
{' '}
diff --git a/src/locales/en.po b/src/locales/en.po
index 238dc806d1..00d2ee995e 100644
--- a/src/locales/en.po
+++ b/src/locales/en.po
@@ -104,7 +104,7 @@ msgstr ""
#: src/components/account-info.jsx:429
#: src/components/account-info.jsx:1120
-#: src/components/compose.jsx:2591
+#: src/components/compose.jsx:2602
#: src/components/media-alt-modal.jsx:45
#: src/components/media-modal.jsx:357
#: src/components/status.jsx:1737
@@ -400,10 +400,10 @@ msgstr ""
#: src/components/account-info.jsx:2094
#: src/components/account-sheet.jsx:37
#: src/components/compose.jsx:859
-#: src/components/compose.jsx:2547
-#: src/components/compose.jsx:3020
-#: src/components/compose.jsx:3228
-#: src/components/compose.jsx:3458
+#: src/components/compose.jsx:2558
+#: src/components/compose.jsx:3031
+#: src/components/compose.jsx:3239
+#: src/components/compose.jsx:3471
#: src/components/drafts.jsx:58
#: src/components/embed-modal.jsx:12
#: src/components/generic-accounts.jsx:142
@@ -681,7 +681,7 @@ msgid "Mark media as sensitive"
msgstr ""
#: src/components/compose.jsx:1381
-#: src/components/compose.jsx:3078
+#: src/components/compose.jsx:3089
#: src/components/shortcuts-settings.jsx:715
#: src/pages/list.jsx:359
msgid "Add"
@@ -713,172 +713,172 @@ msgstr ""
msgid "Failed to download GIF"
msgstr ""
-#: src/components/compose.jsx:1878
-#: src/components/compose.jsx:1955
+#: src/components/compose.jsx:1889
+#: src/components/compose.jsx:1966
#: src/components/nav-menu.jsx:238
msgid "More…"
msgstr ""
-#: src/components/compose.jsx:2360
+#: src/components/compose.jsx:2371
msgid "Uploaded"
msgstr ""
-#: src/components/compose.jsx:2373
+#: src/components/compose.jsx:2384
msgid "Image description"
msgstr ""
-#: src/components/compose.jsx:2374
+#: src/components/compose.jsx:2385
msgid "Video description"
msgstr ""
-#: src/components/compose.jsx:2375
+#: src/components/compose.jsx:2386
msgid "Audio description"
msgstr ""
-#: src/components/compose.jsx:2411
-#: src/components/compose.jsx:2431
+#: src/components/compose.jsx:2422
+#: src/components/compose.jsx:2442
msgid "File size too large. Uploading might encounter issues. Try reduce the file size from {0} to {1} or lower."
msgstr ""
-#: src/components/compose.jsx:2423
-#: src/components/compose.jsx:2443
+#: src/components/compose.jsx:2434
+#: src/components/compose.jsx:2454
msgid "Dimension too large. Uploading might encounter issues. Try reduce dimension from {0}×{1}px to {2}×{3}px."
msgstr ""
-#: src/components/compose.jsx:2451
+#: src/components/compose.jsx:2462
msgid "Frame rate too high. Uploading might encounter issues."
msgstr ""
-#: src/components/compose.jsx:2511
-#: src/components/compose.jsx:2761
+#: src/components/compose.jsx:2522
+#: src/components/compose.jsx:2772
#: src/components/shortcuts-settings.jsx:726
#: src/pages/catchup.jsx:1074
#: src/pages/filters.jsx:412
msgid "Remove"
msgstr ""
-#: src/components/compose.jsx:2528
+#: src/components/compose.jsx:2539
#: src/compose.jsx:83
msgid "Error"
msgstr ""
-#: src/components/compose.jsx:2553
+#: src/components/compose.jsx:2564
msgid "Edit image description"
msgstr ""
-#: src/components/compose.jsx:2554
+#: src/components/compose.jsx:2565
msgid "Edit video description"
msgstr ""
-#: src/components/compose.jsx:2555
+#: src/components/compose.jsx:2566
msgid "Edit audio description"
msgstr ""
-#: src/components/compose.jsx:2600
-#: src/components/compose.jsx:2649
+#: src/components/compose.jsx:2611
+#: src/components/compose.jsx:2660
msgid "Generating description. Please wait…"
msgstr ""
-#: src/components/compose.jsx:2620
+#: src/components/compose.jsx:2631
msgid "Failed to generate description: {0}"
msgstr ""
-#: src/components/compose.jsx:2621
+#: src/components/compose.jsx:2632
msgid "Failed to generate description"
msgstr ""
-#: src/components/compose.jsx:2633
-#: src/components/compose.jsx:2639
-#: src/components/compose.jsx:2685
+#: src/components/compose.jsx:2644
+#: src/components/compose.jsx:2650
+#: src/components/compose.jsx:2696
msgid "Generate description…"
msgstr ""
-#: src/components/compose.jsx:2672
+#: src/components/compose.jsx:2683
msgid "Failed to generate description{0}"
msgstr ""
-#: src/components/compose.jsx:2687
+#: src/components/compose.jsx:2698
msgid "({0}) <0>— experimental0>"
msgstr ""
-#: src/components/compose.jsx:2706
+#: src/components/compose.jsx:2717
msgid "Done"
msgstr ""
-#: src/components/compose.jsx:2742
+#: src/components/compose.jsx:2753
msgid "Choice {0}"
msgstr ""
-#: src/components/compose.jsx:2789
+#: src/components/compose.jsx:2800
msgid "Multiple choices"
msgstr ""
-#: src/components/compose.jsx:2792
+#: src/components/compose.jsx:2803
msgid "Duration"
msgstr ""
-#: src/components/compose.jsx:2823
+#: src/components/compose.jsx:2834
msgid "Remove poll"
msgstr ""
-#: src/components/compose.jsx:3037
+#: src/components/compose.jsx:3048
msgid "Search accounts"
msgstr ""
-#: src/components/compose.jsx:3091
+#: src/components/compose.jsx:3102
#: src/components/generic-accounts.jsx:227
msgid "Error loading accounts"
msgstr ""
-#: src/components/compose.jsx:3234
+#: src/components/compose.jsx:3245
msgid "Custom emojis"
msgstr ""
-#: src/components/compose.jsx:3254
+#: src/components/compose.jsx:3265
msgid "Search emoji"
msgstr ""
-#: src/components/compose.jsx:3285
+#: src/components/compose.jsx:3296
msgid "Error loading custom emojis"
msgstr ""
-#: src/components/compose.jsx:3296
+#: src/components/compose.jsx:3307
msgid "Recently used"
msgstr ""
-#: src/components/compose.jsx:3297
+#: src/components/compose.jsx:3308
msgid "Others"
msgstr ""
-#: src/components/compose.jsx:3335
+#: src/components/compose.jsx:3346
msgid "{0} more…"
msgstr ""
-#: src/components/compose.jsx:3473
+#: src/components/compose.jsx:3486
msgid "Search GIFs"
msgstr ""
-#: src/components/compose.jsx:3488
+#: src/components/compose.jsx:3501
msgid "Powered by GIPHY"
msgstr ""
-#: src/components/compose.jsx:3496
+#: src/components/compose.jsx:3509
msgid "Type to search GIFs"
msgstr ""
-#: src/components/compose.jsx:3594
+#: src/components/compose.jsx:3607
#: src/components/media-modal.jsx:461
#: src/components/timeline.jsx:889
msgid "Previous"
msgstr ""
-#: src/components/compose.jsx:3612
+#: src/components/compose.jsx:3625
#: src/components/media-modal.jsx:480
#: src/components/timeline.jsx:906
msgid "Next"
msgstr ""
-#: src/components/compose.jsx:3629
+#: src/components/compose.jsx:3642
msgid "Error loading GIFs"
msgstr ""
diff --git a/vite.config.js b/vite.config.js
index d39a49f58d..f4cd113465 100644
--- a/vite.config.js
+++ b/vite.config.js
@@ -1,6 +1,7 @@
import { execSync } from 'child_process';
import fs from 'fs';
-import { resolve } from 'path';
+import { createRequire } from 'module';
+import { dirname, join, resolve } from 'path';
import { lingui } from '@lingui/vite-plugin';
import preact from '@preact/preset-vite';
@@ -40,6 +41,66 @@ const rollbarCode = fs.readFileSync(
'utf-8',
);
+let emojiData;
+{
+ // import.meta.resolve requires Node 20.6.0/18.19.0, cannot use that yet.
+ const require = createRequire(import.meta.url);
+ const basePath = dirname(dirname(
+ require.resolve("emojibase-data/en/data.json")
+ ));
+
+ emojiData = ALL_LOCALES.map((lang) => {
+ const emojiLang = [
+ lang.toLowerCase(),
+ lang.replace(/-.*/, '').toLowerCase(),
+ 'en',
+ ].find((lang) => {
+ try {
+ return fs.statSync(join(basePath, lang)).isDirectory();
+ } catch (error) {
+ return false;
+ }
+ });
+
+ const messages = JSON.parse(fs.readFileSync(
+ join(basePath, emojiLang, 'messages.json')
+ ));
+ const emojis = JSON.parse(fs.readFileSync(
+ join(basePath, emojiLang, 'compact.json')
+ ));
+ const shortcodes = JSON.parse(fs.readFileSync(
+ join(basePath, emojiLang, 'shortcodes', 'cldr.json')
+ ));
+
+ const data = {
+ groups: messages.groups,
+ subgroups: messages.subgroups,
+ skinTones: messages.skinTones,
+ emojis: emojis.sort((emoji1, emoji2) => emoji1.order - emoji2.order).map((emoji) => {
+ if (emoji.hexcode in shortcodes)
+ emoji.shortcode = shortcodes[emoji.hexcode];
+
+ // Remove unused fields to keep the data compact
+ delete emoji.order;
+ delete emoji.hexcode;
+
+ if (emoji.skins) {
+ emoji.skins = emoji.skins.sort((skin1, skin2) => skin1.order - skin2.order)
+ .map((skin) => skin.unicode);
+ }
+
+ return emoji;
+ }),
+ };
+
+ return generateFile({
+ type: 'json',
+ output: `./assets/emojis/${lang}.json`,
+ data,
+ });
+ });
+}
+
// https://vitejs.dev/config/
export default defineConfig({
base: './',
@@ -154,6 +215,7 @@ export default defineConfig({
detailed: true,
brotli: true,
}),
+ ...emojiData,
],
build: {
sourcemap: true,
From 81dabb440f57ff40c8a7f417589d009d1ea76e57 Mon Sep 17 00:00:00 2001
From: Wladimir Palant <374261+palant@users.noreply.github.com>
Date: Tue, 3 Dec 2024 06:33:07 +0100
Subject: [PATCH 2/3] Attampted to resolve prettier warnings
---
src/components/compose.jsx | 32 +++++++++++++++++++++-----------
1 file changed, 21 insertions(+), 11 deletions(-)
diff --git a/src/components/compose.jsx b/src/components/compose.jsx
index 6d0c3956c2..d0f955ec38 100644
--- a/src/components/compose.jsx
+++ b/src/components/compose.jsx
@@ -1789,14 +1789,15 @@ function autoResizeTextarea(textarea) {
async function _getCustomEmojis(lang, instance, masto) {
const [unicodeEmojis, customEmojis] = await Promise.all([
fetch(`./assets/emojis/${lang}.json`).then((r) => r.json()),
- masto.v1.customEmojis.list()
+ masto.v1.customEmojis.list(),
]);
for (let emoji of unicodeEmojis.emojis)
if (emoji.group in unicodeEmojis.groups)
emoji.category = unicodeEmojis.groups[emoji.group].message;
- const visibleEmojis = customEmojis.filter((e) => e.visibleInPicker)
+ const visibleEmojis = customEmojis
+ .filter((e) => e.visibleInPicker)
.concat(unicodeEmojis.emojis);
const searcher = new Fuse(visibleEmojis, {
keys: ['shortcode', 'label', 'tags'],
@@ -1876,14 +1877,15 @@ const Textarea = forwardRef((props, ref) => {
let html = '';
results.forEach(({ item: emoji }) => {
const { unicode, shortcode, url } = emoji;
- const presentation = unicode ? unicode :
- `
`;
+ const presentation = unicode
+ ? unicode
+ : `
`;
html += `
${presentation}
- ${encodeHTML(shortcode || "")}
+ ${encodeHTML(shortcode || '')}
`;
});
html += `${t`More…`}`;
@@ -3126,7 +3128,11 @@ function CustomEmojisModal({
setUIState('loading');
(async () => {
try {
- const [emojis, searcher] = await getCustomEmojis(i18n.locale, instance, masto);
+ const [emojis, searcher] = await getCustomEmojis(
+ i18n.locale,
+ instance,
+ masto,
+ );
console.log('emojis', emojis);
searcherRef.current = searcher;
setCustomEmojis(emojis);
@@ -3255,7 +3261,9 @@ function CustomEmojisModal({
e.preventDefault();
const emoji = matches[0];
if (emoji) {
- onSelectEmoji(emoji.unicode ? emoji.unicode : `:${emoji.shortcode}:`);
+ onSelectEmoji(
+ emoji.unicode ? emoji.unicode : `:${emoji.shortcode}:`,
+ );
}
}}
>
@@ -3281,7 +3289,9 @@ function CustomEmojisModal({
{
- onSelectEmoji(emoji.unicode ? emoji.unicode : `:${emoji.shortcode}:`);
+ onSelectEmoji(
+ emoji.unicode ? emoji.unicode : `:${emoji.shortcode}:`,
+ );
}}
showCode
/>
@@ -3381,7 +3391,7 @@ const CustomEmojiButton = memo(({ emoji, onClick, showCode }) => {
onPointerEnter={addEdges}
onFocus={addEdges}
>
- {emoji.unicode && <>{emoji.unicode}> || (
+ {(emoji.unicode && <>{emoji.unicode}>) || (
{!!emoji.staticUrl && (
Date: Tue, 3 Dec 2024 15:20:32 +0100
Subject: [PATCH 3/3] Another attempt to satisfy prettier requirements
---
src/components/compose.jsx | 4 +-
src/locales/en.po | 102 ++++++++++++++++++-------------------
2 files changed, 54 insertions(+), 52 deletions(-)
diff --git a/src/components/compose.jsx b/src/components/compose.jsx
index d0f955ec38..d9a8148811 100644
--- a/src/components/compose.jsx
+++ b/src/components/compose.jsx
@@ -1883,7 +1883,9 @@ const Textarea = forwardRef((props, ref) => {
url,
)}" width="16" height="16" alt="" loading="lazy" />`;
html += `
-
+
${presentation}
${encodeHTML(shortcode || '')}
`;
diff --git a/src/locales/en.po b/src/locales/en.po
index 00d2ee995e..34375472e9 100644
--- a/src/locales/en.po
+++ b/src/locales/en.po
@@ -104,7 +104,7 @@ msgstr ""
#: src/components/account-info.jsx:429
#: src/components/account-info.jsx:1120
-#: src/components/compose.jsx:2602
+#: src/components/compose.jsx:2606
#: src/components/media-alt-modal.jsx:45
#: src/components/media-modal.jsx:357
#: src/components/status.jsx:1737
@@ -400,10 +400,10 @@ msgstr ""
#: src/components/account-info.jsx:2094
#: src/components/account-sheet.jsx:37
#: src/components/compose.jsx:859
-#: src/components/compose.jsx:2558
-#: src/components/compose.jsx:3031
-#: src/components/compose.jsx:3239
-#: src/components/compose.jsx:3471
+#: src/components/compose.jsx:2562
+#: src/components/compose.jsx:3035
+#: src/components/compose.jsx:3247
+#: src/components/compose.jsx:3483
#: src/components/drafts.jsx:58
#: src/components/embed-modal.jsx:12
#: src/components/generic-accounts.jsx:142
@@ -681,7 +681,7 @@ msgid "Mark media as sensitive"
msgstr ""
#: src/components/compose.jsx:1381
-#: src/components/compose.jsx:3089
+#: src/components/compose.jsx:3093
#: src/components/shortcuts-settings.jsx:715
#: src/pages/list.jsx:359
msgid "Add"
@@ -713,172 +713,172 @@ msgstr ""
msgid "Failed to download GIF"
msgstr ""
-#: src/components/compose.jsx:1889
-#: src/components/compose.jsx:1966
+#: src/components/compose.jsx:1893
+#: src/components/compose.jsx:1970
#: src/components/nav-menu.jsx:238
msgid "More…"
msgstr ""
-#: src/components/compose.jsx:2371
+#: src/components/compose.jsx:2375
msgid "Uploaded"
msgstr ""
-#: src/components/compose.jsx:2384
+#: src/components/compose.jsx:2388
msgid "Image description"
msgstr ""
-#: src/components/compose.jsx:2385
+#: src/components/compose.jsx:2389
msgid "Video description"
msgstr ""
-#: src/components/compose.jsx:2386
+#: src/components/compose.jsx:2390
msgid "Audio description"
msgstr ""
-#: src/components/compose.jsx:2422
-#: src/components/compose.jsx:2442
+#: src/components/compose.jsx:2426
+#: src/components/compose.jsx:2446
msgid "File size too large. Uploading might encounter issues. Try reduce the file size from {0} to {1} or lower."
msgstr ""
-#: src/components/compose.jsx:2434
-#: src/components/compose.jsx:2454
+#: src/components/compose.jsx:2438
+#: src/components/compose.jsx:2458
msgid "Dimension too large. Uploading might encounter issues. Try reduce dimension from {0}×{1}px to {2}×{3}px."
msgstr ""
-#: src/components/compose.jsx:2462
+#: src/components/compose.jsx:2466
msgid "Frame rate too high. Uploading might encounter issues."
msgstr ""
-#: src/components/compose.jsx:2522
-#: src/components/compose.jsx:2772
+#: src/components/compose.jsx:2526
+#: src/components/compose.jsx:2776
#: src/components/shortcuts-settings.jsx:726
#: src/pages/catchup.jsx:1074
#: src/pages/filters.jsx:412
msgid "Remove"
msgstr ""
-#: src/components/compose.jsx:2539
+#: src/components/compose.jsx:2543
#: src/compose.jsx:83
msgid "Error"
msgstr ""
-#: src/components/compose.jsx:2564
+#: src/components/compose.jsx:2568
msgid "Edit image description"
msgstr ""
-#: src/components/compose.jsx:2565
+#: src/components/compose.jsx:2569
msgid "Edit video description"
msgstr ""
-#: src/components/compose.jsx:2566
+#: src/components/compose.jsx:2570
msgid "Edit audio description"
msgstr ""
-#: src/components/compose.jsx:2611
-#: src/components/compose.jsx:2660
+#: src/components/compose.jsx:2615
+#: src/components/compose.jsx:2664
msgid "Generating description. Please wait…"
msgstr ""
-#: src/components/compose.jsx:2631
+#: src/components/compose.jsx:2635
msgid "Failed to generate description: {0}"
msgstr ""
-#: src/components/compose.jsx:2632
+#: src/components/compose.jsx:2636
msgid "Failed to generate description"
msgstr ""
-#: src/components/compose.jsx:2644
-#: src/components/compose.jsx:2650
-#: src/components/compose.jsx:2696
+#: src/components/compose.jsx:2648
+#: src/components/compose.jsx:2654
+#: src/components/compose.jsx:2700
msgid "Generate description…"
msgstr ""
-#: src/components/compose.jsx:2683
+#: src/components/compose.jsx:2687
msgid "Failed to generate description{0}"
msgstr ""
-#: src/components/compose.jsx:2698
+#: src/components/compose.jsx:2702
msgid "({0}) <0>— experimental0>"
msgstr ""
-#: src/components/compose.jsx:2717
+#: src/components/compose.jsx:2721
msgid "Done"
msgstr ""
-#: src/components/compose.jsx:2753
+#: src/components/compose.jsx:2757
msgid "Choice {0}"
msgstr ""
-#: src/components/compose.jsx:2800
+#: src/components/compose.jsx:2804
msgid "Multiple choices"
msgstr ""
-#: src/components/compose.jsx:2803
+#: src/components/compose.jsx:2807
msgid "Duration"
msgstr ""
-#: src/components/compose.jsx:2834
+#: src/components/compose.jsx:2838
msgid "Remove poll"
msgstr ""
-#: src/components/compose.jsx:3048
+#: src/components/compose.jsx:3052
msgid "Search accounts"
msgstr ""
-#: src/components/compose.jsx:3102
+#: src/components/compose.jsx:3106
#: src/components/generic-accounts.jsx:227
msgid "Error loading accounts"
msgstr ""
-#: src/components/compose.jsx:3245
+#: src/components/compose.jsx:3253
msgid "Custom emojis"
msgstr ""
-#: src/components/compose.jsx:3265
+#: src/components/compose.jsx:3275
msgid "Search emoji"
msgstr ""
-#: src/components/compose.jsx:3296
+#: src/components/compose.jsx:3308
msgid "Error loading custom emojis"
msgstr ""
-#: src/components/compose.jsx:3307
+#: src/components/compose.jsx:3319
msgid "Recently used"
msgstr ""
-#: src/components/compose.jsx:3308
+#: src/components/compose.jsx:3320
msgid "Others"
msgstr ""
-#: src/components/compose.jsx:3346
+#: src/components/compose.jsx:3358
msgid "{0} more…"
msgstr ""
-#: src/components/compose.jsx:3486
+#: src/components/compose.jsx:3498
msgid "Search GIFs"
msgstr ""
-#: src/components/compose.jsx:3501
+#: src/components/compose.jsx:3513
msgid "Powered by GIPHY"
msgstr ""
-#: src/components/compose.jsx:3509
+#: src/components/compose.jsx:3521
msgid "Type to search GIFs"
msgstr ""
-#: src/components/compose.jsx:3607
+#: src/components/compose.jsx:3619
#: src/components/media-modal.jsx:461
#: src/components/timeline.jsx:889
msgid "Previous"
msgstr ""
-#: src/components/compose.jsx:3625
+#: src/components/compose.jsx:3637
#: src/components/media-modal.jsx:480
#: src/components/timeline.jsx:906
msgid "Next"
msgstr ""
-#: src/components/compose.jsx:3642
+#: src/components/compose.jsx:3654
msgid "Error loading GIFs"
msgstr ""