From 746c8240206107dfc40f0995d17cd24730d40b0d Mon Sep 17 00:00:00 2001 From: Vendicated Date: Tue, 23 Sep 2025 22:24:58 +0200 Subject: [PATCH 1/4] Fix Debug Logging toggle not working Closes #3268 --- src/plugins/_core/noTrack.ts | 9 +++++++++ src/utils/types.ts | 3 ++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/plugins/_core/noTrack.ts b/src/plugins/_core/noTrack.ts index ad1f255ba..eada58cbf 100644 --- a/src/plugins/_core/noTrack.ts +++ b/src/plugins/_core/noTrack.ts @@ -71,6 +71,15 @@ export default definePlugin({ } ], + // The TRACK event takes an optional `resolve` property that is called when the tracking event was submitted to the server. + // A few spots in Discord await this callback before continuing (most notably the Voice Debug Logging toggle). + // Since we NOOP the AnalyticsActionHandlers module, there is no handler for the TRACK event, so we have to handle it ourselves + flux: { + TRACK(event) { + event?.resolve?.(); + } + }, + startAt: StartAt.Init, start() { // Sentry is initialized in its own WebpackInstance. diff --git a/src/utils/types.ts b/src/utils/types.ts index 2f1d6396c..2293b2ffa 100644 --- a/src/utils/types.ts +++ b/src/utils/types.ts @@ -26,6 +26,7 @@ import { MessageClickListener, MessageEditListener, MessageSendListener } from " import { MessagePopoverButtonFactory } from "@api/MessagePopover"; import { Command, FluxEvents } from "@vencord/discord-types"; import { ReactNode } from "react"; +import { LiteralUnion } from "type-fest"; // exists to export default definePlugin({...}) export default function definePlugin

