diff --git a/packages/discord-types/src/utils.d.ts b/packages/discord-types/src/utils.d.ts index 1a8af6323..77ef1212a 100644 --- a/packages/discord-types/src/utils.d.ts +++ b/packages/discord-types/src/utils.d.ts @@ -6,15 +6,17 @@ import type { FluxEvents } from "./fluxEvents"; export { FluxEvents }; +type FluxEventsAutoComplete = LiteralUnion; + export interface FluxDispatcher { _actionHandlers: any; _interceptors: any; _subscriptions: any; addInterceptor(interceptor: any): void; - 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/_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/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 1164ac1b6..7e29ec0ee 100644 --- a/src/plugins/betterFolders/index.tsx +++ b/src/plugins/betterFolders/index.tsx @@ -34,7 +34,7 @@ enum FolderIconDisplay { } export const ExpandedGuildFolderStore = findStoreLazy("ExpandedGuildFolderStore"); -const SortedGuildStore = findStoreLazy("SortedGuildStore"); +export const SortedGuildStore = findStoreLazy("SortedGuildStore"); const FolderUtils = findByPropsLazy("move", "toggleGuildFolderExpand"); let lastGuildId = null as string | null; diff --git a/src/plugins/gameActivityToggle/index.tsx b/src/plugins/gameActivityToggle/index.tsx index 9c0a2429d..2332af2e1 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 ( @@ -71,6 +71,7 @@ function GameActivityToggleButton() { role="switch" aria-checked={!showCurrentGame} redGlow={!showCurrentGame} + plated={props?.nameplate != null} onClick={() => ShowCurrentGame.updateSetting(old => !old)} /> ); @@ -98,7 +99,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])," } } ], diff --git a/src/plugins/index.ts b/src/plugins/index.ts index b2b066c49..eb2ba73ce 100644 --- a/src/plugins/index.ts +++ b/src/plugins/index.ts @@ -233,7 +233,7 @@ export function subscribePluginFluxEvents(p: Plugin, fluxDispatcher: typeof Flux const wrappedHandler = p.flux[event] = function () { if (p.name === "Encryptcord" && event === "MESSAGE_CREATE") return; 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; @@ -253,7 +253,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 2c659c451..e3b253ef6 100644 --- a/src/plugins/platformIndicators/index.tsx +++ b/src/plugins/platformIndicators/index.tsx @@ -27,7 +27,7 @@ import { classes } from "@utils/misc"; import definePlugin, { OptionType } from "@utils/types"; import { User } from "@vencord/discord-types"; import { filters, findStoreLazy, mapMangledModuleLazy } from "@webpack"; -import { PresenceStore, Tooltip, UserStore, useStateFromStores } from "@webpack/common"; +import { AuthenticationStore, PresenceStore, Tooltip, UserStore, useStateFromStores } from "@webpack/common"; export interface Session { sessionId: string; @@ -113,7 +113,7 @@ const PlatformIcon = ({ platform, status, small }: PlatformIconProps) => { }; function useEnsureOwnStatus(user: User) { - if (user.id !== UserStore.getCurrentUser()?.id) { + if (user.id !== AuthenticationStore.getId()) { return; } diff --git a/src/utils/types.ts b/src/utils/types.ts index 0f81d7bdf..f369f86d5 100644 --- a/src/utils/types.ts +++ b/src/utils/types.ts @@ -27,6 +27,7 @@ import { MessagePopoverButtonFactory } from "@api/MessagePopover"; import { NicknameIconFactory } from "@api/NicknameIcons"; 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,9 +152,9 @@ export interface PluginDef { /** * Allows you to subscribe to Flux events */ - flux?: { - [E in FluxEvents]?: (event: any) => void | Promise; - }; + flux?: Partial<{ + [E in LiteralUnion]: (event: any) => void | Promise; + }>; /** * Allows you to manipulate context menus */