diff --git a/cspell.json b/cspell.json index ec580088b..a8e37bdf1 100644 --- a/cspell.json +++ b/cspell.json @@ -33,6 +33,7 @@ "installdir", "Jsonifiable", "konami", + "leaderboard", "lezer", "LOCALAPPDATA", "logname", diff --git a/src/renderer/apis/commands.ts b/src/renderer/apis/commands.ts index be3f26871..a6cc8742b 100644 --- a/src/renderer/apis/commands.ts +++ b/src/renderer/apis/commands.ts @@ -1,4 +1,5 @@ import type { Channel, Guild, User } from "discord-types/general"; +import { REPLUGGED_CLYDE_ID } from "../../constants"; import type { AnyRepluggedCommand, CommandOptionReturn, @@ -12,12 +13,12 @@ import type { } from "../../types"; // eslint-disable-next-line no-duplicate-imports import { ApplicationCommandOptionType } from "../../types"; +import icon from "../assets/logo.png"; import { constants, i18n, messages, users } from "../modules/common"; import type { Store } from "../modules/common/flux"; import { Logger } from "../modules/logger"; import { filters, getByStoreName, waitForModule } from "../modules/webpack"; -import icon from "../assets/logo.png"; -import { REPLUGGED_CLYDE_ID } from "../../constants"; + const logger = Logger.api("Commands"); let RepluggedUser: User | undefined; @@ -204,9 +205,9 @@ export class CommandManager { } /** - * Code to register an slash command - * @param cmd Slash Command to be registered - * @returns An Callback to unregister the slash command + * Code to register a slash command + * @param command Slash command to be registered + * @returns A callback to unregister the slash command */ public registerCommand(command: RepluggedCommand): () => void { if (!commandAndSections.has(this.#section.id)) { @@ -215,8 +216,10 @@ export class CommandManager { commands: new Map(), }); } - const currentSection = commandAndSections.get(this.#section.id); - command.applicationId = currentSection?.section.id; + + const currentSection = commandAndSections.get(this.#section.id)!; // Can't be undefined as we set it above + command.section = currentSection.section; + command.applicationId = currentSection.section.id; command.displayName ??= command.name; command.displayDescription ??= command.description; command.type = 2; @@ -233,15 +236,16 @@ export class CommandManager { return option; }); - currentSection?.commands.set(command.id, command as AnyRepluggedCommand); + currentSection.commands.set(command.id, command as AnyRepluggedCommand); const uninject = (): void => { - void currentSection?.commands.delete(command.id!); + void currentSection.commands.delete(command.id!); this.#unregister = this.#unregister.filter((u) => u !== uninject); }; this.#unregister.push(uninject); return uninject; } + /** * Code to unregister all slash commands registered with this class */ diff --git a/src/renderer/coremods/commands/index.ts b/src/renderer/coremods/commands/index.ts index 8d671c571..ec168606b 100644 --- a/src/renderer/coremods/commands/index.ts +++ b/src/renderer/coremods/commands/index.ts @@ -1,151 +1,104 @@ -import type { AnyRepluggedCommand, RepluggedCommandSection } from "../../../types"; +import { type Store } from "@common/flux"; import type { Channel, Guild } from "discord-types/general"; -import flux, { type Store } from "@common/flux"; +import { REPLUGGED_CLYDE_ID } from "../../../constants"; +import type { AnyRepluggedCommand, RepluggedCommandSection } from "../../../types"; +import { commandAndSections, defaultSection } from "../../apis/commands"; import { Injector } from "../../modules/injector"; -import { Logger } from "../../modules/logger"; import { filters, + getByStoreName, getFunctionKeyBySource, waitForModule, waitForProps, } from "../../modules/webpack"; - -import { commandAndSections, defaultSection } from "../../apis/commands"; import { loadCommands, unloadCommands } from "./commands"; -import { REPLUGGED_CLYDE_ID } from "../../../constants"; -const logger = Logger.api("Commands"); const injector = new Injector(); -type CommandState = - | { - fetchState: { fetching: boolean }; - result: { - sectionIdsByBotId: Record; - sections: Record< - string, - { commands: Record; descriptor: RepluggedCommandSection } - >; - version: string; - }; - serverVersion: symbol | string; - } - | undefined; +interface QueryOptions { + commandTypes: number[]; + applicationCommands?: boolean; + text?: string; + builtIns?: "allow" | "only_text" | "deny"; +} + +interface FetchOptions { + applicationId?: string; + allowFetch?: boolean; + scoreMethod?: "none" | "application_only" | "command_only" | "command_or_application"; + allowEmptySections?: boolean; + allowApplicationState?: boolean; + sortOptions?: { + applications?: { useFrecency: boolean; useScore: boolean }; + commands?: { useFrecency: boolean; useScore: boolean }; + }; + installOnDemand?: boolean; + includeFrecency?: boolean; +} + +interface ApplicationCommandIndex { + descriptors: RepluggedCommandSection[]; + commands: AnyRepluggedCommand[]; + sectionedCommands: Array<{ data: AnyRepluggedCommand[]; section: RepluggedCommandSection }>; + loading: boolean; +} interface ApplicationCommandIndexStore extends Store { - getContextState: (channel: Channel) => CommandState; - getUserState: () => CommandState; query: ( channel: Channel, - queryOptions: { - commandType: number; - text: string; - }, - fetchOptions: { - allowFetch: boolean; - limit: number; - includeFrecency?: boolean; - placeholderCount?: number; - scoreMethod?: string; - }, - ) => - | { - descriptors: RepluggedCommandSection[]; - commands: AnyRepluggedCommand[]; - sectionedCommands: Array<{ data: AnyRepluggedCommand[]; section: RepluggedCommandSection }>; - loading: boolean; - } - | undefined; + queryOptions: QueryOptions, + fetchOptions: FetchOptions, + ) => ApplicationCommandIndex; } -interface ApplicationCommandIndexStoreMod { - useContextIndexState: ( - channel: Channel, - allowCache: boolean, - allowFetch: boolean, - ) => CommandState; - useDiscoveryState: ( - channel: Channel, - guild: Guild, - commandOptions: { - commandType: number; - applicationCommands?: boolean; - builtIns?: "allow" | "deny"; - }, - fetchOptions: { - allowFetch: boolean; - limit: number; - includeFrecency?: boolean; - placeholderCount?: number; - scoreMethod?: string; - }, - ) => - | { - descriptors: RepluggedCommandSection[]; - commands: AnyRepluggedCommand[]; - loading: boolean; - sectionedCommands: Array<{ data: AnyRepluggedCommand[]; section: RepluggedCommandSection }>; - } - | undefined; - useGuildIndexState: (guildId: string, allowFetch: boolean) => CommandState; - useUserIndexState: (allowCache: boolean, allowFetch: boolean) => CommandState; - default: ApplicationCommandIndexStore; -} +type UseDiscoveryState = ( + channel: Channel, + guild: Guild, + queryOptions: QueryOptions, + fetchOptions: FetchOptions, +) => ApplicationCommandIndex; + +type FetchProfile = (id: string) => Promise; async function injectRepluggedBotIcon(): Promise { - // Adds Avatar for replugged to default avatar to be used by system bot just like clyde + // Adds avatar for Replugged to be used by system bot just like Clyde // Ain't removing it on stop because we have checks here - const DefaultAvatars = await waitForProps<{ - BOT_AVATARS: Record | undefined; + const avatarUtilsMod = await waitForProps<{ + BOT_AVATARS: Record; }>("BOT_AVATARS"); - if (DefaultAvatars.BOT_AVATARS) { - DefaultAvatars.BOT_AVATARS.replugged = defaultSection.icon; - } else { - logger.error("Error while injecting custom icon for slash command replies."); - } + avatarUtilsMod.BOT_AVATARS.replugged = defaultSection.icon; } async function injectRepluggedSectionIcon(): Promise { // Patches the function which gets icon URL for slash command sections // makes it return the custom url if it's our section - const AssetsUtils = await waitForProps<{ + const avatarUtilsMod = await waitForProps<{ getApplicationIconURL: (args: { id: string; icon: string }) => string; }>("getApplicationIconURL"); - injector.after(AssetsUtils, "getApplicationIconURL", ([section], res) => + injector.after(avatarUtilsMod, "getApplicationIconURL", ([section], res) => commandAndSections.has(section.id) ? commandAndSections.get(section.id)?.section.icon : res, ); } async function injectApplicationCommandIndexStore(): Promise { - // The module which contains the store - /* - const ApplicationCommandIndexStoreMod = await waitForProps( - "useContextIndexState", - "useDiscoveryState", - "useGuildIndexState", - "useUserIndexState", - ); - */ - const ApplicationCommandIndexStoreMod = await waitForModule( + const applicationCommandIndexStoreMod = await waitForModule>( filters.bySource("APPLICATION_COMMAND_INDEX"), ); - - // This "as" hack is horrible. const useDiscoveryStateKey = getFunctionKeyBySource( - ApplicationCommandIndexStoreMod, + applicationCommandIndexStoreMod, "includeFrecency", - ) as "useDiscoveryState"; + )!; // Base handler function for ApplicationCommandIndexStore which is ran to get the info in store // commands are mainly added here injector.after( - ApplicationCommandIndexStoreMod, + applicationCommandIndexStoreMod, useDiscoveryStateKey, - ([, , { commandType }], res) => { + ([, , { commandTypes }], res) => { const commandAndSectionsArray = Array.from(commandAndSections.values()).filter( (commandAndSection) => commandAndSection.commands.size, ); - if (!res || !commandAndSectionsArray.length || commandType !== 1) return res; + if (!commandAndSectionsArray.length || !commandTypes.includes(1)) return res; if ( !Array.isArray(res.descriptors) || !commandAndSectionsArray.every((commandAndSection) => @@ -212,11 +165,9 @@ async function injectApplicationCommandIndexStore(): Promise { }, ); - // The store itself - //const ApplicationCommandIndexStore = ApplicationCommandIndexStoreMod.default; - const ApplicationCommandIndexStore = Object.values(ApplicationCommandIndexStoreMod).find( - (v) => v instanceof flux.Store, - ) as ApplicationCommandIndexStore; + const ApplicationCommandIndexStore = getByStoreName( + "ApplicationCommandIndexStore", + )!; // Slash command indexing patched to return our slash commands too // only those which match tho @@ -234,7 +185,7 @@ async function injectApplicationCommandIndexStore(): Promise { ), })) .filter((commandAndSection) => commandAndSection.commands.length); - if (!res || !commandAndSectionsArray.length) return res; + if (!commandAndSectionsArray.length) return res; if ( !Array.isArray(res.descriptors) || @@ -286,20 +237,17 @@ async function injectApplicationCommandIndexStore(): Promise { } async function injectProfileFetch(): Promise { - //const mod = await waitForProps<{ - // fetchProfile: (id: string) => Promise; - //}>("fetchProfile"); - const mod = await waitForModule Promise>>( + const userActionCreatorsMod = await waitForModule>( filters.bySource("fetchProfile"), ); - const fetchProfileKey = getFunctionKeyBySource(mod, "fetchProfile")!; - injector.instead(mod, fetchProfileKey, (args, res) => { - if (args[0] === REPLUGGED_CLYDE_ID) { - return; - } - return res(...args); + const fetchProfileKey = getFunctionKeyBySource(userActionCreatorsMod, "fetchProfile")!; + + injector.instead(userActionCreatorsMod, fetchProfileKey, (args, orig) => { + if (args[0] === REPLUGGED_CLYDE_ID) return; + return orig(...args); }); } + export async function start(): Promise { await injectRepluggedBotIcon(); await injectRepluggedSectionIcon(); @@ -307,6 +255,7 @@ export async function start(): Promise { await injectProfileFetch(); loadCommands(); } + export function stop(): void { injector.uninjectAll(); unloadCommands(); diff --git a/src/renderer/coremods/commands/plaintextPatches.ts b/src/renderer/coremods/commands/plaintextPatches.ts deleted file mode 100644 index 0b80980c4..000000000 --- a/src/renderer/coremods/commands/plaintextPatches.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { PlaintextPatch } from "src/types"; - -export default [ - { - //disables api request to find commands if its added by replugged - find: "filteredSectionId:null", - replacements: [ - { - match: /\w+\({applicationId:(\w+)}/, - replace: (suffix, id) => `${id} == "replugged"||${suffix}`, - }, - ], - }, -] as PlaintextPatch[]; diff --git a/src/renderer/coremods/language/plaintextPatches.ts b/src/renderer/coremods/language/plaintextPatches.ts index a7f8acfed..2f00ec4c7 100644 --- a/src/renderer/coremods/language/plaintextPatches.ts +++ b/src/renderer/coremods/language/plaintextPatches.ts @@ -13,7 +13,8 @@ export default [ }, { match: /children:\[(.+?\.localeName[^\]]*?)]/, - replace: (_, ogChild) => `children:[${coremodStr}?.Percentage(${ogChild}) ?? ${ogChild}]`, + replace: (_, ogChild) => + `children:${coremodStr}?.Percentage?${coremodStr}.Percentage(${ogChild}):[${ogChild}]`, }, ], }, diff --git a/src/renderer/coremods/messagePopover/index.ts b/src/renderer/coremods/messagePopover/index.tsx similarity index 61% rename from src/renderer/coremods/messagePopover/index.ts rename to src/renderer/coremods/messagePopover/index.tsx index edd461075..9b36de0df 100644 --- a/src/renderer/coremods/messagePopover/index.ts +++ b/src/renderer/coremods/messagePopover/index.tsx @@ -1,5 +1,6 @@ +import { getBySource, getFunctionBySource } from "@webpack"; import type { Channel, Message } from "discord-types/general"; -import type { GetButtonItem, IconButtonProps } from "../../../types/coremods/message"; +import type { GetButtonItem, HoverBarButtonProps } from "../../../types/coremods/message"; import { Logger } from "../../modules/logger"; const logger = Logger.api("MessagePopover"); @@ -27,26 +28,33 @@ export function removeButton(item: GetButtonItem): void { buttons.delete(item); } +type HoverBarButton = React.FC; + /** * @internal * @hidden */ -export function _buildPopoverElements( - msg: Message, - channel: Channel, - makeButton: (item: IconButtonProps) => React.ComponentType, -): React.ComponentType[] { - const items = [] as React.ComponentType[]; +export function _buildPopoverElements(msg: Message, channel: Channel): React.ReactElement[] { + const items: React.ReactElement[] = []; + + // Waiting for the module is not necessary, as it is already loaded by the time this function is called + const hoverBarButtonStr = ".hoverBarButton"; + const HoverBarButton = getFunctionBySource( + getBySource(hoverBarButtonStr), + hoverBarButtonStr, + ); + if (!HoverBarButton) { + logger.error("Could not find HoverBarButton"); + return items; + } buttons.forEach((key, getItem) => { try { const item = getItem(msg, channel); try { if (item) { - item.message ??= msg; - item.channel ??= channel; item.key = key; - items.push(makeButton(item)); + items.push(); } } catch (err) { logger.error(`Error in making the button [${item?.key}]`, err, item); diff --git a/src/renderer/coremods/messagePopover/plaintextPatches.ts b/src/renderer/coremods/messagePopover/plaintextPatches.ts index a578f34ec..7e327b16a 100644 --- a/src/renderer/coremods/messagePopover/plaintextPatches.ts +++ b/src/renderer/coremods/messagePopover/plaintextPatches.ts @@ -2,13 +2,12 @@ import type { PlaintextPatch } from "src/types"; export default [ { - find: 'key:"copy-id"', + find: "Messages.MESSAGE_UTILITIES_A11Y_LABEL", replacements: [ { - match: - /(Fragment,{children:\[)(.{0,200}children:\[.{0,20}?(\w{1,3})\({.{0,5}\s?key:"copy-id".{0,20}channel:(.{1,3})[,}].{0,20}message:(.{1,3})[,}])/, - replace: (_, prefix, suffix, makeButton, channel, message) => - `${prefix}...(replugged.coremods.coremods.messagePopover?._buildPopoverElements(${message},${channel},${makeButton}) ?? []),${suffix}`, + match: /(\.Fragment,{children:\[)(.{1,75},{label:\w+\.\w+\.Messages\.COPY_ID_MESSAGE)/, + replace: (_, prefix, suffix) => + `${prefix}...(replugged.coremods.coremods.messagePopover?._buildPopoverElements(arguments[0]?.message,arguments[0]?.channel) ?? []),${suffix}`, }, ], }, diff --git a/src/renderer/coremods/notices/plaintextPatches.tsx b/src/renderer/coremods/notices/plaintextPatches.ts similarity index 56% rename from src/renderer/coremods/notices/plaintextPatches.tsx rename to src/renderer/coremods/notices/plaintextPatches.ts index fd96d2006..e24a8da80 100644 --- a/src/renderer/coremods/notices/plaintextPatches.tsx +++ b/src/renderer/coremods/notices/plaintextPatches.ts @@ -7,9 +7,9 @@ export default [ find: /hasNotice:\w+,sidebarTheme:\w+/, replacements: [ { - match: /(\w+\.base,children:\[)(.+?}\)),/, + match: /(\w+\.base,children:\[.{0,50})(\w+\.\w+\?null.{10,30}}\)),/, replace: (_, prefix, noticeWrapper) => - `${prefix}${coremodStr}?.AnnouncementContainer({originalRes:${noticeWrapper}}) ?? ${noticeWrapper},`, + `${prefix}${coremodStr}?.AnnouncementContainer?${coremodStr}.AnnouncementContainer({originalRes:${noticeWrapper}}):${noticeWrapper},`, }, ], }, diff --git a/src/renderer/coremods/notrack/index.ts b/src/renderer/coremods/notrack/index.ts deleted file mode 100644 index 273b304b9..000000000 --- a/src/renderer/coremods/notrack/index.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { waitForProps } from "@webpack"; -import { Injector } from "@replugged"; - -const inj = new Injector(); - -export async function start(): Promise { - const { AnalyticsActionHandlers } = await waitForProps<{ - AnalyticsActionHandlers: { - handleConnectionClosed: () => void; - handleConnectionOpen: (e: unknown) => void; - handleFingerprint: () => void; - handleTrack: (e: unknown) => void; - }; - }>("AnalyticsActionHandlers"); - - inj.instead(AnalyticsActionHandlers, "handleTrack", () => {}); -} - -export function stop(): void { - inj.uninjectAll(); -} diff --git a/src/renderer/coremods/notrack/plaintextPatches.ts b/src/renderer/coremods/notrack/plaintextPatches.ts index efba16a67..e774a27f1 100644 --- a/src/renderer/coremods/notrack/plaintextPatches.ts +++ b/src/renderer/coremods/notrack/plaintextPatches.ts @@ -2,14 +2,11 @@ import type { PlaintextPatch } from "src/types"; export default [ { + find: "AnalyticsActionHandlers.handleTrack", replacements: [ { - match: /window\.DiscordSentry=function\(\){/, - replace: "$&return;", - }, - { - match: /crossDomainError=function\(\){/, - replace: "$&return;", + match: /=>\w+\.AnalyticsActionHandlers\.handle\w+\([^)]*\)/g, + replace: "=>{}", }, ], }, @@ -17,7 +14,7 @@ export default [ find: "window.DiscordSentry", replacements: [ { - match: /null!=window.DiscordSentry/g, + match: /\w+=window\.DiscordSentry/g, replace: "false", }, ], @@ -27,18 +24,10 @@ export default [ replacements: [{ match: /updateCrashReporter\(\w+\){/, replace: "$&return;" }], }, { - find: "TRACKING_URL:", - replacements: [ - { - replace: "", - }, - ], - }, - { - find: /this\._metrics\.push\(.\);/, + find: /this\._metrics\.push\(.\),/, replacements: [ { - match: /this\._metrics\.push\(.\);/, + match: /this\._metrics\.push\(.\),/, replace: "", }, ], diff --git a/src/renderer/managers/coremods.ts b/src/renderer/managers/coremods.ts index e975c24ab..4d2b1a845 100644 --- a/src/renderer/managers/coremods.ts +++ b/src/renderer/managers/coremods.ts @@ -8,7 +8,6 @@ import { default as messagePopover } from "../coremods/messagePopover/plaintextP import { default as notices } from "../coremods/notices/plaintextPatches"; import { default as contextMenu } from "../coremods/contextMenu/plaintextPatches"; import { default as languagePlaintext } from "../coremods/language/plaintextPatches"; -import { default as commandsPlaintext } from "../coremods/commands/plaintextPatches"; import { default as settingsPlaintext } from "../coremods/settings/plaintextPatches"; import { default as badgesPlaintext } from "../coremods/badges/plaintextPatches"; import { Logger } from "../modules/logger"; @@ -25,7 +24,6 @@ export namespace coremods { export let noDevtoolsWarning: Coremod; export let settings: Coremod; export let badges: Coremod; - export let notrack: Coremod; export let installer: Coremod; export let messagePopover: Coremod; export let notices: Coremod; @@ -60,7 +58,6 @@ export async function startAll(): Promise { coremods.watcher = await import("../coremods/watcher"); coremods.commands = await import("../coremods/commands"); coremods.welcome = await import("../coremods/welcome"); - coremods.notrack = await import("../coremods/notrack"); await Promise.all( Object.entries(coremods).map(async ([name, mod]) => { @@ -86,7 +83,6 @@ export function runPlaintextPatches(): void { notices, contextMenu, languagePlaintext, - commandsPlaintext, settingsPlaintext, badgesPlaintext, ].forEach(patchPlaintext); diff --git a/src/renderer/modules/common/channels.ts b/src/renderer/modules/common/channels.ts index e365ac26f..57dd370a5 100644 --- a/src/renderer/modules/common/channels.ts +++ b/src/renderer/modules/common/channels.ts @@ -29,6 +29,7 @@ export interface ChannelStore { getChannel(channelId: string): Channel | undefined; getChannelIds(guildId?: string): string[]; getDebugInfo(): DebugInfo; + getDMChannelFromUserId(userId: string): Channel | undefined; getDMFromUserId(userId: string): string | undefined; getDMUserIds(): string[]; getGuildChannelsVersion(guildId: string): number; diff --git a/src/renderer/modules/common/components.ts b/src/renderer/modules/common/components.ts index 6e0bf66e2..8d3484076 100644 --- a/src/renderer/modules/common/components.ts +++ b/src/renderer/modules/common/components.ts @@ -1,5 +1,6 @@ import type { LoaderType } from "@components"; import type { ClickableCompType } from "@components/Clickable"; +import type { NoticeType } from "@components/Notice"; import type { OriginalTextType } from "@components/Text"; import type { ButtonType } from "../components/ButtonItem"; import type { CheckboxType } from "../components/CheckboxItem"; @@ -31,6 +32,8 @@ interface DiscordComponents { FormSwitch: SwitchItemType; FormText: FormTextCompType; FormTextTypes: Record; + HelpMessage: NoticeType; + HelpMessageTypes: NoticeType["HelpMessageTypes"]; Menu: ContextMenuType["ContextMenu"]; MenuCheckboxItem: ContextMenuType["MenuCheckboxItem"]; MenuControlItem: ContextMenuType["MenuControlItem"]; diff --git a/src/renderer/modules/common/toast.ts b/src/renderer/modules/common/toast.ts index 3bb98e049..aff944e49 100644 --- a/src/renderer/modules/common/toast.ts +++ b/src/renderer/modules/common/toast.ts @@ -7,6 +7,10 @@ const Kind = { FAILURE: 2, CUSTOM: 3, CLIP: 4, + LINK: 5, + FORWARD: 6, + BOOKMARK: 7, + CLOCK: 8, } as const; const Position = { diff --git a/src/renderer/modules/common/users.ts b/src/renderer/modules/common/users.ts index 4d8c0b06c..72753624e 100644 --- a/src/renderer/modules/common/users.ts +++ b/src/renderer/modules/common/users.ts @@ -33,6 +33,7 @@ export interface UserStore { } export interface GuildMemberStore { + getCachedSelfMember: (guildId: string) => GuildMember | null; getCommunicationDisabledUserMap: () => Record; getCommunicationDisabledVersion: () => number; getMember: (guildId: string, userId: string) => GuildMember | null; diff --git a/src/renderer/modules/components/ButtonItem.tsx b/src/renderer/modules/components/ButtonItem.tsx index 0f4fd6ba9..1fc80c733 100644 --- a/src/renderer/modules/components/ButtonItem.tsx +++ b/src/renderer/modules/components/ButtonItem.tsx @@ -18,22 +18,22 @@ interface ButtonProps extends React.ComponentPropsWithoutRef<"button"> { submittingFinishedLabel?: string; } -interface Path { +interface Location { pathname?: string; search?: string; + state?: S; hash?: string; + key?: string; } -interface LinkProps extends Omit, "href"> { +interface LinkProps extends React.AnchorHTMLAttributes { + component?: React.ComponentType; + to: string | Location | ((location: Location) => string | Location); replace?: boolean; - state?: unknown; - to: string | Path; - reloadDocument?: boolean; - preventScrollReset?: boolean; - relative?: "route" | "path"; + innerRef?: React.Ref; } -interface ButtonLinkProps extends LinkProps { +interface ButtonLinkProps extends LinkProps { look?: string; color?: string; size?: string; diff --git a/src/renderer/modules/components/Notice.tsx b/src/renderer/modules/components/Notice.tsx index 3e750b934..202f3d43d 100644 --- a/src/renderer/modules/components/Notice.tsx +++ b/src/renderer/modules/components/Notice.tsx @@ -1,36 +1,29 @@ +import components from "@common/components"; import type React from "react"; -import { filters, waitForModule } from "../webpack"; import type { Variant } from "./Text"; -const Types = { +const HelpMessageTypes = { WARNING: 0, INFO: 1, ERROR: 2, POSITIVE: 3, } as const; -interface NoticeProps { +interface HelpMessageProps { children: React.ReactNode; - messageType: (typeof Types)[keyof typeof Types]; + messageType: (typeof HelpMessageTypes)[keyof typeof HelpMessageTypes]; textColor?: string; textVariant?: Variant; className?: string; } -export type NoticeType = React.FC & { - Types: typeof Types; // for backwards compat - HelpMessageTypes: typeof Types; - default: React.FC; +export type NoticeType = React.FC & { + Types: typeof HelpMessageTypes; // for backwards compat + HelpMessageTypes: typeof HelpMessageTypes; }; -const NoticeComp = await waitForModule(filters.bySource("WARNING=0]")); -//const Notice = NoticeComp.default as NoticeType; -// Notice component is a top-level exported function -const Notice = Object.values(NoticeComp).find((v) => typeof v === "function") as NoticeType; -// Help Message Types are a top-level exported object -Notice.HelpMessageTypes = Object.values(NoticeComp).find((v) => "INFO" in v)! as typeof Types; -Notice.Types = Notice.HelpMessageTypes; -//Notice.Types = NoticeComp.HelpMessageTypes; -//Notice.HelpMessageTypes = NoticeComp.HelpMessageTypes; +const { HelpMessage } = components; +HelpMessage.HelpMessageTypes = components.HelpMessageTypes; +HelpMessage.Types = HelpMessage.HelpMessageTypes; -export default Notice; +export default HelpMessage; diff --git a/src/renderer/modules/components/RadioItem.tsx b/src/renderer/modules/components/RadioItem.tsx index 3182e5949..6a853ccaf 100644 --- a/src/renderer/modules/components/RadioItem.tsx +++ b/src/renderer/modules/components/RadioItem.tsx @@ -13,6 +13,8 @@ type RadioOptionType = { tooltipPosition?: "top" | "bottom" | "left" | "right" | "center" | "window_center"; icon?: React.ComponentType; collapsibleContent?: React.ReactNode; + radioItemIconClassName?: string; + radioBarClassName?: string; }; interface RadioProps { diff --git a/src/renderer/modules/components/SwitchItem.tsx b/src/renderer/modules/components/SwitchItem.tsx index 4bcaf79d0..0f9539451 100644 --- a/src/renderer/modules/components/SwitchItem.tsx +++ b/src/renderer/modules/components/SwitchItem.tsx @@ -19,6 +19,7 @@ interface SwitchItemProps { note?: string; tooltipNote?: string; disabled?: boolean; + disabledText?: string; hideBorder?: boolean; style?: React.CSSProperties; className?: string; diff --git a/src/renderer/modules/components/Tooltip.tsx b/src/renderer/modules/components/Tooltip.tsx index e28bc96c5..e59364245 100644 --- a/src/renderer/modules/components/Tooltip.tsx +++ b/src/renderer/modules/components/Tooltip.tsx @@ -22,16 +22,7 @@ interface TooltipEnums { Aligns: typeof Aligns; Positions: typeof Positions; Colors: Record< - | "PRIMARY" - | "NESTED" - | "BLACK" - | "GREY" - | "BRAND" - | "GREEN" - | "YELLOW" - | "RED" - | "CUSTOM" - | "PREMIUM", + "PRIMARY" | "NESTED" | "BLACK" | "GREY" | "BRAND" | "GREEN" | "YELLOW" | "RED" | "PREMIUM", string >; } @@ -44,6 +35,7 @@ interface BaseTooltipProps { spacing?: number; delay?: number; allowOverflow?: boolean; + overflowOnly?: boolean; disableTooltipPointerEvents?: boolean; forceOpen?: boolean; hideOnClick?: boolean; @@ -53,9 +45,11 @@ interface BaseTooltipProps { className?: string; tooltipClassName?: string; tooltipContentClassName?: string; + tooltipPointerClassName?: string; style?: React.CSSProperties; tooltipStyle?: React.CSSProperties; onTooltipShow?: () => void; + onTooltipHide?: () => void; onAnimationRest?: (result: unknown, spring: unknown, item?: unknown) => void; } diff --git a/src/renderer/modules/injector.ts b/src/renderer/modules/injector.ts index 8be38446b..b78072de4 100644 --- a/src/renderer/modules/injector.ts +++ b/src/renderer/modules/injector.ts @@ -320,7 +320,7 @@ export class Injector { * By default, items are placed in a group for custom items, though that can be customized with `sectionId` and `indexInSection` * @param navId The id of the menu to add to * @param item The function that creates the item to add - * @param sectionId — The number of the section to add to. Defaults to replugged's section + * @param sectionId — The number of the section to add to. Defaults to Replugged's section * @param indexInSection — The index in the section to add to. Defaults to the end position * @returns A callback to de-register the function * @@ -372,7 +372,7 @@ export class Injector { * * @example * ``` - * import { Injector, components, types } from "replugged"; + * import { Injector, types } from "replugged"; * * const injector = new Injector(); * diff --git a/src/types/coremods/commands.ts b/src/types/coremods/commands.ts index 95349f84f..df031871a 100644 --- a/src/types/coremods/commands.ts +++ b/src/types/coremods/commands.ts @@ -54,6 +54,7 @@ export type GetValueType = undefined extends T : T["value"]; export interface InexecutableRepluggedCommand { + section?: RepluggedCommandSection; applicationId?: string; type?: number; id?: string; diff --git a/src/types/coremods/contextMenu.ts b/src/types/coremods/contextMenu.ts index ffaffd22e..745b38d14 100644 --- a/src/types/coremods/contextMenu.ts +++ b/src/types/coremods/contextMenu.ts @@ -28,6 +28,7 @@ export enum ContextMenuTypes { Account = "account", ActivityShelfItemContext = "activity-shelf-item-context", AddQuestions = "add-questions", + AppDetailsMoreMenu = "app-details-more-menu", ApplicationDirectoryProfile = "application-directory-profile", AttachmentLinkContext = "attachment-link-context", /** Right-click mute or deafen buttons */ @@ -36,20 +37,23 @@ export enum ContextMenuTypes { ChannelAttach = "channel-attach", ChannelAutocomplete = "channel-autocomplete", ChannelCallOverflowPopout = "channel-call-overflow-popout", - ChannelNotificationCustomSettingsItems = "ChannelNotificationCustomSettingsItems", - ClipsContext = "clips-context", - ClipsMoreOptions = "clips-more-options", - CommandListSort = "command-list-sort", /** Right-click channel */ ChannelContext = "channel-context", /** Right-click a channel mention */ ChannelMentionContext = "channel-mention-context", ChannelSummariesContextMenu = "channel-summaries-context-menu", + ChannelNotificationCustomSettingsItems = "ChannelNotificationCustomSettingsItems", + ClipsContext = "clips-context", + ClipsMoreOptions = "clips-more-options", + CommandListSort = "command-list-sort", ComponentButton = "component-button", + ContentInventoryContext = "content-inventory-context", CopyId = "copy-id", + Demo = "demo", /** Right-click a role */ DevContext = "dev-context", DevtoolsOverflow = "devtools-overflow", + DiscoveryEntrypointContextMenu = "discovery-entrypoint-context-menu", EditProfilePopout = "edit-profile-popout", ExitOptions = "exit-options", /** Right-click an emoji or sticker in the popout */ @@ -57,11 +61,15 @@ export enum ContextMenuTypes { /** Right-click the favorites server icon */ FavoriteServerContext = "favorite-server-context", FavoritesHeaderPopout = "favorites-header-popout", + ForumTag = "forum-tag", /** Click the triple dots icon for a user in the home screen */ FriendRow = "friend-row", GameContext = "game-context", + GameProfileContext = "game-profile-context", /** Right-click a group chat */ GdmContext = "gdm-context", + GlobalDiscoverySearchFilterOptions = "global-discovery-search-filter-options", + GlobalDiscoveryTabsOverflowMenu = "global-discovery-tabs-overflow-menu", /** Right-click "Browse Channels" */ GuildBrowseChannelsContextMenu = "guild-browse-channels-context-menu", /** Right-click guild icon */ @@ -77,14 +85,17 @@ export enum ContextMenuTypes { /** Right-click a role in the guild settings */ GuildSettingsRoleContext = "guild-settings-role-context", GuildShopContext = "guild-shop-context", + ImageContextCommandPopout = "image-context-commands-popout", ImageContext = "image-context", JoinRequestGuildContext = "join-request-guild-context", LaunchContext = "launch-context", + LeaderboardPopoutContextMenu = "leaderboard-popout-context-menu", ManageBroadcast = "manage-broadcast", ManageIntegration = "manage-integration", ManageMultiAccount = "manage-multi-account", ManageStreams = "manage-streams", MemberApplicationContextMenu = "member-application-context-menu", + MemberApplicationsTabsOverflowMenu = "member-applications-tabs-overflow-menu", MemberListSettingsMenu = "member-list-settings-menu", MemberSafetyFlags = "member-safety-flags", MembersTableJoinMethodMenu = "members-table-join-method-menu", @@ -92,10 +103,12 @@ export enum ContextMenuTypes { MentionsFilter = "mentions-filter", /** Click the triple dots on a message popover */ MessageActions = "message-actions", + MessageReminderCreate = "message-reminder-create", MessageReminderSnooze = "message-reminder-snooze", /** Right-click message */ Message = "message", ModerationRaidContext = "moderation-raid-context", + NonUserBotProfileOverflowMenu = "non-user-bot-profile-overflow-menu", NotificationActions = "notification-actions", NowPlayingMenu = "now-playing-menu", Overlay = "overlay", @@ -110,6 +123,8 @@ export enum ContextMenuTypes { RtcChannel = "rtc-channel", SearchResults = "search-results", SetImageForAction = "set-image-for-action", + SetStatusSubmenuMobileWeb = "set-status-submenu-mobile-web", + SetStatusSubmenu = "set-status-submenu", SignupButtonContext = "signup-button-context", SortAndView = "sort-and-view", SoundButtonContext = "sound-button-context", @@ -121,6 +136,7 @@ export enum ContextMenuTypes { Status = "status", StreamContext = "stream-context", SubscriptionContext = "subscription-context", + SwitchAccountsSubmenu = "switch-accounts-submenu", TestSkus = "test-skus", TestStoreListing = "test-store-listing", TextContext = "text-context", @@ -129,6 +145,8 @@ export enum ContextMenuTypes { ThreadContext = "thread-context", TransferMenu = "transfer-menu", UnknownUserContext = "unknown-user-context", + UserBotProfileAddApp = "user-bot-profile-add-app", + UserBotProfileOverflowMenu = "user-bot-profile-overflow-menu", /** Right-click user */ UserContext = "user-context", UserProfileActions = "user-profile-actions", diff --git a/src/types/coremods/message.ts b/src/types/coremods/message.ts index 9fe1a3d4f..92a2cd915 100644 --- a/src/types/coremods/message.ts +++ b/src/types/coremods/message.ts @@ -7,19 +7,19 @@ interface ButtonPopoverProps extends React.ComponentPropsWithoutRef<"div"> { dangerous?: boolean; } -export interface IconButtonProps extends Omit { +export interface HoverBarButtonProps extends ButtonPopoverProps { label: string; - channel?: Channel; - message?: Message; - onClick: (channel: Channel, message: Message, event: React.MouseEvent) => void; ariaLabel?: string; tooltipText?: string; tooltipColor?: string; icon: React.ComponentType; iconProps?: Record; - key?: string; + onTooltipShow?: () => void; + onTooltipHide?: () => void; separator?: boolean; sparkle?: boolean; + showNewBadge?: boolean; + buttonClassName?: string; } -export type GetButtonItem = (message: Message, channel: Channel) => IconButtonProps | null; +export type GetButtonItem = (message: Message, channel: Channel) => HoverBarButtonProps | null;