refactor and fix all references to emojis

This commit is contained in:
Moon.eth
2026-07-30 17:59:15 +09:00
parent b814d79f19
commit 49c9c2ba57
28 changed files with 422 additions and 77 deletions
+3
View File
@@ -261,6 +261,9 @@ export interface Notification {
created_at: string
account: Account
status?: Status | null
/** Pleroma/Akkoma emoji-reaction notification payload. */
emoji?: string | null
emoji_url?: string | null
}
export interface Context {
+25 -1
View File
@@ -1,4 +1,4 @@
import type { Notification } from './api/types'
import type { CustomEmoji, Notification } from './api/types'
import { toPlainText } from './util/html'
import { displayNameOf, profilePath } from './util/profile'
@@ -53,6 +53,30 @@ export interface NotificationPresentation {
href: string
}
export interface NotificationEmoji {
text: string
emojis: CustomEmoji[]
}
/** Turn Pleroma's separate reaction name/URL fields into EmojiText input. */
export function emojiForNotification(notification: Notification): NotificationEmoji | null {
const text = notification.emoji?.trim()
if (!text) return null
const shortcode = text.match(/^:([^:]+):$/)?.[1]
if (!shortcode || !notification.emoji_url) return { text, emojis: [] }
return {
text,
emojis: [
{
shortcode,
url: notification.emoji_url,
static_url: notification.emoji_url,
visible_in_picker: false,
},
],
}
}
const MESSAGE: Record<string, string> = {
mention: 'mentioned you in an entry',
status: 'posted a new entry',
+19 -3
View File
@@ -119,6 +119,15 @@ export function escapeHtml(value: string): string {
* Runs on the *sanitized* string and only injects `<img>` with a URL taken from
* the emoji list, so it cannot reintroduce markup from the original content.
*/
export function safeCustomEmojiUrl(value: string): string | null {
try {
const url = new URL(value, window.location.href)
return url.protocol === 'http:' || url.protocol === 'https:' ? url.href : null
} catch {
return null
}
}
function applyEmojis(html: string, emojis: CustomEmoji[] | undefined): string {
if (!emojis?.length) return html
const table = new Map(emojis.map((emoji) => [emoji.shortcode, emoji]))
@@ -128,7 +137,9 @@ function applyEmojis(html: string, emojis: CustomEmoji[] | undefined): string {
const replaced = text.replace(/:([a-zA-Z0-9_+-]+):/g, (whole, shortcode: string) => {
const emoji = table.get(shortcode)
if (!emoji) return whole
return `<img class="custom-emoji" src="${escapeHtml(emoji.url)}" alt=":${escapeHtml(
const url = safeCustomEmojiUrl(emoji.url)
if (!url) return whole
return `<img class="custom-emoji" src="${escapeHtml(url)}" alt=":${escapeHtml(
shortcode,
)}:" title=":${escapeHtml(shortcode)}:" draggable="false" />`
})
@@ -213,7 +224,12 @@ export function toPlainText(source: string | null | undefined): string {
return (container.textContent ?? '').replace(/\s+/g, ' ').trim()
}
/** Emoji-substituted display name, safe for `{@html}`. */
/** Emoji-substituted plain text, escaped and safe for `{@html}`. */
export function renderEmojiText(text: string, emojis: CustomEmoji[] | undefined): string {
return applyEmojis(escapeHtml(text), emojis)
}
/** Backward-compatible semantic name for existing display-name callers. */
export function renderDisplayName(name: string, emojis: CustomEmoji[] | undefined): string {
return applyEmojis(escapeHtml(name), emojis)
return renderEmojiText(name, emojis)
}