Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/lib/app.svelte.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { SvelteMap } from "svelte/reactivity";
import { handlers } from "./handlers";
import { History } from "./history.svelte";
import { log } from "./log";
import { BadgeManager } from "./managers/badge-manager";
import { ChannelManager } from "./managers/channel-manager";
import { EmoteManager } from "./managers/emote-manager";
import { SplitLayout } from "./split-layout";
Expand Down Expand Up @@ -64,7 +65,7 @@ class App {
/**
* Provider-specific global badges.
*/
public readonly badges = new Map<string, Badge>();
public readonly badges = new BadgeManager();

/**
* 7TV paints.
Expand Down
35 changes: 24 additions & 11 deletions src/lib/commands/built-in/reload-badges.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,34 @@
import { CommandError } from "$lib/errors/command-error";
import { ErrorMessage } from "$lib/errors/messages";
import { defineCommand, parseBool } from "../util";
import { app } from "$lib/app.svelte";
import { defineCommand } from "../util";

export default defineCommand({
provider: "Built-in",
name: "reload-badges",
description: "Reload all badges for the channel and optionally global badges",
args: ["include-global"],
description:
"Reload all badges for the channel and optionally badges from a comma-separated list of providers",
args: ["providers"],
async exec(args, channel) {
const includeGlobal = parseBool(args[0]);
const providers = args[0]?.split(",") ?? [];

if (includeGlobal === null) {
throw new CommandError(ErrorMessage.INVALID_BOOL_ARG);
}
if (providers.includes("all")) {
await app.badges.fetch(true);
} else {
const promises: Promise<unknown>[] = [channel.fetchBadges(true)];

if (providers.includes("twitch")) {
promises.push(app.badges.fetchTwitch(true));
}

if (providers.includes("bttv")) {
promises.push(app.badges.fetchBttv(true));
}

await channel.client.fetchBadges(includeGlobal);
await channel.fetchBadges(true);
if (providers.includes("chatterino")) {
promises.push(app.badges.fetchChatterino(true));
}

await Promise.all(promises);
}

channel.chat.addSystemMessage("Reloaded badges.");
},
Expand Down
6 changes: 3 additions & 3 deletions src/lib/components/User.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -182,12 +182,12 @@
: relationship.badges.slice(0, 10)}

<div class="flex flex-wrap items-center gap-1">
{#each badges as badge (`${badge.setID}:${badge.version}`)}
{#each badges as badge (badge.id)}
<img
class="size-4"
src={badge.imageURL}
alt={badge.title}
title={badge.title}
src={badge.imageUrl}
alt={badge.description}
/>
{/each}

Expand Down
37 changes: 4 additions & 33 deletions src/lib/components/message/Message.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
<script lang="ts">
import { app } from "$lib/app.svelte";
import type { Badge } from "$lib/graphql/twitch";
import type { LinkNode } from "$lib/models/message/parse";
import type { UserMessage } from "$lib/models/message/user-message";
import { settings } from "$lib/settings";
Expand All @@ -17,36 +15,8 @@

const { message, nested = false }: Props = $props();

const badges: Badge[] = [];
const linkNodes = $derived(message.nodes.filter((n) => n.type === "link"));

if (message.shared) {
const { user } = message.source;

badges.push({
setID: user.id,
version: "1",
title: user.displayName,
description: user.displayName,
imageURL: user.avatarUrl,
});
}

for (const badge of message.badges) {
const chatBadge = message.source.badges.get(badge);
const globalBadge = app.twitch.badges.get(badge);

const resolved = chatBadge ?? globalBadge;

if (resolved) {
badges.push(resolved);
}
}

if (message.author.badge) {
badges.push(message.author.badge);
}

function canEmbed(node: LinkNode) {
return (
node.data.tld.domain === "7tv.app" ||
Expand All @@ -59,17 +29,18 @@

<Timestamp date={message.timestamp} />

{#each badges as badge (badge.title)}
{#each message.badges as badge (badge.id)}
<Tooltip.Root>
<Tooltip.Trigger>
{#snippet child({ props })}
<img
{...props}
class="inline-block align-middle"
src={badge.imageURL}
class={["inline-block align-middle", badge.color && "rounded-xs"]}
src={badge.imageUrl}
alt={badge.description}
width="18"
height="18"
style:background-color={badge.color}
/>
{/snippet}
</Tooltip.Trigger>
Expand Down
5 changes: 3 additions & 2 deletions src/lib/handlers/irc/privmsg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,17 @@ export default defineHandler({
if (!channel) return;

const message = new UserMessage(channel, data);
const badges = (data.source ?? data).badges;

message.author.color = data.name_color;
message.author.username = data.sender.login;
message.author.displayName = data.sender.name;

message.viewer ??= await channel.viewers.fetch(data.sender.id);
message.viewer.broadcaster = message.badges.some((b) => b.startsWith("broadcaster"));
message.viewer.broadcaster = badges.some((b) => b.name.startsWith("broadcaster"));
message.viewer.moderator = message.viewer.broadcaster || data.is_mod;
message.viewer.subscriber = data.is_subscriber;
message.viewer.vip = message.badges.some((b) => b.startsWith("vip"));
message.viewer.vip = badges.some((b) => b.name.startsWith("vip"));
message.viewer.returning = data.is_returning_chatter;
message.viewer.new = data.is_first_msg;

Expand Down
13 changes: 7 additions & 6 deletions src/lib/handlers/irc/whisper.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { page } from "$app/state";
import { app } from "$lib/app.svelte";
import { Whisper } from "$lib/models/whisper.svelte";
import { getOrInsertComputed } from "$lib/util";
import { defineHandler } from "../helper";

export default defineHandler({
Expand All @@ -10,17 +11,17 @@ export default defineHandler({

const sender = await app.twitch.users.fetch(data.sender.id);

if (!app.user.whispers.has(sender.id)) {
app.user.whispers.set(sender.id, new Whisper(app.twitch, sender));
}

const whisper = app.user.whispers.get(sender.id)!;
const whisper = getOrInsertComputed(
app.user.whispers,
sender.id,
() => new Whisper(app.twitch, sender),
);

whisper.messages.push({
id: data.message_id,
createdAt: new Date(),
badges: data.badges
.map((b) => app.twitch.badges.get(`${b.name}:${b.version}`))
.map((b) => app.badges.get(`${b.name}:${b.version}`))
.filter((b) => b != null),
user: sender,
text: data.message_text,
Expand Down
18 changes: 11 additions & 7 deletions src/lib/handlers/seventv/cosmetic-create.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { app } from "$lib/app.svelte";
import { Badge } from "$lib/models/badge";
import { defineHandler } from "../helper";

function toRgb(decimal: number) {
Expand All @@ -16,13 +17,16 @@ export default defineHandler({
if (cosmetic.kind === "BADGE") {
const file = cosmetic.data.host.files.find((f) => f.name.startsWith("4x"))!;

app.badges.set(cosmetic.id, {
setID: cosmetic.id,
version: "1",
title: cosmetic.data.name,
description: cosmetic.data.tooltip,
imageURL: `https:${cosmetic.data.host.url}/${file.name}`,
});
app.badges.set(
cosmetic.id,
new Badge({
setId: "7tv",
version: cosmetic.id,
title: cosmetic.data.name,
description: cosmetic.data.tooltip,
imageUrl: `https:${cosmetic.data.host.url}/${file.name}`,
}),
);

return;
}
Expand Down
6 changes: 5 additions & 1 deletion src/lib/handlers/seventv/entitlement-create.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { app } from "$lib/app.svelte";
import { getOrInsert } from "$lib/util";
import { defineHandler } from "../helper";

export default defineHandler({
Expand All @@ -11,7 +12,10 @@ export default defineHandler({

switch (data.kind) {
case "BADGE": {
app.u2b.set(user.id, app.badges.get(data.ref_id));
const badge = app.badges.get(data.ref_id);
if (!badge) return;

getOrInsert(app.badges.users, user.id, []).push(badge);
break;
}

Expand Down
Loading