diff --git a/LOTUS_FEATURES.md b/LOTUS_FEATURES.md index 5efe081ae..a59109d55 100644 --- a/LOTUS_FEATURES.md +++ b/LOTUS_FEATURES.md @@ -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 diff --git a/src/app/components/emoji-board/EmojiBoard.tsx b/src/app/components/emoji-board/EmojiBoard.tsx index 1a591e931..52a18758f 100644 --- a/src/app/components/emoji-board/EmojiBoard.tsx +++ b/src/app/components/emoji-board/EmojiBoard.tsx @@ -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(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(); diff --git a/src/app/features/settings/account/Profile.tsx b/src/app/features/settings/account/Profile.tsx index cf2aade5c..a2f3f930c 100644 --- a/src/app/features/settings/account/Profile.tsx +++ b/src/app/features/settings/account/Profile.tsx @@ -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)} /> }