fix(status): unicode-only emoji picker (custom emojis silently did nothing)
CI / Build & Quality Checks (push) Successful in 11m25s
CI / Trigger Desktop Build (push) Successful in 6s

The status-message emoji picker listed the guild's custom/image-pack emojis, but
clicking one did nothing — the field only wires onEmojiSelect (unicode), not
onCustomEmojiSelect, so custom picks were silently dropped (the room composer
works because it wires both). A custom emoji is an mxc image and a status is
plain-text presence status_msg, so it can't render there anyway.

Add an EmojiBoard hideCustomEmojis (unicode-only) mode that zeroes the image
packs (removing pack groups, sidebar icons, and search results) and filters
custom entries out of Recent, and enable it on the status field. Now every emoji
shown actually inserts. Additive prop, default off — no change to other pickers.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-18 15:16:55 -04:00
co-authored by Claude Opus 4.8
parent 0ce5e763ad
commit 539901ec64
3 changed files with 25 additions and 4 deletions
+1
View File
@@ -950,6 +950,7 @@ A presence status selector in the user panel offering five modes:
### Custom Status Message
- Up to 64 characters of free text plus an emoji
- **Emoji picker is unicode-only** (`EmojiBoard hideCustomEmojis`): a status is plain-text presence (`status_msg`) that can't render a custom mxc-image emoji, so the picker hides custom/image-pack emojis (packs, sidebar icons, search, and custom entries in Recent) — every emoji shown actually inserts. (Previously custom emojis were listed but silently did nothing when clicked, since there was no `onCustomEmojiSelect` on this field.)
- Optional auto-clear timer with presets: 30 minutes, 1 hour, 4 hours, 1 day, 3 days, 7 days
- Status is broadcast via `mx.setPresence({ status_msg: ... })`
- Character counter appears at 56/64 characters remaining to warn of the limit
+19 -4
View File
@@ -57,6 +57,9 @@ import { VirtualTile } from '../virtualizer';
const RECENT_GROUP_ID = 'recent_group';
const SEARCH_GROUP_ID = 'search_group';
// Stable empty pack list for hideCustomEmojis (unicode-only) mode — a fresh []
// each render would needlessly re-run the memoized group/search builders.
const NO_IMAGE_PACKS: ImagePack[] = [];
/**
* Lazily pull in the emojibase data (see plugins/emoji `loadEmojiData`). The
@@ -99,6 +102,7 @@ type StickerGroupItem = {
const useGroups = (
tab: EmojiBoardTab,
imagePacks: ImagePack[],
hideCustomEmojis?: boolean,
): [EmojiGroupItem[], StickerGroupItem[]] => {
const mx = useMatrixClient();
@@ -114,7 +118,9 @@ const useGroups = (
g.push({
id: RECENT_GROUP_ID,
name: 'Recent',
items: recentEmojis,
// In unicode-only mode drop recently-used CUSTOM emojis (PackImageReader);
// keep only unicode ones (IEmoji has `unicode`).
items: hideCustomEmojis ? recentEmojis.filter((e) => 'unicode' in e) : recentEmojis,
});
imagePacks.forEach((pack) => {
@@ -139,7 +145,7 @@ const useGroups = (
});
return g;
}, [mx, recentEmojis, labels, imagePacks, tab, loadedEmojiGroups]);
}, [mx, recentEmojis, labels, imagePacks, tab, loadedEmojiGroups, hideCustomEmojis]);
const stickerGroupItems = useMemo(() => {
const g: StickerGroupItem[] = [];
@@ -434,6 +440,11 @@ type EmojiBoardProps = {
onStickerSelect?: (mxc: string, shortcode: string, label: string) => void;
allowTextCustomEmoji?: boolean;
addToRecentEmoji?: boolean;
// Unicode-only mode: hide custom/image-pack emojis (packs, sidebar icons,
// search, and custom entries in Recent). For targets that can only hold plain
// text — e.g. the presence status message, which can't render an mxc image —
// so users only see emojis that actually insert.
hideCustomEmojis?: boolean;
};
export function EmojiBoard({
@@ -447,6 +458,7 @@ export function EmojiBoard({
onStickerSelect,
allowTextCustomEmoji,
addToRecentEmoji = true,
hideCustomEmojis,
}: EmojiBoardProps) {
const mx = useMatrixClient();
@@ -460,8 +472,11 @@ export function EmojiBoard({
const activeGroupIdAtom = useMemo(() => atom<string | undefined>(undefined), []);
const setActiveGroupId = useSetAtom(activeGroupIdAtom);
const setRecentStickers = useSetAtom(recentStickersAtom);
const imagePacks = useRelevantImagePacks(usage, imagePackRooms);
const [emojiGroupItems, stickerGroupItems] = useGroups(tab, imagePacks);
const relevantImagePacks = useRelevantImagePacks(usage, imagePackRooms);
// Unicode-only mode: drop all custom/image packs so the sidebar, group list,
// and search show only insertable unicode emojis.
const imagePacks = hideCustomEmojis ? NO_IMAGE_PACKS : relevantImagePacks;
const [emojiGroupItems, stickerGroupItems] = useGroups(tab, imagePacks, hideCustomEmojis);
const groups = emojiTab ? emojiGroupItems : stickerGroupItems;
const renderItem = useItemRenderer(tab);
const { emojis: loadedEmojis } = useEmojiData();
@@ -648,6 +648,11 @@ function ProfileStatus() {
imagePackRooms={[]}
returnFocusOnDeactivate={false}
onEmojiSelect={handleEmojiSelect}
// A status message is plain-text presence (`status_msg`), so it
// can't hold a custom mxc-image emoji — show unicode only, so every
// emoji in the picker actually inserts (custom ones silently no-op'd
// because there's no onCustomEmojiSelect here).
hideCustomEmojis
requestClose={() => setEmojiAnchor(undefined)}
/>
}