Vencord/src/webpack/common/utils.ts
2024-07-02 17:39:48 +02:00

182 lines
6.5 KiB
TypeScript

/*
* 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 <https://www.gnu.org/licenses/>.
*/
import { canonicalizeMatch } from "@utils/patches";
import type { Channel } from "discord-types/general";
// eslint-disable-next-line path-alias/no-relative
import { _resolveReady, filters, findByCodeLazy, findByPropsLazy, findLazy, mapMangledModuleLazy, waitFor } from "../webpack";
import type * as t from "./types/utils";
export let FluxDispatcher: t.FluxDispatcher;
waitFor(["dispatch", "subscribe"], m => {
FluxDispatcher = m;
// Non import call to avoid circular dependency
Vencord.Plugins.subscribeAllPluginsFluxEvents(m);
const cb = () => {
m.unsubscribe("CONNECTION_OPEN", cb);
_resolveReady();
};
m.subscribe("CONNECTION_OPEN", cb);
});
export let ComponentDispatch;
waitFor(["dispatchToLastSubscribed"], m => ComponentDispatch = m);
export const Constants: t.Constants = mapMangledModuleLazy('ME:"/users/@me"', {
Endpoints: filters.byProps("USER", "ME"),
UserFlags: filters.byProps("STAFF", "SPAMMER"),
FriendsSections: m => m.PENDING === "PENDING" && m.ADD_FRIEND
});
export const RestAPI: t.RestAPI = findLazy(m => typeof m === "object" && m.del && m.put);
export const moment: typeof import("moment") = findByPropsLazy("parseTwoDigitYear");
export const hljs: typeof import("highlight.js") = findByPropsLazy("highlight", "registerLanguage");
export const lodash: typeof import("lodash") = findByPropsLazy("debounce", "cloneDeep");
export const i18n: t.i18n = findLazy(m => m.Messages?.["en-US"]);
export let SnowflakeUtils: t.SnowflakeUtils;
waitFor(["fromTimestamp", "extractTimestamp"], m => SnowflakeUtils = m);
export let Parser: t.Parser;
waitFor("parseTopic", m => Parser = m);
export let Alerts: t.Alerts;
waitFor(["show", "close"], m => Alerts = m);
const ToastType = {
MESSAGE: 0,
SUCCESS: 1,
FAILURE: 2,
CUSTOM: 3
};
const ToastPosition = {
TOP: 0,
BOTTOM: 1
};
export interface ToastData {
message: string,
id: string,
/**
* Toasts.Type
*/
type: number,
options?: ToastOptions;
}
export interface ToastOptions {
/**
* Toasts.Position
*/
position?: number;
component?: React.ReactNode,
duration?: number;
}
export const Toasts = {
Type: ToastType,
Position: ToastPosition,
// what's less likely than getting 0 from Math.random()? Getting it twice in a row
genId: () => (Math.random() || Math.random()).toString(36).slice(2),
// hack to merge with the following interface, dunno if there's a better way
...{} as {
show(data: ToastData): void;
pop(): void;
create(message: string, type: number, options?: ToastOptions): ToastData;
}
};
// This is the same module but this is easier
waitFor("showToast", m => {
Toasts.show = m.showToast;
Toasts.pop = m.popToast;
Toasts.create = m.createToast;
});
/**
* Show a simple toast. If you need more options, use Toasts.show manually
*/
export function showToast(message: string, type = ToastType.MESSAGE, options?: ToastOptions) {
Toasts.show(Toasts.create(message, type, options));
}
export const UserUtils = {
getUser: findByCodeLazy(".USER(")
};
export const UploadManager = findByPropsLazy("clearAll", "addFile");
export const UploadHandler = {
promptToUpload: findByCodeLazy(".ATTACHMENT_TOO_MANY_ERROR_TITLE,") as (files: File[], channel: Channel, draftType: Number) => void
};
export const ApplicationAssetUtils = findByPropsLazy("fetchAssetIds", "getAssetImage") as {
fetchAssetIds: (applicationId: string, e: string[]) => Promise<string[]>;
};
export const Clipboard: t.Clipboard = mapMangledModuleLazy('queryCommandEnabled("copy")', {
copy: filters.byCode(".copy("),
SUPPORTS_COPY: e => typeof e === "boolean"
});
export const NavigationRouter: t.NavigationRouter = mapMangledModuleLazy("Transitioning to ", {
transitionTo: filters.byCode("transitionTo -"),
transitionToGuild: filters.byCode("transitionToGuild -"),
back: filters.byCode("goBack()"),
forward: filters.byCode("goForward()"),
});
export let SettingsRouter: any;
waitFor(["open", "saveAccountChanges"], m => SettingsRouter = m);
export const PermissionsBits: t.PermissionsBits = findLazy(m => typeof m.ADMINISTRATOR === "bigint");
export const zustandCreate = findByCodeLazy("will be removed in v4");
export const zustandPersist = findByCodeLazy("[zustand persist middleware]");
export const MessageActions = findByPropsLazy("editMessage", "sendMessage");
export const MessageCache = findByPropsLazy("clearCache", "_channelMessages");
export const UserProfileActions = findByPropsLazy("openUserProfileModal", "closeUserProfileModal");
export const InviteActions = findByPropsLazy("resolveInvite");
export const IconUtils: t.IconUtils = findByPropsLazy("getGuildBannerURL", "getUserAvatarURL");
const openExpressionPickerMatcher = canonicalizeMatch(/setState\({activeView:\i,activeViewType:/);
// TODO: type
export const ExpressionPickerStore: t.ExpressionPickerStore = mapMangledModuleLazy("expression-picker-last-active-view", {
closeExpressionPicker: filters.byCode("setState({activeView:null"),
openExpressionPicker: m => typeof m === "function" && openExpressionPickerMatcher.test(m.toString()),
});
export const PopoutActions: t.PopoutActions = mapMangledModuleLazy('type:"POPOUT_WINDOW_OPEN"', {
open: filters.byCode('type:"POPOUT_WINDOW_OPEN"'),
close: filters.byCode('type:"POPOUT_WINDOW_CLOSE"'),
setAlwaysOnTop: filters.byCode('type:"POPOUT_WINDOW_SET_ALWAYS_ON_TOP"'),
});
export const UsernameUtils: t.UsernameUtils = findByPropsLazy("useName", "getGlobalName");
export const DisplayProfileUtils: t.DisplayProfileUtils = mapMangledModuleLazy(/=\i\.getUserProfile\(\i\),\i=\i\.getGuildMemberProfile\(/, {
getDisplayProfile: filters.byCode(".getGuildMemberProfile("),
useDisplayProfile: filters.byCode(/\[\i\.\i,\i\.\i],\(\)=>/)
});