feat(expressions): pickers and upload for emojis and stickers

Completes the feature: the tables existed but nothing could be put in them.

Space settings gain an Emojis & Stickers panel behind MANAGE_SPACE, with a
512KB ceiling — both are fetched on every message that uses them, so weight
matters more than fidelity. The suggested name is pre-normalised so the common
case needs no typing, and a name collision reports itself distinctly from an
upload failure: the corrective action is different.

Custom emojis join the emoji picker as their own category. They have no native
character, so selecting one inserts :name: — the same text the renderer
resolves back to an image, which also means copying a message yields something
that still reads.

Stickers get a picker tab that only appears inside a space, since that is where
they exist, and send immediately on click: a sticker is the whole message, so
parking it in the composer to await Enter would make no sense.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-01 18:25:10 -03:00
co-authored by Claude Opus 5
parent 7d003021d1
commit 7f08384372
10 changed files with 392 additions and 12 deletions
+20
View File
@@ -56,6 +56,26 @@ export const en = {
'accountMenu.copyId': 'Copy User ID',
'accountMenu.copied': 'Copied',
// Custom emojis and stickers
'expressions.title': 'Emojis & Stickers',
'expressions.emojis': 'Emojis',
'expressions.stickers': 'Stickers',
'expressions.addEmoji': 'Add emoji',
'expressions.addSticker': 'Add sticker',
'expressions.emptyEmojis': 'No custom emojis yet.',
'expressions.emptyStickers': 'No stickers yet.',
'expressions.namePrompt': 'Name',
'expressions.nameHintEmoji': 'Letters, numbers and underscore. Used as :name: in messages.',
'expressions.confirm': 'Add',
'expressions.cancel': 'Cancel',
'expressions.remove': 'Remove',
'expressions.tooLarge': 'Image must be under 512 KB.',
'expressions.uploadFailed': 'Could not add that image. Try another one.',
'expressions.nameTaken': 'That name is already in use in this server.',
'expressions.uploading': 'Uploading…',
'expressions.pickerTitle': 'Stickers',
'expressions.pickerEmpty': 'This server has no stickers yet.',
// Search
'search.placeholder': 'Search messages…',
'search.filters': 'Filters',