Skip to content
Merged
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
2 changes: 1 addition & 1 deletion src/lib/commands/built-in/reload-badges.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export default defineCommand({
if (providers.includes("all")) {
await app.badges.fetch(true);
} else {
const promises: Promise<unknown>[] = [channel.fetchBadges(true)];
const promises: Promise<unknown>[] = [channel.badges.fetch(true)];

if (providers.includes("twitch")) {
promises.push(app.badges.fetchTwitch(true));
Expand Down
2 changes: 1 addition & 1 deletion src/lib/commands/built-in/reload-cheermotes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export default defineCommand({
name: "reload-cheermotes",
description: "Reload cheermotes for the channel",
async exec(_, channel) {
await channel.fetchCheermotes(true);
await channel.cheermotes.fetch(true);

channel.chat.notice("Reloaded cheermotes.");
},
Expand Down
42 changes: 42 additions & 0 deletions src/lib/managers/channel-badge-manager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import * as cache from "tauri-plugin-cache-api";

import type { Channel } from "#lib/models/channel.svelte.js";

import { channelBadgesQuery } from "#lib/graphql/twitch.js";
import { Badge } from "#lib/models/badge.js";

export class ChannelBadgeManager extends Map<string, Badge> {
public constructor(public readonly channel: Channel) {
super();
}

/**
* Retrieves the list of badges in the channel and caches them for later
* use.
*/
public async fetch(force = false) {
if (!force && this.size) {
return this;
}

let badges = await cache.get<Badge[]>(`badges:${this.channel.id}`);

if (force || !badges) {
if (force) this.clear();

const { user } = await this.channel.client.gql(channelBadgesQuery, {
id: this.channel.id,
});

badges = user?.broadcastBadges?.flatMap((b) => (b ? [Badge.fromGql(b)] : [])) ?? [];

await cache.set(`badges:${this.channel.id}`, badges);
}

for (const badge of badges) {
this.set(badge.id, badge);
}

return this;
}
}
42 changes: 42 additions & 0 deletions src/lib/managers/cheermote-manager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import * as cache from "tauri-plugin-cache-api";

import type { Cheermote } from "#lib/graphql/twitch.js";
import type { Channel } from "#lib/models/channel.svelte.js";

import { cheermoteQuery } from "#lib/graphql/twitch.js";

/**
* The cheermotes in a channel keyed by their lowercased prefix.
*/
export class CheermoteManager extends Map<string, Cheermote> {
public constructor(public readonly channel: Channel) {
super();
}

/**
* Retrieves the list of cheermotes in the channel and caches them for later
* use.
*/
public async fetch(force = false) {
if (!force && this.size) {
return this;
}

let cheermotes = await cache.get<Cheermote[]>(`cheermotes:${this.channel.id}`);

if (force || !cheermotes) {
if (force) this.clear();

const { user } = await this.channel.client.gql(cheermoteQuery, { id: this.channel.id });

cheermotes = user?.cheer?.emotes.filter((e) => e != null) ?? [];
await cache.set(`cheermotes:${this.channel.id}`, cheermotes);
}

for (const cheermote of cheermotes) {
this.set(cheermote.prefix.toLowerCase(), cheermote);
}

return this;
}
}
66 changes: 10 additions & 56 deletions src/lib/models/channel.svelte.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
import { invoke } from "@tauri-apps/api/core";
import * as cache from "tauri-plugin-cache-api";

import type { Cheermote, PredictionOutcome } from "#lib/graphql/twitch.js";
import type { PredictionOutcome } from "#lib/graphql/twitch.js";
import type { TwitchClient } from "#lib/twitch/client.js";
import type { IrcMessage } from "#lib/twitch/irc.js";

import { app } from "#lib/app.svelte.js";
import {
channelBadgesQuery,
cheermoteQuery,
pollQuery,
predictionQuery,
streamQuery,
Expand All @@ -24,14 +21,15 @@ import {
} from "#lib/graphql/twitch.js";
import { handlers } from "#lib/handlers/index.js";
import { log } from "#lib/log.js";
import { ChannelBadgeManager } from "#lib/managers/channel-badge-manager.js";
import { ChannelEmoteManager } from "#lib/managers/channel-emote-manager.js";
import { CheermoteManager } from "#lib/managers/cheermote-manager.js";
import { ViewerManager } from "#lib/managers/viewer-manager.js";
import { settings } from "#lib/settings/index.js";
import { storage } from "#lib/stores.js";

import type { User } from "./user.svelte";

import { Badge } from "./badge";
import { Chat } from "./chat.svelte";
import { Poll } from "./poll.svelte";
import { Prediction } from "./prediction.svelte";
Expand Down Expand Up @@ -64,7 +62,7 @@ export class Channel {
/**
* The badges in the channel.
*/
public readonly badges = new Map<string, Badge>();
public readonly badges: ChannelBadgeManager;

/**
* The emotes in the channel.
Expand All @@ -74,7 +72,7 @@ export class Channel {
/**
* The cheermotes in the channel.
*/
public readonly cheermotes = $state<Cheermote[]>([]);
public readonly cheermotes: CheermoteManager;

/**
* The viewers in the channel.
Expand Down Expand Up @@ -129,7 +127,9 @@ export class Channel {
this.stream = stream;

this.chat = new Chat(this);
this.badges = new ChannelBadgeManager(this);
this.emotes = new ChannelEmoteManager(this);
this.cheermotes = new CheermoteManager(this);
this.viewers = new ViewerManager(this);

this.pinned = $derived(storage.state.pinned.includes(this.id));
Expand Down Expand Up @@ -160,8 +160,8 @@ export class Channel {
await Promise.all([
this.fetchStream(),
this.emotes.fetch(),
this.fetchBadges(),
this.fetchCheermotes(),
this.badges.fetch(),
this.cheermotes.fetch(),
this.chat.fetchPinned(),
this.fetchPoll(),
this.fetchPrediction(),
Expand Down Expand Up @@ -226,54 +226,8 @@ export class Channel {
this.chat.reset();
this.badges.clear();
this.emotes.clear();
this.cheermotes.clear();
this.viewers.clear();
this.cheermotes.length = 0;
}

/**
* Retrieves the list of badges in the channel and caches them for later use.
*/
public async fetchBadges(force = false) {
if (!force && this.badges.size) {
return this.badges;
}

let badges = await cache.get<Badge[]>(`badges:${this.id}`);

if (force || !badges) {
if (force) this.badges.clear();

const { user } = await this.client.gql(channelBadgesQuery, { id: this.id });
badges = user?.broadcastBadges?.flatMap((b) => (b ? [Badge.fromGql(b)] : [])) ?? [];

await cache.set(`badges:${this.id}`, badges);
}

for (const badge of badges) {
this.badges.set(badge.id, badge);
}

return this.badges;
}

/**
* Retrieves the list of cheermotes in the channel and caches them for later
* use.
*/
public async fetchCheermotes(force = false) {
let cheermotes = await cache.get<Cheermote[]>(`cheermotes:${this.id}`);

if (force || !cheermotes) {
if (force) this.cheermotes.length = 0;

const { user } = await this.client.gql(cheermoteQuery, { id: this.id });

cheermotes = user?.cheer?.emotes.filter((e) => e != null) ?? [];
await cache.set(`cheermotes:${this.id}`, cheermotes);
}

this.cheermotes.push(...cheermotes);
return this.cheermotes;
}

/**
Expand Down
5 changes: 2 additions & 3 deletions src/lib/models/message/parse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,9 +123,8 @@ function classify(base: BaseNode, message: UserMessage, ircEmotes: IrcEmote[]):
};
}

const cheermote = message.channel.cheermotes.find(
(c) => part.toLowerCase().startsWith(c.prefix.toLowerCase()) && /\d+$/.test(part),
);
const cheer = /^(.+?)\d+$/.exec(part);
const cheermote = cheer && message.channel.cheermotes.get(cheer[1].toLowerCase());

if (cheermote) {
const amount = Number(part.slice(cheermote.prefix.length));
Expand Down
2 changes: 1 addition & 1 deletion src/lib/models/message/user-message.svelte.ts
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ export class UserMessage extends TextualMessage {
}

this.source = await app.channels.fetch(source.channel_id);
await this.source.fetchBadges();
await this.source.badges.fetch();

this.#populateBadges();
}
Expand Down
Loading