From 63fc354d483b86857bbee7f540c66ba614fc0f1f Mon Sep 17 00:00:00 2001 From: V Date: Sat, 22 Apr 2023 03:18:19 +0200 Subject: [PATCH] feat: auto-managed flux subscriptions via plugin.flux (#959) --- src/plugins/index.ts | 42 ++++++--- src/plugins/keepCurrentChannel.ts | 75 ++++++++-------- src/plugins/memberCount.tsx | 34 +++----- src/plugins/moyai.ts | 72 +++++++--------- src/plugins/relationshipNotifier/events.ts | 41 --------- src/plugins/relationshipNotifier/index.ts | 25 ++++-- src/plugins/serverListIndicators.tsx | 50 +++++------ src/plugins/supportHelper.tsx | 18 ++-- src/plugins/vcNarrator.tsx | 99 ++++++++++------------ src/utils/types.ts | 7 ++ 10 files changed, 210 insertions(+), 253 deletions(-) delete mode 100644 src/plugins/relationshipNotifier/events.ts diff --git a/src/plugins/index.ts b/src/plugins/index.ts index d0d16c23..ab69ca74 100644 --- a/src/plugins/index.ts +++ b/src/plugins/index.ts @@ -20,6 +20,8 @@ import { registerCommand, unregisterCommand } from "@api/Commands"; import { Settings } from "@api/settings"; import Logger from "@utils/Logger"; import { Patch, Plugin } from "@utils/types"; +import { FluxDispatcher } from "@webpack/common"; +import { FluxEvents } from "@webpack/types"; import Plugins from "~plugins"; @@ -111,56 +113,64 @@ export function startDependenciesRecursive(p: Plugin) { } export const startPlugin = traceFunction("startPlugin", function startPlugin(p: Plugin) { + const { name, commands, flux } = p; + if (p.start) { - logger.info("Starting plugin", p.name); + logger.info("Starting plugin", name); if (p.started) { - logger.warn(`${p.name} already started`); + logger.warn(`${name} already started`); return false; } try { p.start(); p.started = true; } catch (e) { - logger.error(`Failed to start ${p.name}\n`, e); + logger.error(`Failed to start ${name}\n`, e); return false; } } - if (p.commands?.length) { - logger.info("Registering commands of plugin", p.name); - for (const cmd of p.commands) { + if (commands?.length) { + logger.info("Registering commands of plugin", name); + for (const cmd of commands) { try { - registerCommand(cmd, p.name); + registerCommand(cmd, name); } catch (e) { logger.error(`Failed to register command ${cmd.name}\n`, e); return false; } } + } + if (flux) { + for (const event in flux) { + FluxDispatcher.subscribe(event as FluxEvents, flux[event]); + } } return true; }, p => `startPlugin ${p.name}`); export const stopPlugin = traceFunction("stopPlugin", function stopPlugin(p: Plugin) { + const { name, commands, flux } = p; if (p.stop) { - logger.info("Stopping plugin", p.name); + logger.info("Stopping plugin", name); if (!p.started) { - logger.warn(`${p.name} already stopped`); + logger.warn(`${name} already stopped`); return false; } try { p.stop(); p.started = false; } catch (e) { - logger.error(`Failed to stop ${p.name}\n`, e); + logger.error(`Failed to stop ${name}\n`, e); return false; } } - if (p.commands?.length) { - logger.info("Unregistering commands of plugin", p.name); - for (const cmd of p.commands) { + if (commands?.length) { + logger.info("Unregistering commands of plugin", name); + for (const cmd of commands) { try { unregisterCommand(cmd.name); } catch (e) { @@ -170,5 +180,11 @@ export const stopPlugin = traceFunction("stopPlugin", function stopPlugin(p: Plu } } + if (flux) { + for (const event in flux) { + FluxDispatcher.unsubscribe(event as FluxEvents, flux[event]); + } + } + return true; }, p => `stopPlugin ${p.name}`); diff --git a/src/plugins/keepCurrentChannel.ts b/src/plugins/keepCurrentChannel.ts index e553b936..b226c34e 100644 --- a/src/plugins/keepCurrentChannel.ts +++ b/src/plugins/keepCurrentChannel.ts @@ -19,7 +19,7 @@ import * as DataStore from "@api/DataStore"; import { Devs } from "@utils/constants"; import definePlugin from "@utils/types"; -import { ChannelStore, FluxDispatcher, NavigationRouter, SelectedChannelStore, SelectedGuildStore } from "@webpack/common"; +import { ChannelStore, NavigationRouter, SelectedChannelStore, SelectedGuildStore } from "@webpack/common"; export interface LogoutEvent { type: "LOGOUT"; @@ -37,63 +37,54 @@ interface PreviousChannel { channelId: string | null; } +let isSwitchingAccount = false; +let previousCache: PreviousChannel | undefined; + +function attemptToNavigateToChannel(guildId: string | null, channelId: string) { + if (!ChannelStore.hasChannel(channelId)) return; + NavigationRouter.transitionTo(`/channels/${guildId ?? "@me"}/${channelId}`); +} export default definePlugin({ name: "KeepCurrentChannel", description: "Attempt to navigate to the channel you were in before switching accounts or loading Discord.", authors: [Devs.Nuckyz], - isSwitchingAccount: false, - previousCache: {} as PreviousChannel, + flux: { + LOGOUT(e: LogoutEvent) { + ({ isSwitchingAccount } = e); + }, - attemptToNavigateToChannel(guildId: string | null, channelId: string) { - if (!ChannelStore.hasChannel(channelId)) return; - NavigationRouter.transitionTo(`/channels/${guildId ?? "@me"}/${channelId}`); - }, + CONNECTION_OPEN() { + if (!isSwitchingAccount) return; + isSwitchingAccount = false; - onLogout(e: LogoutEvent) { - this.isSwitchingAccount = e.isSwitchingAccount; - }, + if (previousCache?.channelId) + attemptToNavigateToChannel(previousCache.guildId, previousCache.channelId); + }, - onConnectionOpen() { - if (!this.isSwitchingAccount) return; - this.isSwitchingAccount = false; + async CHANNEL_SELECT({ guildId, channelId }: ChannelSelectEvent) { + if (isSwitchingAccount) return; - if (this.previousCache.channelId) this.attemptToNavigateToChannel(this.previousCache.guildId, this.previousCache.channelId); - }, - - async onChannelSelect({ guildId, channelId }: ChannelSelectEvent) { - if (this.isSwitchingAccount) return; - - this.previousCache = { - guildId, - channelId - }; - await DataStore.set("KeepCurrentChannel_previousData", this.previousCache); + previousCache = { + guildId, + channelId + }; + await DataStore.set("KeepCurrentChannel_previousData", previousCache); + } }, async start() { - const previousData = await DataStore.get("KeepCurrentChannel_previousData"); - if (previousData) { - this.previousCache = previousData; - - if (this.previousCache.channelId) this.attemptToNavigateToChannel(this.previousCache.guildId, this.previousCache.channelId); - } else { - this.previousCache = { + previousCache = await DataStore.get("KeepCurrentChannel_previousData"); + if (!previousCache) { + previousCache = { guildId: SelectedGuildStore.getGuildId(), channelId: SelectedChannelStore.getChannelId() ?? null }; - await DataStore.set("KeepCurrentChannel_previousData", this.previousCache); + + await DataStore.set("KeepCurrentChannel_previousData", previousCache); + } else if (previousCache.channelId) { + attemptToNavigateToChannel(previousCache.guildId, previousCache.channelId); } - - FluxDispatcher.subscribe("LOGOUT", this.onLogout.bind(this)); - FluxDispatcher.subscribe("CONNECTION_OPEN", this.onConnectionOpen.bind(this)); - FluxDispatcher.subscribe("CHANNEL_SELECT", this.onChannelSelect.bind(this)); - }, - - stop() { - FluxDispatcher.unsubscribe("LOGOUT", this.onLogout); - FluxDispatcher.unsubscribe("CONNECTION_OPEN", this.onConnectionOpen); - FluxDispatcher.unsubscribe("CHANNEL_SELECT", this.onChannelSelect); } }); diff --git a/src/plugins/memberCount.tsx b/src/plugins/memberCount.tsx index 3a68126d..0b5409c0 100644 --- a/src/plugins/memberCount.tsx +++ b/src/plugins/memberCount.tsx @@ -23,7 +23,7 @@ import { getCurrentChannel } from "@utils/discord"; import { useForceUpdater } from "@utils/misc"; import definePlugin from "@utils/types"; import { findStoreLazy } from "@webpack"; -import { FluxDispatcher, Tooltip } from "@webpack/common"; +import { Tooltip } from "@webpack/common"; const counts = {} as Record; let forceUpdate: () => void; @@ -107,27 +107,21 @@ export default definePlugin({ } }], - onGuildMemberListUpdate({ guildId, groups, memberCount, id }) { - // eeeeeh - sometimes it has really wrong counts??? like 10 times less than actual - // but if we only listen to everyone updates, sometimes we never get the count? - // this seems to work but isn't optional - if (id !== "everyone" && counts[guildId]) return; + flux: { + GUILD_MEMBER_LIST_UPDATE({ guildId, groups, memberCount, id }) { + // eeeeeh - sometimes it has really wrong counts??? like 10 times less than actual + // but if we only listen to everyone updates, sometimes we never get the count? + // this seems to work but isn't optional + if (id !== "everyone" && counts[guildId]) return; - let count = 0; - for (const group of groups) { - if (group.id !== "offline") - count += group.count; + let count = 0; + for (const group of groups) { + if (group.id !== "offline") + count += group.count; + } + counts[guildId] = [memberCount, count]; + forceUpdate?.(); } - counts[guildId] = [memberCount, count]; - forceUpdate?.(); - }, - - start() { - FluxDispatcher.subscribe("GUILD_MEMBER_LIST_UPDATE", this.onGuildMemberListUpdate); - }, - - stop() { - FluxDispatcher.unsubscribe("GUILD_MEMBER_LIST_UPDATE", this.onGuildMemberListUpdate); }, render: () => ( diff --git a/src/plugins/moyai.ts b/src/plugins/moyai.ts index 146ac3f6..b32bd992 100644 --- a/src/plugins/moyai.ts +++ b/src/plugins/moyai.ts @@ -21,7 +21,7 @@ import { makeRange } from "@components/PluginSettings/components/SettingSliderCo import { Devs } from "@utils/constants"; import { sleep } from "@utils/misc"; import definePlugin, { OptionType } from "@utils/types"; -import { FluxDispatcher, SelectedChannelStore, UserStore } from "@webpack/common"; +import { SelectedChannelStore, UserStore } from "@webpack/common"; import { Message, ReactionEmoji } from "discord-types/general"; interface IMessageCreate { @@ -80,50 +80,40 @@ export default definePlugin({ description: "🗿🗿🗿🗿🗿🗿🗿🗿", settings, - async onMessage(e: IMessageCreate) { - if (e.optimistic || e.type !== "MESSAGE_CREATE") return; - if (e.message.state === "SENDING") return; - if (settings.store.ignoreBots && e.message.author?.bot) return; - if (!e.message.content) return; - if (e.channelId !== SelectedChannelStore.getChannelId()) return; + flux: { + async MESSAGE_CREATE({ optimistic, type, message, channelId }: IMessageCreate) { + if (optimistic || type !== "MESSAGE_CREATE") return; + if (message.state === "SENDING") return; + if (settings.store.ignoreBots && message.author?.bot) return; + if (!message.content) return; + if (channelId !== SelectedChannelStore.getChannelId()) return; - const moyaiCount = getMoyaiCount(e.message.content); + const moyaiCount = getMoyaiCount(message.content); + + for (let i = 0; i < moyaiCount; i++) { + boom(); + await sleep(300); + } + }, + + MESSAGE_REACTION_ADD({ optimistic, type, channelId, userId, emoji }: IReactionAdd) { + if (optimistic || type !== "MESSAGE_REACTION_ADD") return; + if (settings.store.ignoreBots && UserStore.getUser(userId)?.bot) return; + if (channelId !== SelectedChannelStore.getChannelId()) return; + + const name = emoji.name.toLowerCase(); + if (name !== MOYAI && !name.includes("moyai") && !name.includes("moai")) return; + + boom(); + }, + + VOICE_CHANNEL_EFFECT_SEND({ emoji }: IVoiceChannelEffectSendEvent) { + if (!emoji?.name) return; + const name = emoji.name.toLowerCase(); + if (name !== MOYAI && !name.includes("moyai") && !name.includes("moai")) return; - for (let i = 0; i < moyaiCount; i++) { boom(); - await sleep(300); } - }, - - onReaction(e: IReactionAdd) { - if (e.optimistic || e.type !== "MESSAGE_REACTION_ADD") return; - if (settings.store.ignoreBots && UserStore.getUser(e.userId)?.bot) return; - if (e.channelId !== SelectedChannelStore.getChannelId()) return; - - const name = e.emoji.name.toLowerCase(); - if (name !== MOYAI && !name.includes("moyai") && !name.includes("moai")) return; - - boom(); - }, - - onVoiceChannelEffect(e: IVoiceChannelEffectSendEvent) { - if (!e.emoji?.name) return; - const name = e.emoji.name.toLowerCase(); - if (name !== MOYAI && !name.includes("moyai") && !name.includes("moai")) return; - - boom(); - }, - - start() { - FluxDispatcher.subscribe("MESSAGE_CREATE", this.onMessage); - FluxDispatcher.subscribe("MESSAGE_REACTION_ADD", this.onReaction); - FluxDispatcher.subscribe("VOICE_CHANNEL_EFFECT_SEND", this.onVoiceChannelEffect); - }, - - stop() { - FluxDispatcher.unsubscribe("MESSAGE_CREATE", this.onMessage); - FluxDispatcher.unsubscribe("MESSAGE_REACTION_ADD", this.onReaction); - FluxDispatcher.unsubscribe("VOICE_CHANNEL_EFFECT_SEND", this.onVoiceChannelEffect); } }); diff --git a/src/plugins/relationshipNotifier/events.ts b/src/plugins/relationshipNotifier/events.ts deleted file mode 100644 index 0c6914d9..00000000 --- a/src/plugins/relationshipNotifier/events.ts +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Vencord, a modification for Discord's desktop app - * Copyright (c) 2023 Vendicated and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . -*/ - -import { FluxEvents } from "@webpack/types"; - -import { onChannelDelete, onGuildDelete, onRelationshipRemove } from "./functions"; -import { syncAndRunChecks, syncFriends, syncGroups, syncGuilds } from "./utils"; - -export const FluxHandlers: Partial void>>> = { - GUILD_CREATE: [syncGuilds], - GUILD_DELETE: [onGuildDelete], - CHANNEL_CREATE: [syncGroups], - CHANNEL_DELETE: [onChannelDelete], - RELATIONSHIP_ADD: [syncFriends], - RELATIONSHIP_UPDATE: [syncFriends], - RELATIONSHIP_REMOVE: [syncFriends, onRelationshipRemove], - CONNECTION_OPEN: [syncAndRunChecks] -}; - -export function forEachEvent(fn: (event: FluxEvents, handler: (data: any) => void) => void) { - for (const event in FluxHandlers) { - for (const cb of FluxHandlers[event]) { - fn(event as FluxEvents, cb); - } - } -} diff --git a/src/plugins/relationshipNotifier/index.ts b/src/plugins/relationshipNotifier/index.ts index 6319142c..7300fba8 100644 --- a/src/plugins/relationshipNotifier/index.ts +++ b/src/plugins/relationshipNotifier/index.ts @@ -18,12 +18,10 @@ import { Devs } from "@utils/constants"; import definePlugin from "@utils/types"; -import { FluxDispatcher } from "@webpack/common"; -import { forEachEvent } from "./events"; -import { removeFriend, removeGroup, removeGuild } from "./functions"; +import { onChannelDelete, onGuildDelete, onRelationshipRemove, removeFriend, removeGroup, removeGuild } from "./functions"; import settings from "./settings"; -import { syncAndRunChecks } from "./utils"; +import { syncAndRunChecks, syncFriends, syncGroups, syncGuilds } from "./utils"; export default definePlugin({ name: "RelationshipNotifier", @@ -55,15 +53,24 @@ export default definePlugin({ } ], + flux: { + GUILD_CREATE: syncGuilds, + GUILD_DELETE: onGuildDelete, + CHANNEL_CREATE: syncGroups, + CHANNEL_DELETE: onChannelDelete, + RELATIONSHIP_ADD: syncFriends, + RELATIONSHIP_UPDATE: syncFriends, + RELATIONSHIP_REMOVE(e) { + onRelationshipRemove(e); + syncFriends(); + }, + CONNECTION_OPEN: syncAndRunChecks + }, + async start() { setTimeout(() => { syncAndRunChecks(); }, 5000); - forEachEvent((ev, cb) => FluxDispatcher.subscribe(ev, cb)); - }, - - stop() { - forEachEvent((ev, cb) => FluxDispatcher.unsubscribe(ev, cb)); }, removeFriend, diff --git a/src/plugins/serverListIndicators.tsx b/src/plugins/serverListIndicators.tsx index e1c0829e..ed168b5f 100644 --- a/src/plugins/serverListIndicators.tsx +++ b/src/plugins/serverListIndicators.tsx @@ -22,7 +22,7 @@ import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import { useForceUpdater } from "@utils/misc"; import definePlugin, { OptionType } from "@utils/types"; -import { FluxDispatcher, GuildStore,PresenceStore, RelationshipStore } from "@webpack/common"; +import { GuildStore, PresenceStore, RelationshipStore } from "@webpack/common"; enum IndicatorType { SERVER = 1 << 0, @@ -71,6 +71,24 @@ function ServersIndicator() { ); } +function handlePresenceUpdate() { + onlineFriends = 0; + const relations = RelationshipStore.getRelationships(); + for (const id of Object.keys(relations)) { + const type = relations[id]; + // FRIEND relationship type + if (type === 1 && PresenceStore.getStatus(id) !== "offline") { + onlineFriends += 1; + } + } + forceUpdateFriendCount?.(); +} + +function handleGuildUpdate() { + guildCount = GuildStore.getGuildCount(); + forceUpdateGuildCount?.(); +} + export default definePlugin({ name: "ServerListIndicators", description: "Add online friend count or server count in the server list", @@ -99,37 +117,21 @@ export default definePlugin({ ; }, - handlePresenceUpdate() { - onlineFriends = 0; - const relations = RelationshipStore.getRelationships(); - for (const id of Object.keys(relations)) { - const type = relations[id]; - // FRIEND relationship type - if (type === 1 && PresenceStore.getStatus(id) !== "offline") { - onlineFriends += 1; - } - } - forceUpdateFriendCount?.(); + flux: { + PRESENCE_UPDATES: handlePresenceUpdate, + GUILD_CREATE: handleGuildUpdate, + GUILD_DELETE: handleGuildUpdate, }, - handleGuildUpdate() { - guildCount = GuildStore.getGuildCount(); - forceUpdateGuildCount?.(); - }, start() { - this.handlePresenceUpdate(); - this.handleGuildUpdate(); addServerListElement(ServerListRenderPosition.Above, this.renderIndicator); - FluxDispatcher.subscribe("PRESENCE_UPDATES", this.handlePresenceUpdate); - FluxDispatcher.subscribe("GUILD_CREATE", this.handleGuildUpdate); - FluxDispatcher.subscribe("GUILD_DELETE", this.handleGuildUpdate); + + handlePresenceUpdate(); + handleGuildUpdate(); }, stop() { removeServerListElement(ServerListRenderPosition.Above, this.renderIndicator); - FluxDispatcher.unsubscribe("PRESENCE_UPDATES", this.handlePresenceUpdate); - FluxDispatcher.unsubscribe("GUILD_CREATE", this.handleGuildUpdate); - FluxDispatcher.unsubscribe("GUILD_DELETE", this.handleGuildUpdate); } }); diff --git a/src/plugins/supportHelper.tsx b/src/plugins/supportHelper.tsx index 21d9059b..469b5aed 100644 --- a/src/plugins/supportHelper.tsx +++ b/src/plugins/supportHelper.tsx @@ -21,7 +21,7 @@ import { Devs, SUPPORT_CHANNEL_ID } from "@utils/constants"; import { makeCodeblock } from "@utils/misc"; import definePlugin from "@utils/types"; import { isOutdated } from "@utils/updater"; -import { Alerts, FluxDispatcher, Forms, UserStore } from "@webpack/common"; +import { Alerts, Forms, UserStore } from "@webpack/common"; import gitHash from "~git-hash"; import plugins from "~plugins"; @@ -69,18 +69,16 @@ ${makeCodeblock(Object.keys(plugins).filter(Vencord.Plugins.isPluginEnabled).joi } }], - rememberDismiss() { - DataStore.set(REMEMBER_DISMISS_KEY, gitHash); - }, - - start() { - FluxDispatcher.subscribe("CHANNEL_SELECT", async ({ channelId }) => { + flux: { + async CHANNEL_SELECT({ channelId }) { if (channelId !== SUPPORT_CHANNEL_ID) return; const myId = BigInt(UserStore.getCurrentUser().id); if (Object.values(Devs).some(d => d.id === myId)) return; if (isOutdated && gitHash !== await DataStore.get(REMEMBER_DISMISS_KEY)) { + const rememberDismiss = () => DataStore.set(REMEMBER_DISMISS_KEY, gitHash); + Alerts.show({ title: "Hold on!", body:
@@ -90,10 +88,10 @@ ${makeCodeblock(Object.keys(plugins).filter(Vencord.Plugins.isPluginEnabled).joi to do so, in case you can't access the Updater page.
, - onCancel: this.rememberDismiss, - onConfirm: this.rememberDismiss + onCancel: rememberDismiss, + onConfirm: rememberDismiss }); } - }); + } } }); diff --git a/src/plugins/vcNarrator.tsx b/src/plugins/vcNarrator.tsx index f55f639c..a318e891 100644 --- a/src/plugins/vcNarrator.tsx +++ b/src/plugins/vcNarrator.tsx @@ -24,7 +24,7 @@ import { Margins } from "@utils/margins"; import { wordsToTitle } from "@utils/text"; import definePlugin, { OptionType, PluginOptionsItem } from "@utils/types"; import { findByPropsLazy } from "@webpack"; -import { Button, ChannelStore, FluxDispatcher, Forms, SelectedChannelStore, useMemo, UserStore } from "@webpack/common"; +import { Button, ChannelStore, Forms, SelectedChannelStore, useMemo, UserStore } from "@webpack/common"; interface VoiceState { userId: string; @@ -137,49 +137,6 @@ function updateStatuses(type: string, { deaf, mute, selfDeaf, selfMute, userId, } */ -function handleVoiceStates({ voiceStates }: { voiceStates: VoiceState[]; }) { - const myChanId = SelectedChannelStore.getVoiceChannelId(); - const myId = UserStore.getCurrentUser().id; - - for (const state of voiceStates) { - const { userId, channelId, oldChannelId } = state; - const isMe = userId === myId; - if (!isMe) { - if (!myChanId) continue; - if (channelId !== myChanId && oldChannelId !== myChanId) continue; - } - - const [type, id] = getTypeAndChannelId(state, isMe); - if (!type) continue; - - const template = Settings.plugins.VcNarrator[type + "Message"]; - const user = isMe ? "" : UserStore.getUser(userId).username; - const channel = ChannelStore.getChannel(id).name; - - speak(formatText(template, user, channel)); - - // updateStatuses(type, state, isMe); - } -} - -function handleToggleSelfMute() { - const chanId = SelectedChannelStore.getVoiceChannelId()!; - const s = VoiceStateStore.getVoiceStateForChannel(chanId) as VoiceState; - if (!s) return; - - const event = s.mute || s.selfMute ? "unmute" : "mute"; - speak(formatText(Settings.plugins.VcNarrator[event + "Message"], "", ChannelStore.getChannel(chanId).name)); -} - -function handleToggleSelfDeafen() { - const chanId = SelectedChannelStore.getVoiceChannelId()!; - const s = VoiceStateStore.getVoiceStateForChannel(chanId) as VoiceState; - if (!s) return; - - const event = s.deaf || s.selfDeaf ? "undeafen" : "deafen"; - speak(formatText(Settings.plugins.VcNarrator[event + "Message"], "", ChannelStore.getChannel(chanId).name)); -} - function playSample(tempSettings: any, type: string) { const settings = Object.assign({}, Settings.plugins.VcNarrator, tempSettings); @@ -191,6 +148,51 @@ export default definePlugin({ description: "Announces when users join, leave, or move voice channels via narrator", authors: [Devs.Ven], + flux: { + VOICE_STATE_UPDATES({ voiceStates }: { voiceStates: VoiceState[]; }) { + const myChanId = SelectedChannelStore.getVoiceChannelId(); + const myId = UserStore.getCurrentUser().id; + + for (const state of voiceStates) { + const { userId, channelId, oldChannelId } = state; + const isMe = userId === myId; + if (!isMe) { + if (!myChanId) continue; + if (channelId !== myChanId && oldChannelId !== myChanId) continue; + } + + const [type, id] = getTypeAndChannelId(state, isMe); + if (!type) continue; + + const template = Settings.plugins.VcNarrator[type + "Message"]; + const user = isMe ? "" : UserStore.getUser(userId).username; + const channel = ChannelStore.getChannel(id).name; + + speak(formatText(template, user, channel)); + + // updateStatuses(type, state, isMe); + } + }, + + AUDIO_TOGGLE_SELF_MUTE() { + const chanId = SelectedChannelStore.getVoiceChannelId()!; + const s = VoiceStateStore.getVoiceStateForChannel(chanId) as VoiceState; + if (!s) return; + + const event = s.mute || s.selfMute ? "unmute" : "mute"; + speak(formatText(Settings.plugins.VcNarrator[event + "Message"], "", ChannelStore.getChannel(chanId).name)); + }, + + AUDIO_TOGGLE_SELF_DEAF() { + const chanId = SelectedChannelStore.getVoiceChannelId()!; + const s = VoiceStateStore.getVoiceStateForChannel(chanId) as VoiceState; + if (!s) return; + + const event = s.deaf || s.selfDeaf ? "undeafen" : "deafen"; + speak(formatText(Settings.plugins.VcNarrator[event + "Message"], "", ChannelStore.getChannel(chanId).name)); + } + }, + start() { if (typeof speechSynthesis === "undefined" || speechSynthesis.getVoices().length === 0) { new Logger("VcNarrator").warn( @@ -199,15 +201,6 @@ export default definePlugin({ return; } - FluxDispatcher.subscribe("VOICE_STATE_UPDATES", handleVoiceStates); - FluxDispatcher.subscribe("AUDIO_TOGGLE_SELF_MUTE", handleToggleSelfMute); - FluxDispatcher.subscribe("AUDIO_TOGGLE_SELF_DEAF", handleToggleSelfDeafen); - }, - - stop() { - FluxDispatcher.unsubscribe("VOICE_STATE_UPDATES", handleVoiceStates); - FluxDispatcher.subscribe("AUDIO_TOGGLE_SELF_MUTE", handleToggleSelfMute); - FluxDispatcher.subscribe("AUDIO_TOGGLE_SELF_DEAF", handleToggleSelfDeafen); }, optionsCache: null as Record | null, diff --git a/src/utils/types.ts b/src/utils/types.ts index 54c9674b..d2b5e0ed 100644 --- a/src/utils/types.ts +++ b/src/utils/types.ts @@ -17,6 +17,7 @@ */ import { Command } from "@api/Commands"; +import { FluxEvents } from "@webpack/types"; import { Promisable } from "type-fest"; // exists to export default definePlugin({...}) @@ -101,6 +102,12 @@ export interface PluginDef { settingsAboutComponent?: React.ComponentType<{ tempSettings?: Record; }>; + /** + * Allows you to subscribe to Flux events + */ + flux?: { + [E in FluxEvents]?: (event: any) => void; + }; } export enum OptionType {