Fix custom emoji rendering#33
Merged
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Root cause
AccountDisplayName.vue(the emoji-aware renderer) wasn't used consistently. Some call sites went through it; most didn't. Its hardcoded styling also made it awkward to drop into places that wanted different typography — so people interpolated manually and the shortcodes leaked.Approach
Make the wrong thing hard to reach. One pure helper, reused everywhere.
utils/customEmojis.ts— newrenderCustomEmojis(html, emojis, imgClass?). Pipeline: sanitize → dedupe emojis (first-wins) → substitute → strip unresolved shortcodes. HTML-escapes the alt attribute (fixes a theoretical XSS when a shortcode contains"). Single source of truth.AccountDisplayName.vuerefactored to minimal API:name,emojis?,class?. Dropped theasLink/hreflink-wrapper (caller's concern) and hardcoded font classes (caller owns visual style). Uses the shared helper.RichText.vue— inline loop deleted, calls the same helper. Public API unchanged.usePageHeadercomposable anduseNavigationStore(web + mobile) both extended withtitleEmojis.MobileHeader,DesktopFeedHeader,AppHeaderrender the title viarenderCustomEmojis+v-html. Callers that set an account-based title (@[acct].vue,@[acct]/[id]/index.vue,messages/[id].vue,messages/new.vue, mobileMessageThread) now pass emojis.Fixed
<AccountDisplayName>:ProfileInformation(profile display name),Status.vuex3 (author header, parent-context preview, reblog indicator),StatusQuote,AccountList,ShareStatusFormx2,ChatHeader,MentionList,Take,search.vue,messages/new.vue. Each caller supplies its own class string.nameandvalue(ProfileInformation.vue) — render via helper withaccount.emojis.extractTextstill strips HTML first; emojis are substituted on the plain result.PollDisplay.vue) —option.titlerendered via helper withoption.emojis(masto.js types put emojis per-option).MessageBubble.vue) — addedsenderEmojisprop; caller threads it through from the conversation account. AlsosharedStatus.authorEmojisfor the shared-post preview shape.ChatListItem.vue) — plain-text context, strips unresolved shortcodes before joining.Avatar.vueandProfileHeader.vuenow hide<img>on@errorso the muted-circle / gradient fallback shows cleanly instead of a broken-image icon + alt text.Supporting fixes from the same audit
ALTpill that opens a bottom-sheet dialog (long descriptions no longer cover the image).Status.vuevertical rhythm:pt-3 pb-1when actions render (balances the button'spb-2tap target), andpt-1when a Pinned/reblog indicator is present so the label reads as a caption attached to the post.Tests
utils/__tests__/customEmojis.test.ts— 9 cases covering resolved substitution, unresolved strip, timestamp-unaffected, dedupe of duplicate shortcodes, alt-attribute HTML escaping, empty cases, ReDoS-style input, custom img class.Exports
renderCustomEmojisandstripUnresolvedEmojiShortcodesre-exported from@repo/uiso app layers can use them for plain-text contexts (nav page titles, aria-announcements, comma-joined group names).