(p: P & Record) { @@ -151,7 +152,7 @@ export interface PluginDef { * Allows you to subscribe to Flux events */ flux?: { - [E in FluxEvents]?: (event: any) => void | Promise; + [E in LiteralUnion]?: (event: any) => void | Promise; }; /** * Allows you to manipulate context menus From cb845b522458532479c077ed82279c27e025601e Mon Sep 17 00:00:00 2001 From: Vendicated Date: Tue, 23 Sep 2025 23:58:38 +0200 Subject: [PATCH 2/4] PlatformIndicators: update indicators in real time if status changes Closes #3516 --- packages/discord-types/src/utils.d.ts | 8 +++-- src/plugins/index.ts | 4 +-- src/plugins/platformIndicators/index.tsx | 41 +++++++----------------- src/utils/types.ts | 6 ++-- 4 files changed, 21 insertions(+), 38 deletions(-) diff --git a/packages/discord-types/src/utils.d.ts b/packages/discord-types/src/utils.d.ts index 77b6f88b8..14b8fa507 100644 --- a/packages/discord-types/src/utils.d.ts +++ b/packages/discord-types/src/utils.d.ts @@ -6,13 +6,15 @@ import type { FluxEvents } from "./fluxEvents"; export { FluxEvents }; +type FluxEventsAutoComplete = LiteralUnion; + export interface FluxDispatcher { _actionHandlers: any; _subscriptions: any; - dispatch(event: { [key: string]: unknown; type: FluxEvents; }): Promise; + dispatch(event: { [key: string]: unknown; type: FluxEventsAutoComplete; }): Promise; isDispatching(): boolean; - subscribe(event: FluxEvents, callback: (data: any) => void): void; - unsubscribe(event: FluxEvents, callback: (data: any) => void): void; + subscribe(event: FluxEventsAutoComplete, callback: (data: any) => void): void; + unsubscribe(event: FluxEventsAutoComplete, callback: (data: any) => void): void; wait(callback: () => void): void; } diff --git a/src/plugins/index.ts b/src/plugins/index.ts index 8288935bd..5bb60a13a 100644 --- a/src/plugins/index.ts +++ b/src/plugins/index.ts @@ -222,7 +222,7 @@ export function subscribePluginFluxEvents(p: Plugin, fluxDispatcher: typeof Flux for (const [event, handler] of Object.entries(p.flux)) { const wrappedHandler = p.flux[event] = function () { try { - const res = handler.apply(p, arguments as any); + const res = handler!.apply(p, arguments as any); return res instanceof Promise ? res.catch(e => logger.error(`${p.name}: Error while handling ${event}\n`, e)) : res; @@ -242,7 +242,7 @@ export function unsubscribePluginFluxEvents(p: Plugin, fluxDispatcher: typeof Fl logger.debug("Unsubscribing from flux events of plugin", p.name); for (const [event, handler] of Object.entries(p.flux)) { - fluxDispatcher.unsubscribe(event as FluxEvents, handler); + fluxDispatcher.unsubscribe(event as FluxEvents, handler!); } } } diff --git a/src/plugins/platformIndicators/index.tsx b/src/plugins/platformIndicators/index.tsx index f804e987c..add930648 100644 --- a/src/plugins/platformIndicators/index.tsx +++ b/src/plugins/platformIndicators/index.tsx @@ -22,12 +22,11 @@ import { addProfileBadge, BadgePosition, BadgeUserArgs, ProfileBadge, removeProf import { addMemberListDecorator, removeMemberListDecorator } from "@api/MemberListDecorators"; import { addMessageDecoration, removeMessageDecoration } from "@api/MessageDecorations"; import { Settings } from "@api/Settings"; -import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; import { User } from "@vencord/discord-types"; import { filters, findStoreLazy, mapMangledModuleLazy } from "@webpack"; -import { PresenceStore, Tooltip, UserStore } from "@webpack/common"; +import { AuthenticationStore, PresenceStore, Tooltip, UserStore, useStateFromStores } from "@webpack/common"; export interface Session { sessionId: string; @@ -85,7 +84,7 @@ const PlatformIcon = ({ platform, status, small }: { platform: Platform, status: }; function ensureOwnStatus(user: User) { - if (user.id === UserStore.getCurrentUser().id) { + if (user.id === AuthenticationStore.getId()) { const sessions = SessionsStore.getSessions(); if (typeof sessions !== "object") return null; const sortedSessions = Object.values(sessions).sort(({ status: a }, { status: b }) => { @@ -104,7 +103,7 @@ function ensureOwnStatus(user: User) { }, {}); const { clientStatuses } = PresenceStore.getState(); - clientStatuses[UserStore.getCurrentUser().id] = ownStatus; + clientStatuses[AuthenticationStore.getId()] = ownStatus; } } @@ -115,7 +114,7 @@ function getBadges({ userId }: BadgeUserArgs): ProfileBadge[] { ensureOwnStatus(user); - const status = PresenceStore.getState()?.clientStatuses?.[user.id] as Record; + const status = PresenceStore.getClientStatus(user.id) as Record; if (!status) return []; return Object.entries(status).map(([platform, status]) => ({ @@ -134,11 +133,9 @@ function getBadges({ userId }: BadgeUserArgs): ProfileBadge[] { } const PlatformIndicator = ({ user, small = false }: { user: User; small?: boolean; }) => { - if (!user || user.bot) return null; - ensureOwnStatus(user); - const status = PresenceStore.getState()?.clientStatuses?.[user.id] as Record; + const status = useStateFromStores([PresenceStore], () => PresenceStore.getClientStatus(user.id) as Record); if (!status) return null; const icons = Object.entries(status).map(([platform, status]) => ( @@ -170,10 +167,8 @@ const badge: ProfileBadge = { const indicatorLocations = { list: { description: "In the member list", - onEnable: () => addMemberListDecorator("platform-indicator", props => - - - + onEnable: () => addMemberListDecorator("platform-indicator", ({ user }) => + user && !user.bot ? : null ), onDisable: () => removeMemberListDecorator("platform-indicator") }, @@ -184,11 +179,10 @@ const indicatorLocations = { }, messages: { description: "Inside messages", - onEnable: () => addMessageDecoration("platform-indicator", props => - - - - ), + onEnable: () => addMessageDecoration("platform-indicator", props => { + const user = props.message?.author; + return user && !user.bot ? : null; + }), onDisable: () => removeMessageDecoration("platform-indicator") } }; @@ -201,19 +195,6 @@ export default definePlugin({ start() { const settings = Settings.plugins.PlatformIndicators; - const { displayMode } = settings; - - // transfer settings from the old ones, which had a select menu instead of booleans - if (displayMode) { - if (displayMode !== "both") settings[displayMode] = true; - else { - settings.list = true; - settings.badges = true; - } - settings.messages = true; - delete settings.displayMode; - } - Object.entries(indicatorLocations).forEach(([key, value]) => { if (settings[key]) value.onEnable(); }); diff --git a/src/utils/types.ts b/src/utils/types.ts index 2293b2ffa..2d21eafff 100644 --- a/src/utils/types.ts +++ b/src/utils/types.ts @@ -151,9 +151,9 @@ export interface PluginDef { /** * Allows you to subscribe to Flux events */ - flux?: { - [E in LiteralUnion]?: (event: any) => void | Promise; - }; + flux?: Partial<{ + [E in LiteralUnion]: (event: any) => void | Promise; + }>; /** * Allows you to manipulate context menus */ From 7c839be64fbef65b4825ec1d17acd0264aadcd64 Mon Sep 17 00:00:00 2001 From: thororen <78185467+thororen1234@users.noreply.github.com> Date: Wed, 24 Sep 2025 10:49:50 -0400 Subject: [PATCH 3/4] BetterFolders: close folder if the last server is removed (#3658) Co-authored-by: V --- src/plugins/betterFolders/FolderSideBar.tsx | 23 +++++++++++++++++---- src/plugins/betterFolders/index.tsx | 5 ++--- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/plugins/betterFolders/FolderSideBar.tsx b/src/plugins/betterFolders/FolderSideBar.tsx index 40329122a..0e643958e 100644 --- a/src/plugins/betterFolders/FolderSideBar.tsx +++ b/src/plugins/betterFolders/FolderSideBar.tsx @@ -21,24 +21,39 @@ import { findComponentByCodeLazy, findStoreLazy } from "@webpack"; import { Animations, useStateFromStores } from "@webpack/common"; import type { CSSProperties } from "react"; -import { ExpandedGuildFolderStore, settings } from "."; +import { ExpandedGuildFolderStore, settings, SortedGuildStore } from "."; const ChannelRTCStore = findStoreLazy("ChannelRTCStore"); const GuildsBar = findComponentByCodeLazy('("guildsnav")'); +function getExpandedFolderIds() { + const expandedFolders = ExpandedGuildFolderStore.getExpandedFolders(); + const folders = SortedGuildStore.getGuildFolders(); + + const expandedFolderIds = new Set(); + + for (const folder of folders) { + if (expandedFolders.has(folder.folderId) && folder.guildIds?.length) { + expandedFolderIds.add(folder.folderId); + } + } + + return expandedFolderIds; +} + export default ErrorBoundary.wrap(guildsBarProps => { - const expandedFolders = useStateFromStores([ExpandedGuildFolderStore], () => ExpandedGuildFolderStore.getExpandedFolders()); + const expandedFolderIds = useStateFromStores([ExpandedGuildFolderStore, SortedGuildStore], () => getExpandedFolderIds()); const isFullscreen = useStateFromStores([ChannelRTCStore], () => ChannelRTCStore.isFullscreenInContext()); const Sidebar = ( ); - const visible = !!expandedFolders.size; + const visible = !!expandedFolderIds.size; const guilds = document.querySelector(guildsBarProps.className.split(" ").map(c => `.${c}`).join("")); // We need to display none if we are in fullscreen. Yes this seems horrible doing with css, but it's literally how Discord does it. diff --git a/src/plugins/betterFolders/index.tsx b/src/plugins/betterFolders/index.tsx index 62567f51d..5a1754d31 100644 --- a/src/plugins/betterFolders/index.tsx +++ b/src/plugins/betterFolders/index.tsx @@ -22,7 +22,7 @@ import { definePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; import { getIntlMessage } from "@utils/discord"; import definePlugin, { OptionType } from "@utils/types"; -import { findByPropsLazy, findLazy, findStoreLazy } from "@webpack"; +import { findByPropsLazy, findStoreLazy } from "@webpack"; import { FluxDispatcher } from "@webpack/common"; import { ReactNode } from "react"; @@ -35,8 +35,7 @@ enum FolderIconDisplay { } export const ExpandedGuildFolderStore = findStoreLazy("ExpandedGuildFolderStore"); -const SortedGuildStore = findStoreLazy("SortedGuildStore"); -const GuildsTree = findLazy(m => m.prototype?.moveNextTo); +export const SortedGuildStore = findStoreLazy("SortedGuildStore"); const FolderUtils = findByPropsLazy("move", "toggleGuildFolderExpand"); let lastGuildId = null as string | null; From c5a1bbd7db2472cfb7cfc62a0cec59f3135e479e Mon Sep 17 00:00:00 2001 From: Vendicated Date: Thu, 25 Sep 2025 15:19:34 +0200 Subject: [PATCH 4/4] GameActivityToggle: fix background colour when using nameplate --- src/plugins/gameActivityToggle/index.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/plugins/gameActivityToggle/index.tsx b/src/plugins/gameActivityToggle/index.tsx index 97885177b..8959acc29 100644 --- a/src/plugins/gameActivityToggle/index.tsx +++ b/src/plugins/gameActivityToggle/index.tsx @@ -60,7 +60,7 @@ function makeIcon(showCurrentGame?: boolean) { }; } -function GameActivityToggleButton() { +function GameActivityToggleButton(props: { nameplate?: any; }) { const showCurrentGame = ShowCurrentGame.useSetting(); return ( @@ -70,6 +70,7 @@ function GameActivityToggleButton() { role="switch" aria-checked={!showCurrentGame} redGlow={!showCurrentGame} + plated={props?.nameplate != null} onClick={() => ShowCurrentGame.updateSetting(old => !old)} /> ); @@ -97,7 +98,7 @@ export default definePlugin({ find: "#{intl::ACCOUNT_SPEAKING_WHILE_MUTED}", replacement: { match: /className:\i\.buttons,.{0,50}children:\[/, - replace: "$&$self.GameActivityToggleButton()," + replace: "$&$self.GameActivityToggleButton(arguments[0])," } } ],