Stop drawing 7TV words as pictures
The window half of removing #195. The substitution in the formatter, the per-buffer map behind it, and the state and context that carried it. Kick's chat does not render these, so a message that read as pictures here read as words everywhere else - which made this client's view of a conversation quietly different from the one the channel was actually having. Kick's own `[emote:id:name]` is untouched. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
+1
-1
Submodule nobilis updated: 0a0ee0ff81...7e15621f43
@@ -141,8 +141,6 @@ export interface RowContext {
|
||||
pinned: string[]
|
||||
/** Colour is how IRC has always been written, and also how a bot shouts. */
|
||||
ircColours: boolean
|
||||
/** Kick's 7TV emotes for this conversation, as word -> picture. */
|
||||
wordEmotes: Record<string, string>
|
||||
}
|
||||
|
||||
/** The above, for one conversation. Called once per list, not once per row. */
|
||||
@@ -153,7 +151,6 @@ export function useRowContext(bufferId: string): RowContext {
|
||||
const kind = useChat((s) => s.buffers).find((b) => b.id === bufferId)?.kind
|
||||
const pinned = useChat((s) => s.pinnedMessages)[bufferId]
|
||||
const [ircColours] = usePref<boolean>('irc.renderColours', true)
|
||||
const wordEmotes = useChat((s) => s.kickWordEmotes)[bufferId]
|
||||
return useMemo(
|
||||
() => ({
|
||||
permissions: permissions ?? {},
|
||||
@@ -161,10 +158,9 @@ export function useRowContext(bufferId: string): RowContext {
|
||||
emoji: emoji ?? [],
|
||||
inDirectMessage: kind === 'dm',
|
||||
pinned: pinned ?? [],
|
||||
ircColours,
|
||||
wordEmotes: wordEmotes ?? {}
|
||||
ircColours
|
||||
}),
|
||||
[permissions, smilies, emoji, kind, pinned, ircColours, wordEmotes]
|
||||
[permissions, smilies, emoji, kind, pinned, ircColours]
|
||||
)
|
||||
}
|
||||
|
||||
@@ -332,8 +328,7 @@ function MessageRowBody({
|
||||
smilies: smilieIndex,
|
||||
emoji: bufferEmoji,
|
||||
inDirectMessage,
|
||||
ircColours,
|
||||
wordEmotes
|
||||
ircColours
|
||||
} = shared
|
||||
const sniffed = useSniffedTypes()
|
||||
|
||||
@@ -409,10 +404,7 @@ function MessageRowBody({
|
||||
channels,
|
||||
// Only IRC's own traffic is drawn with IRC's codes. Elsewhere they
|
||||
// are stripped, which is what the formatter does when not told.
|
||||
ircFormatting: service === 'irc' && ircColours ? 'render' : 'strip',
|
||||
// Kick's 7TV emotes, which only mean anything in the channel whose
|
||||
// set they came from.
|
||||
wordEmotes
|
||||
ircFormatting: service === 'irc' && ircColours ? 'render' : 'strip'
|
||||
}),
|
||||
media,
|
||||
codeBlocks,
|
||||
|
||||
@@ -225,13 +225,6 @@ export interface FormatOptions {
|
||||
* bold.
|
||||
*/
|
||||
ircFormatting?: 'render' | 'strip'
|
||||
/**
|
||||
* Words that stand for pictures in this conversation - Kick's 7TV emotes.
|
||||
*
|
||||
* Per conversation, unlike every other emote here: these carry no id, so a
|
||||
* word is only an emote in the channel whose set contains it.
|
||||
*/
|
||||
wordEmotes?: Record<string, string>
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -427,26 +420,6 @@ export function formatMessage(text: string, opts: FormatOptions = {}): string {
|
||||
stow(`<img src="${kickEmoteUrl(id)}" class="custom-emoji" alt=":${escapeHtml(name)}:">`)
|
||||
)
|
||||
|
||||
// 7TV emotes, which are not tokens at all - a bare word that stands for a
|
||||
// picture in this channel and is ordinary text in the next. So unlike the
|
||||
// two above, this needs the channel's own set, and a word not in it is left
|
||||
// exactly as it was.
|
||||
//
|
||||
// Whole words only, and case-sensitively: 7TV names are chosen to be typed
|
||||
// and collide with ordinary English constantly - a channel with an emote
|
||||
// called "ok" or "no" would otherwise turn half the conversation into
|
||||
// pictures. The boundary is whitespace rather than \b, because names
|
||||
// routinely contain digits and underscores and \b would match inside them.
|
||||
if (opts.wordEmotes && Object.keys(opts.wordEmotes).length > 0) {
|
||||
const words = opts.wordEmotes
|
||||
out = out.replace(/(^|\s)([^\s]{1,64})(?=\s|$)/g, (whole, lead: string, word: string) => {
|
||||
const url = Object.prototype.hasOwnProperty.call(words, word) ? words[word] : undefined
|
||||
return url
|
||||
? `${lead}${stow(`<img src="${url}" class="custom-emoji" alt=":${escapeHtml(word)}:">`)}`
|
||||
: whole
|
||||
})
|
||||
}
|
||||
|
||||
// Discord channel links. What arrives is `<#1393001234568164748>` and
|
||||
// nothing else - no name anywhere in the payload - so this is the only
|
||||
// chance to make it read as the "#general" the sender saw when they typed
|
||||
|
||||
@@ -452,14 +452,6 @@ export interface ChatState {
|
||||
* most of what a viewer wants to know, were nowhere.
|
||||
*/
|
||||
kickStreams: Record<string, KickStream>
|
||||
/**
|
||||
* Kick's 7TV emotes, by buffer, as word -> picture.
|
||||
*
|
||||
* Per buffer because that is what they are: a 7TV emote is a bare word that
|
||||
* stands for a picture in one channel and means nothing in the next, unlike
|
||||
* Kick's own `[emote:id:name]`, which carries its id and resolves anywhere.
|
||||
*/
|
||||
kickWordEmotes: Record<string, Record<string, string>>
|
||||
/** The poll and prediction running in each channel, by `cardSlot`. */
|
||||
livePolls: Record<string, LiveCard>
|
||||
/**
|
||||
@@ -601,7 +593,6 @@ const INITIAL: ChatState = {
|
||||
openThread: null,
|
||||
matrixPermissions: {},
|
||||
kickStreams: {},
|
||||
kickWordEmotes: {},
|
||||
livePolls: {},
|
||||
pinnedByBuffer: {},
|
||||
reviewCard: null,
|
||||
@@ -2395,14 +2386,6 @@ export class ChatStore {
|
||||
})
|
||||
break
|
||||
|
||||
// A Kick channel's 7TV emotes: words that are pictures in this room and
|
||||
// ordinary text everywhere else, which is why they are kept per buffer.
|
||||
case 'kickWordEmotes':
|
||||
this.set({
|
||||
kickWordEmotes: { ...this.state.kickWordEmotes, [data.bufferId]: data.emotes }
|
||||
})
|
||||
break
|
||||
|
||||
// Somebody left this room unread, here or on another client.
|
||||
case 'markedUnread':
|
||||
this.set({
|
||||
|
||||
Reference in New Issue
Block a user