Skip to content

Commit

Permalink
feat: early third party emotes rendering support
Browse files Browse the repository at this point in the history
does not support modifiers or zero-width emotes
  • Loading branch information
6lr61 committed Sep 18, 2024
1 parent 649212d commit cba9780
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 19 deletions.
4 changes: 2 additions & 2 deletions src/components/Emotes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ export default function Emotes(): ReactElement {
<ul className="flex flex-row flex-wrap gap-1">
{Object.entries(bttvEmotes).map(([name, emote]) => (
<li key={name}>
<img alt={name} src={emote.small.file} {...emote.small} />
<img alt={name} {...emote.small} />
</li>
))}
</ul>
<h2>SevenTV</h2>
<ul className="flex flex-row flex-wrap gap-1">
{Object.entries(sevenTvEmotes).map(([name, emote]) => (
<li key={name}>
<img alt={name} src={emote.small.file} {...emote.small} />
<img alt={name} {...emote.small} />
</li>
))}
</ul>
Expand Down
20 changes: 17 additions & 3 deletions src/components/Message.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import {
useThirdPartyEmotes,
type Fragment,
} from "../hooks/useThirdPartyEmotes";
import type { ChatMessage } from "../hooks/useTwitchChat";
import type { ChatFragment } from "../utils/event-sub/events/chat/message";
import BadgeList from "./BadgeList";
import ElapsedTime from "./ElapsedTime";
import MentionSegment from "./MentionSegment";
Expand Down Expand Up @@ -28,7 +31,7 @@ function colorToRgba(color?: string): string | undefined {
}

function fragmentToComponent(
fragment: ChatFragment,
fragment: Fragment,
index: number
): React.ReactElement | undefined {
const key = `fragment-${index.toString()}`;
Expand All @@ -40,12 +43,23 @@ function fragmentToComponent(
return <TwitchEmote key={key} fragment={fragment} />;
case "mention":
return <MentionSegment key={key} text={fragment.text} />;
case "7tv-emote":
case "bttv-emote":
return (
<img
key={key}
className="relative inline-block -my-2"
{...fragment.small}
/>
);
default:
return;
}
}

export default function Message({ message }: Props): React.ReactElement {
const fragments = useThirdPartyEmotes(message.message.fragments);

return (
<article className="flex flex-row gap-1">
<ProfilePicture login={message.chatter_user_login} />
Expand All @@ -61,7 +75,7 @@ export default function Message({ message }: Props): React.ReactElement {
</header>
{message.reply && <Reply message={message.reply} />}
<section className="h-full bg-slate-800 break-words px-2 py-1">
{message.message.fragments.map((fragment, index) =>
{fragments.map((fragment, index) =>
fragmentToComponent(fragment, index)
)}
</section>
Expand Down
10 changes: 5 additions & 5 deletions src/hooks/useBttvEmotes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ interface BetterTTVEmote {
height?: number;
}

interface BetterTTVEmoteFragment {
export interface BetterTTVEmoteFragment {
type: "bttv-emote";
text: string;
animated: boolean;
Expand All @@ -32,12 +32,12 @@ interface BetterTTVEmoteFragment {
name: string;
};
small: {
file: string;
src: string;
height: number;
width: number;
};
big: {
file: string;
src: string;
height: number;
width: number;
};
Expand All @@ -62,12 +62,12 @@ function makeBttvFragments(
name: emote.user.displayName,
},
small: {
file: `https://cdn.betterttv.net/emote/${emote.id}/1x.webp`,
src: `https://cdn.betterttv.net/emote/${emote.id}/1x.webp`,
height: emote.height ?? 28,
width: emote.width ?? 28,
},
big: {
file: `https://cdn.betterttv.net/emote/${emote.id}/3x.webp`,
src: `https://cdn.betterttv.net/emote/${emote.id}/3x.webp`,
height: 3 * (emote.height ?? 28),
width: 3 * (emote.width ?? 28),
},
Expand Down
18 changes: 9 additions & 9 deletions src/hooks/useSevenTvEmotes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ interface Connection {
emote_set: EmoteSet | null;
}

interface SevenTVEmoteFragment {
export interface SevenTVEmoteFragment {
type: "7tv-emote";
text: string;
animated: boolean;
Expand All @@ -154,14 +154,14 @@ interface SevenTVEmoteFragment {
name: string;
};
small: {
file: string;
height?: number;
width?: number;
src: string;
height: number;
width: number;
};
big: {
file: string;
height?: number;
width?: number;
src: string;
height: number;
width: number;
};
}

Expand All @@ -182,10 +182,10 @@ function filterEmote(emote: SevenTvEmoteModel): boolean {
function sizeOf(
filename: string,
files: SevenTvEmoteModel["data"]["host"]["files"]
): { height?: number; width?: number } {
): { height: number; width: number } {
const file = files.find(({ name }) => name === filename);

return { height: file?.height, width: file?.width };
return { height: file?.height ?? 32, width: file?.width ?? 32 };
}

function makeSevenTvFragments(
Expand Down
55 changes: 55 additions & 0 deletions src/hooks/useThirdPartyEmotes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { useContext } from "react";
import { AuthContext } from "../contexts/auth-state/AuthContext";
import { useBttvEmotes, type BetterTTVEmoteFragment } from "./useBttvEmotes";
import {
useSevenTvEmotes,
type SevenTVEmoteFragment,
} from "./useSevenTvEmotes";
import type { ChatFragment } from "../utils/event-sub/events/chat/message";

export type Fragment =
| ChatFragment
| BetterTTVEmoteFragment
| SevenTVEmoteFragment;

export function useThirdPartyEmotes(fragments: ChatFragment[]): Fragment[] {
const { authState } = useContext(AuthContext);
const bttvEmotes = useBttvEmotes(authState?.user.id);
const sevenTvEmotes = useSevenTvEmotes(authState?.user.id);
const emotes = { ...bttvEmotes, ...sevenTvEmotes };
const names = Object.keys(emotes);

return fragments.flatMap((fragment) => {
if (fragment.type !== "text") {
return fragment;
}

const found = [...fragment.text.matchAll(/[!:\w]+/g)].filter(
({ 0: match }) => names.includes(match)
);

/// Some text Kappa Kappa and such
const subFragments: Fragment[] = [];
let start = 0;
for (const { 0: name, index } of found) {
if (start < index) {
subFragments.push({
type: "text",
text: fragment.text.slice(start, index),
});
}

subFragments.push(emotes[name]);
start = index + name.length;
}

if (start < fragment.text.length) {
subFragments.push({
type: "text",
text: fragment.text.slice(start),
});
}

return subFragments;
});
}

0 comments on commit cba9780

Please sign in to comment.