diff --git a/bin/index.mts b/bin/index.mts index cb99799c1..b5140444e 100755 --- a/bin/index.mts +++ b/bin/index.mts @@ -419,6 +419,7 @@ async function buildPlugin({ watch, noInstall, production, noReload, addon }: Ar esbuild.context( overwrites({ ...common, + format: "cjs", entryPoints: [path.join(folderPath, manifest.plaintextPatches)], outfile: `${distPath}/plaintextPatches.js`, }), diff --git a/scripts/build-plugins/intl-loader.mts b/scripts/build-plugins/intl-loader.mts index 7276c919e..39e12fed6 100644 --- a/scripts/build-plugins/intl-loader.mts +++ b/scripts/build-plugins/intl-loader.mts @@ -69,13 +69,13 @@ export default { getTranslationImport: (importPath) => `import("${importPath}")`, debug: !production, preGenerateBinds: false, - getPrelude: () => `import {waitForProps} from '@webpack';`, + getPrelude: () => `import {getByProps} from '@webpack';`, }).getOutput(); return { contents: transformedOutput.replace( /require\('@discord\/intl'\);/, - "await waitForProps('createLoader','IntlManager');", + "getByProps('createLoader','IntlManager');", ), loader: "js", }; diff --git a/scripts/build.mts b/scripts/build.mts index 5e54d370e..389cdbc49 100644 --- a/scripts/build.mts +++ b/scripts/build.mts @@ -68,8 +68,12 @@ const contexts = await Promise.all([ entryPoints: ["src/renderer/index.ts"], platform: "browser", target: `chrome${CHROME_VERSION}`, + format: "iife", + footer: { + js: "//# sourceURL=replugged://RepluggedRenderer/renderer.js", + css: "/*# sourceURL=replugged://RepluggedRenderer/renderer.css */", + }, outfile: `${distDir}/renderer.js`, - format: "esm", loader: { ".png": "dataurl", }, diff --git a/src/main/index.ts b/src/main/index.ts index 8479570e1..b364c65ba 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -105,8 +105,8 @@ electron.protocol.registerSchemesAsPrivileged([ }, ]); -async function loadReactDevTools(): Promise { - const rdtSetting = await getSetting("dev.replugged.Settings", "reactDevTools", false); +function loadReactDevTools(): void { + const rdtSetting = getSetting("dev.replugged.Settings", "reactDevTools", false); if (rdtSetting) { void electron.session.defaultSession.loadExtension(CONFIG_PATHS["react-devtools"]); @@ -180,7 +180,7 @@ electron.app.once("ready", () => { cb({ path: filePath }); }); - void loadReactDevTools(); + loadReactDevTools(); }); // This module is required this way at runtime. diff --git a/src/main/ipc/index.ts b/src/main/ipc/index.ts index b9814fe9a..09e5972f4 100644 --- a/src/main/ipc/index.ts +++ b/src/main/ipc/index.ts @@ -6,7 +6,13 @@ import "./quick-css"; import "./react-devtools"; import "./settings"; import "./themes"; +import { readFileSync } from "fs"; +import { join } from "path"; ipcMain.on(RepluggedIpcChannels.GET_DISCORD_PRELOAD, (event) => { event.returnValue = (event.sender as RepluggedWebContents).originalPreload; }); + +ipcMain.on(RepluggedIpcChannels.GET_REPLUGGED_RENDERER, (event) => { + event.returnValue = readFileSync(join(__dirname, "./renderer.js"), "utf-8"); +}); diff --git a/src/main/ipc/installer.ts b/src/main/ipc/installer.ts index 67f24232b..9210d839d 100644 --- a/src/main/ipc/installer.ts +++ b/src/main/ipc/installer.ts @@ -105,7 +105,7 @@ async function github( } async function store(id: string): Promise { - const apiUrl = await getSetting("dev.replugged.Settings", "apiUrl", WEBSITE_URL); + const apiUrl = getSetting("dev.replugged.Settings", "apiUrl", WEBSITE_URL); const STORE_BASE_URL = `${apiUrl}/api/v1/store`; const manifestUrl = `${STORE_BASE_URL}/${id}`; const asarUrl = `${manifestUrl}.asar`; @@ -191,7 +191,7 @@ ipcMain.handle( if (type === "replugged") { // Manually set Path and URL for security purposes path = "replugged.asar"; - const apiUrl = await getSetting("dev.replugged.Settings", "apiUrl", WEBSITE_URL); + const apiUrl = getSetting("dev.replugged.Settings", "apiUrl", WEBSITE_URL); url = `${apiUrl}/api/v1/store/dev.replugged.Replugged.asar`; } diff --git a/src/main/ipc/plugins.ts b/src/main/ipc/plugins.ts index 67b9bc884..ec5dcad2e 100644 --- a/src/main/ipc/plugins.ts +++ b/src/main/ipc/plugins.ts @@ -4,12 +4,12 @@ IPC events: - REPLUGGED_UNINSTALL_PLUGIN: returns whether a plugin by the provided name was successfully uninstalled */ -import { readFile, readdir, readlink, rm, stat } from "fs/promises"; +import { rm } from "fs/promises"; import { extname, join, sep } from "path"; import { ipcMain, shell } from "electron"; import { RepluggedIpcChannels, type RepluggedPlugin } from "../../types"; import { plugin } from "../../types/addon"; -import type { Dirent, Stats } from "fs"; +import { type Dirent, type Stats, readFileSync, readdirSync, readlinkSync, statSync } from "fs"; import { CONFIG_PATHS } from "src/util.mjs"; const PLUGINS_DIR = CONFIG_PATHS.plugins; @@ -18,7 +18,7 @@ export const isFileAPlugin = (f: Dirent | Stats, name: string): boolean => { return f.isDirectory() || (f.isFile() && extname(name) === ".asar"); }; -async function getPlugin(pluginName: string): Promise { +function getPlugin(pluginName: string): RepluggedPlugin { const manifestPath = join(PLUGINS_DIR, pluginName, "manifest.json"); if (!manifestPath.startsWith(`${PLUGINS_DIR}${sep}`)) { // Ensure file changes are restricted to the base path @@ -26,7 +26,7 @@ async function getPlugin(pluginName: string): Promise { } const manifest: unknown = JSON.parse( - await readFile(manifestPath, { + readFileSync(manifestPath, { encoding: "utf-8", }), ); @@ -38,56 +38,66 @@ async function getPlugin(pluginName: string): Promise { }; const cssPath = data.manifest.renderer?.replace(/\.js$/, ".css"); - const hasCSS = - cssPath && - (await stat(join(PLUGINS_DIR, pluginName, cssPath)) - .then(() => true) - .catch(() => false)); + try { + const hasCSS = cssPath && statSync(join(PLUGINS_DIR, pluginName, cssPath)); - if (hasCSS) data.hasCSS = true; + if (hasCSS) data.hasCSS = true; + } catch { + data.hasCSS = false; + } return data; } -ipcMain.handle( - RepluggedIpcChannels.GET_PLUGIN, - async (_, pluginName: string): Promise => { - try { - return await getPlugin(pluginName); - } catch {} - }, -); +ipcMain.on(RepluggedIpcChannels.GET_PLUGIN, (event, pluginName: string) => { + try { + event.returnValue = getPlugin(pluginName); + } catch {} +}); -ipcMain.handle(RepluggedIpcChannels.LIST_PLUGINS, async (): Promise => { +ipcMain.on(RepluggedIpcChannels.LIST_PLUGINS, (event) => { const plugins = []; - const pluginDirs = ( - await Promise.all( - ( - await readdir(PLUGINS_DIR, { - withFileTypes: true, - }) - ).map(async (f) => { - if (isFileAPlugin(f, f.name)) return f; - if (f.isSymbolicLink()) { - const actualPath = await readlink(join(PLUGINS_DIR, f.name)); - const actualFile = await stat(actualPath); + const pluginDirs = readdirSync(PLUGINS_DIR, { + withFileTypes: true, + }) + .map((f) => { + if (isFileAPlugin(f, f.name)) return f; + if (f.isSymbolicLink()) { + try { + const actualPath = readlinkSync(join(PLUGINS_DIR, f.name)); + const actualFile = statSync(actualPath); + if (isFileAPlugin(actualFile, actualPath)) return f; - } - }), - ) - ).filter(Boolean) as Dirent[]; + } catch {} + } + + return void 0; + }) + .filter(Boolean) as Dirent[]; for (const pluginDir of pluginDirs) { try { - plugins.push(await getPlugin(pluginDir.name)); + plugins.push(getPlugin(pluginDir.name)); } catch (e) { console.error(`Invalid plugin: ${pluginDir.name}`); console.error(e); } } + event.returnValue = plugins; +}); + +ipcMain.on(RepluggedIpcChannels.READ_PLUGIN_PLAINTEXT_PATCHES, (event, pluginName) => { + const plugin = getPlugin(pluginName); + if (!plugin.manifest.plaintextPatches) return; + + const path = join(CONFIG_PATHS.plugins, pluginName, plugin.manifest.plaintextPatches); + if (!path.startsWith(`${PLUGINS_DIR}${sep}`)) { + // Ensure file changes are restricted to the base path + throw new Error("Invalid plugin name"); + } - return plugins; + if (path) event.returnValue = readFileSync(path, "utf-8"); }); ipcMain.handle(RepluggedIpcChannels.UNINSTALL_PLUGIN, async (_, pluginName: string) => { diff --git a/src/main/ipc/react-devtools.ts b/src/main/ipc/react-devtools.ts index 03b868b09..86a67cec8 100644 --- a/src/main/ipc/react-devtools.ts +++ b/src/main/ipc/react-devtools.ts @@ -11,7 +11,7 @@ const OUTPUT_PATH = join(CONFIG_PATHS["react-devtools"]); const ZIP_PATH = join(OUTPUT_PATH, "extension.zip"); ipcMain.handle(RepluggedIpcChannels.DOWNLOAD_REACT_DEVTOOLS, async () => { - const apiUrl = await getSetting("dev.replugged.Settings", "apiUrl", WEBSITE_URL); + const apiUrl = getSetting("dev.replugged.Settings", "apiUrl", WEBSITE_URL); const REACT_DEVTOOLS_URL = `${apiUrl}/api/v1/react-devtools`; let buffer; diff --git a/src/main/ipc/settings.ts b/src/main/ipc/settings.ts index f08f4811e..9180d02ab 100644 --- a/src/main/ipc/settings.ts +++ b/src/main/ipc/settings.ts @@ -1,4 +1,3 @@ -import { readFile, writeFile } from "fs/promises"; import { resolve, sep } from "path"; import { ipcMain, shell } from "electron"; import { RepluggedIpcChannels } from "../../types"; @@ -8,6 +7,7 @@ import type { TransactionHandler, } from "../../types/settings"; import { CONFIG_PATHS } from "src/util.mjs"; +import { readFileSync, writeFileSync } from "fs"; const SETTINGS_DIR = CONFIG_PATHS.settings; @@ -20,53 +20,49 @@ export function getSettingsPath(namespace: string): string { return resolved; } -async function readSettings(namespace: string): Promise> { +function readSettings(namespace: string): Map { const path = getSettingsPath(namespace); try { - const data = await readFile(path, "utf8"); + const data = readFileSync(path, "utf8"); return new Map(Object.entries(JSON.parse(data))); } catch { return new Map(); } } -function writeSettings(namespace: string, settings: SettingsMap): Promise { - return writeFile( +function writeSettings(namespace: string, settings: SettingsMap): void { + writeFileSync( getSettingsPath(namespace), JSON.stringify(Object.fromEntries(settings.entries()), null, 2), "utf8", ); } -const locks: Record | undefined> = {}; +const locks: Record unknown) | undefined> = {}; -async function transaction(namespace: string, handler: TransactionHandler): Promise { - const lock = locks[namespace] ?? Promise.resolve(); +function transaction(namespace: string, handler: TransactionHandler): T { + const lock = locks[namespace]; - const result = lock.then(() => handler()); + if (lock) lock(); - locks[namespace] = result.catch(() => {}); + const result = handler(); + + locks[namespace] = () => result; return result; } -export async function readTransaction( - namespace: string, - handler: SettingsTransactionHandler, -): Promise { - return transaction(namespace, async () => { - const settings = await readSettings(namespace); +export function readTransaction(namespace: string, handler: SettingsTransactionHandler): T { + return transaction(namespace, () => { + const settings = readSettings(namespace); return handler(settings); }); } -export async function writeTransaction( - namespace: string, - handler: SettingsTransactionHandler, -): Promise { - return transaction(namespace, async () => { - const postHandlerTransform: Array<(settings: SettingsMap) => void | Promise> = []; +export function writeTransaction(namespace: string, handler: SettingsTransactionHandler): T { + return transaction(namespace, () => { + const postHandlerTransform: Array<(settings: SettingsMap) => void | void> = []; - const settings = await readSettings(namespace); + const settings = readSettings(namespace); if (namespace.toLowerCase() === "dev.replugged.settings") { // Prevent the "apiUrl" setting from changing const originalValue = settings.get("apiUrl"); @@ -79,52 +75,53 @@ export async function writeTransaction( }); } - const res = await handler(settings); + const res = handler(settings); for (const transform of postHandlerTransform) { - await transform(settings); + transform(settings); } - await writeSettings(namespace, settings); + writeSettings(namespace, settings); return res; }); } -export async function getSetting(namespace: string, key: string, fallback: T): Promise; -export async function getSetting( - namespace: string, - key: string, - fallback?: T, -): Promise; -export async function getSetting( - namespace: string, - key: string, - fallback?: T, -): Promise { - const setting = (await readTransaction(namespace, (settings) => settings.get(key))) as T; +export function getSetting(namespace: string, key: string, fallback: T): T; +export function getSetting(namespace: string, key: string, fallback?: T): T | undefined; +export function getSetting(namespace: string, key: string, fallback?: T): T | undefined { + const setting = readTransaction(namespace, (settings) => settings.get(key)) as T; return setting ?? fallback; } -ipcMain.handle(RepluggedIpcChannels.GET_SETTING, async (_, namespace: string, key: string) => - getSetting(namespace, key), +ipcMain.on( + RepluggedIpcChannels.GET_SETTING, + (event, namespace: string, key: string) => (event.returnValue = getSetting(namespace, key)), ); -ipcMain.handle(RepluggedIpcChannels.HAS_SETTING, async (_, namespace: string, key: string) => - readTransaction(namespace, (settings) => settings.has(key)), +ipcMain.on( + RepluggedIpcChannels.HAS_SETTING, + (event, namespace: string, key: string) => + (event.returnValue = readTransaction(namespace, (settings) => settings.has(key))), ); -ipcMain.handle( +ipcMain.on( RepluggedIpcChannels.SET_SETTING, - (_, namespace: string, key: string, value: unknown) => - void writeTransaction(namespace, (settings) => settings.set(key, value)), + (event, namespace: string, key: string, value: unknown) => + (event.returnValue = writeTransaction(namespace, (settings) => settings.set(key, value))), ); -ipcMain.handle(RepluggedIpcChannels.DELETE_SETTING, (_, namespace: string, key: string) => - writeTransaction(namespace, (settings) => settings.delete(key)), +ipcMain.on( + RepluggedIpcChannels.DELETE_SETTING, + (event, namespace: string, key: string) => + (event.returnValue = writeTransaction(namespace, (settings) => settings.delete(key))), ); -ipcMain.handle(RepluggedIpcChannels.GET_ALL_SETTINGS, async (_, namespace: string) => - readTransaction(namespace, (settings) => Object.fromEntries(settings.entries())), +ipcMain.on( + RepluggedIpcChannels.GET_ALL_SETTINGS, + (event, namespace: string) => + (event.returnValue = readTransaction(namespace, (settings) => + Object.fromEntries(settings.entries()), + )), ); ipcMain.on(RepluggedIpcChannels.OPEN_SETTINGS_FOLDER, () => shell.openPath(SETTINGS_DIR)); diff --git a/src/preload.ts b/src/preload.ts index 0b8397545..f51f0672f 100644 --- a/src/preload.ts +++ b/src/preload.ts @@ -32,10 +32,11 @@ const RepluggedNative = { }, plugins: { - get: async (pluginPath: string): Promise => - ipcRenderer.invoke(RepluggedIpcChannels.GET_PLUGIN, pluginPath), - list: async (): Promise => - ipcRenderer.invoke(RepluggedIpcChannels.LIST_PLUGINS), + get: (pluginPath: string): RepluggedPlugin | undefined => + ipcRenderer.sendSync(RepluggedIpcChannels.GET_PLUGIN, pluginPath), + list: (): RepluggedPlugin[] => ipcRenderer.sendSync(RepluggedIpcChannels.LIST_PLUGINS), + readPlaintextPatch: (pluginPath: string): string | undefined => + ipcRenderer.sendSync(RepluggedIpcChannels.READ_PLUGIN_PLAINTEXT_PATCHES, pluginPath), uninstall: async (pluginPath: string): Promise => ipcRenderer.invoke(RepluggedIpcChannels.UNINSTALL_PLUGIN, pluginPath), openFolder: () => ipcRenderer.send(RepluggedIpcChannels.OPEN_PLUGINS_FOLDER), @@ -81,15 +82,15 @@ const RepluggedNative = { settings: { get: (namespace: string, key: string) => - ipcRenderer.invoke(RepluggedIpcChannels.GET_SETTING, namespace, key), + ipcRenderer.sendSync(RepluggedIpcChannels.GET_SETTING, namespace, key), set: (namespace: string, key: string, value: unknown) => - ipcRenderer.invoke(RepluggedIpcChannels.SET_SETTING, namespace, key, value), // invoke or send? + ipcRenderer.sendSync(RepluggedIpcChannels.SET_SETTING, namespace, key, value), // invoke or send? has: (namespace: string, key: string) => - ipcRenderer.invoke(RepluggedIpcChannels.HAS_SETTING, namespace, key), + ipcRenderer.sendSync(RepluggedIpcChannels.HAS_SETTING, namespace, key), delete: (namespace: string, key: string) => - ipcRenderer.invoke(RepluggedIpcChannels.DELETE_SETTING, namespace, key), + ipcRenderer.sendSync(RepluggedIpcChannels.DELETE_SETTING, namespace, key), all: (namespace: string) => - ipcRenderer.invoke(RepluggedIpcChannels.GET_ALL_SETTINGS, namespace), + ipcRenderer.sendSync(RepluggedIpcChannels.GET_ALL_SETTINGS, namespace), startTransaction: (namespace: string) => ipcRenderer.invoke(RepluggedIpcChannels.START_SETTINGS_TRANSACTION, namespace), endTransaction: (namespace: string, settings: Record | null) => @@ -115,7 +116,9 @@ export type RepluggedNativeType = typeof RepluggedNative; contextBridge.exposeInMainWorld("RepluggedNative", RepluggedNative); // webFrame.executeJavaScript returns a Promise, but we don't have any use for it -void webFrame.executeJavaScript('void import("replugged://renderer");'); +const renderer = ipcRenderer.sendSync(RepluggedIpcChannels.GET_REPLUGGED_RENDERER); + +void webFrame.executeJavaScript(renderer); try { window.addEventListener("beforeunload", () => { diff --git a/src/renderer/apis/settings.ts b/src/renderer/apis/settings.ts index 93708dd52..7ec3f22b0 100644 --- a/src/renderer/apis/settings.ts +++ b/src/renderer/apis/settings.ts @@ -108,8 +108,8 @@ export class SettingsManager, D extends ke * Loads the latest stored settings for this namespace from the user's file system into this manager. This must be called * before managing any settings, unless you have created an instance using {@link init init()}, which calls this method. */ - public async load(): Promise { - this.#settings = await window.RepluggedNative.settings.all(this.namespace); + public load(): void { + this.#settings = window.RepluggedNative.settings.all(this.namespace); } /** @@ -199,15 +199,15 @@ const managers = new Map(); * @param defaultSettings Default values for the settings in the namespace. These will be used if no value is set for a setting. Using the `fallback` parameter of {@link SettingsManager.get get()} will override these defaults. * @returns Manager for the namespace. */ -export async function init, D extends keyof T = never>( +export function init, D extends keyof T = never>( namespace: string, defaultSettings?: Partial, -): Promise> { +): SettingsManager { if (managers.has(namespace)) { return managers.get(namespace)! as SettingsManager; } const manager = new SettingsManager(namespace, (defaultSettings || {}) as Partial); managers.set(namespace, manager); - await manager.load(); + manager.load(); return manager; } diff --git a/src/renderer/coremods/badges/badges/Booster.tsx b/src/renderer/coremods/badges/badges/Booster.tsx index b215f73ed..92e94ba33 100644 --- a/src/renderer/coremods/badges/badges/Booster.tsx +++ b/src/renderer/coremods/badges/badges/Booster.tsx @@ -1,4 +1,4 @@ -import React from "@common/react"; +import { React } from "@common"; export default React.memo((props: React.ComponentPropsWithoutRef<"svg">) => ( diff --git a/src/renderer/coremods/badges/badges/BugHunter.tsx b/src/renderer/coremods/badges/badges/BugHunter.tsx index 6a7933c00..8bb039cf8 100644 --- a/src/renderer/coremods/badges/badges/BugHunter.tsx +++ b/src/renderer/coremods/badges/badges/BugHunter.tsx @@ -1,4 +1,4 @@ -import React from "@common/react"; +import { React } from "@common"; export default React.memo((props: React.ComponentPropsWithoutRef<"svg">) => ( diff --git a/src/renderer/coremods/badges/badges/Contributor.tsx b/src/renderer/coremods/badges/badges/Contributor.tsx index c106c095c..928faecee 100644 --- a/src/renderer/coremods/badges/badges/Contributor.tsx +++ b/src/renderer/coremods/badges/badges/Contributor.tsx @@ -1,4 +1,4 @@ -import React from "@common/react"; +import { React } from "@common"; export default React.memo((props: React.ComponentPropsWithoutRef<"svg">) => ( diff --git a/src/renderer/coremods/badges/badges/Developer.tsx b/src/renderer/coremods/badges/badges/Developer.tsx index 1efc4b359..21e194165 100644 --- a/src/renderer/coremods/badges/badges/Developer.tsx +++ b/src/renderer/coremods/badges/badges/Developer.tsx @@ -1,4 +1,4 @@ -import React from "@common/react"; +import { React } from "@common"; export default React.memo((props: React.ComponentPropsWithoutRef<"svg">) => ( diff --git a/src/renderer/coremods/badges/badges/EarlyUser.tsx b/src/renderer/coremods/badges/badges/EarlyUser.tsx index 0585561a2..84971b8a9 100644 --- a/src/renderer/coremods/badges/badges/EarlyUser.tsx +++ b/src/renderer/coremods/badges/badges/EarlyUser.tsx @@ -1,4 +1,4 @@ -import React from "@common/react"; +import { React } from "@common"; export default React.memo((props: React.ComponentPropsWithoutRef<"svg">) => ( diff --git a/src/renderer/coremods/badges/badges/Staff.tsx b/src/renderer/coremods/badges/badges/Staff.tsx index 1469611de..297b6f058 100644 --- a/src/renderer/coremods/badges/badges/Staff.tsx +++ b/src/renderer/coremods/badges/badges/Staff.tsx @@ -1,4 +1,4 @@ -import React from "@common/react"; +import { React } from "@common"; export default React.memo((props: React.ComponentPropsWithoutRef<"svg">) => ( diff --git a/src/renderer/coremods/badges/badges/Support.tsx b/src/renderer/coremods/badges/badges/Support.tsx index bae7a7bc1..cb7367be4 100644 --- a/src/renderer/coremods/badges/badges/Support.tsx +++ b/src/renderer/coremods/badges/badges/Support.tsx @@ -1,4 +1,4 @@ -import React from "@common/react"; +import { React } from "@common"; export default React.memo((props: React.ComponentPropsWithoutRef<"svg">) => ( diff --git a/src/renderer/coremods/badges/badges/Translator.tsx b/src/renderer/coremods/badges/badges/Translator.tsx index ad6ebe031..e02096aac 100644 --- a/src/renderer/coremods/badges/badges/Translator.tsx +++ b/src/renderer/coremods/badges/badges/Translator.tsx @@ -1,4 +1,4 @@ -import React from "@common/react"; +import { React } from "@common"; export default React.memo((props: React.ComponentPropsWithoutRef<"svg">) => ( diff --git a/src/renderer/coremods/badges/index.tsx b/src/renderer/coremods/badges/index.tsx index 02d8c1cf9..863c8cf51 100644 --- a/src/renderer/coremods/badges/index.tsx +++ b/src/renderer/coremods/badges/index.tsx @@ -1,5 +1,4 @@ -import { intl } from "@common/i18n"; -import React from "@common/react"; +import { React, i18n } from "@common"; import { Logger } from "@replugged"; import { filters, getFunctionKeyBySource, waitForModule } from "@webpack"; import { DISCORD_BLURPLE, DISCORD_INVITE, WEBLATE_URL } from "src/constants"; @@ -55,43 +54,47 @@ const inviteUrl = `https://discord.gg/${DISCORD_INVITE}`; const badgeElements = [ { id: "booster", - description: intl.string(t.REPLUGGED_BADGES_BOOSTER), + description: i18n.intl.string(t.REPLUGGED_BADGES_BOOSTER), component: Badges.Booster, link: inviteUrl, }, { id: "contributor", - description: intl.string(t.REPLUGGED_BADGES_CONTRIBUTOR), + description: i18n.intl.string(t.REPLUGGED_BADGES_CONTRIBUTOR), component: Badges.Contributor, link: contributorsUrl, }, { id: "developer", - description: intl.string(t.REPLUGGED_BADGES_DEVELOPER), + description: i18n.intl.string(t.REPLUGGED_BADGES_DEVELOPER), component: Badges.Developer, link: contributorsUrl, }, - { id: "early", description: intl.string(t.REPLUGGED_BADGES_EARLY), component: Badges.EarlyUser }, + { + id: "early", + description: i18n.intl.string(t.REPLUGGED_BADGES_EARLY), + component: Badges.EarlyUser, + }, { id: "hunter", - description: intl.string(t.REPLUGGED_BADGES_HUNTER), + description: i18n.intl.string(t.REPLUGGED_BADGES_HUNTER), component: Badges.BugHunter, }, { id: "staff", - description: intl.string(t.REPLUGGED_BADGES_STAFF), + description: i18n.intl.string(t.REPLUGGED_BADGES_STAFF), component: Badges.Staff, link: inviteUrl, }, { id: "support", - description: intl.string(t.REPLUGGED_BADGES_SUPPORT), + description: i18n.intl.string(t.REPLUGGED_BADGES_SUPPORT), component: Badges.Support, link: inviteUrl, }, { id: "translator", - description: intl.string(t.REPLUGGED_BADGES_TRANSLATOR), + description: i18n.intl.string(t.REPLUGGED_BADGES_TRANSLATOR), component: Badges.Translator, link: WEBLATE_URL, }, diff --git a/src/renderer/coremods/commands/commands.ts b/src/renderer/coremods/commands/commands.ts index 26a5f34c5..472be8a09 100644 --- a/src/renderer/coremods/commands/commands.ts +++ b/src/renderer/coremods/commands/commands.ts @@ -1,10 +1,12 @@ -import { intl } from "@common/i18n"; +import { i18n } from "@common"; import { Injector, plugins, themes } from "@replugged"; import { t } from "src/renderer/modules/i18n"; import { ApplicationCommandOptionType } from "../../../types"; const injector = new Injector(); +const { intl } = i18n; + export function loadCommands(): void { injector.utils.registerSlashCommand({ name: intl.string(t.REPLUGGED_COMMAND_ENABLE_NAME), diff --git a/src/renderer/coremods/experiments/plaintextPatches.ts b/src/renderer/coremods/experiments/plaintextPatches.ts index 69b58724e..267367fbd 100644 --- a/src/renderer/coremods/experiments/plaintextPatches.ts +++ b/src/renderer/coremods/experiments/plaintextPatches.ts @@ -2,7 +2,7 @@ import { init } from "src/renderer/apis/settings"; import { type GeneralSettings, type PlaintextPatch, defaultSettings } from "src/types"; // TODO: see if we can import this from General.tsx -const generalSettings = await init( +const generalSettings = init( "dev.replugged.Settings", defaultSettings, ); diff --git a/src/renderer/coremods/installer/AddonEmbed.tsx b/src/renderer/coremods/installer/AddonEmbed.tsx index 7640e6ef3..4c5a33c22 100644 --- a/src/renderer/coremods/installer/AddonEmbed.tsx +++ b/src/renderer/coremods/installer/AddonEmbed.tsx @@ -1,5 +1,4 @@ -import { React } from "@common"; -import { intl } from "@common/i18n"; +import { React, i18n } from "@common"; import { Button, Clickable, Text, Tooltip } from "@components"; import { Logger } from "@replugged"; import { getByProps } from "@webpack"; @@ -11,6 +10,8 @@ import { InstallLinkProps, authorList, checkIsInstalled, getInfo, install } from import "./addonEmbed.css"; +const { intl } = i18n; + const logger = Logger.coremod("AddonEmbed"); type ClassMod = Record< diff --git a/src/renderer/coremods/installer/commands.ts b/src/renderer/coremods/installer/commands.ts index b7899a123..9316419d4 100644 --- a/src/renderer/coremods/installer/commands.ts +++ b/src/renderer/coremods/installer/commands.ts @@ -1,9 +1,11 @@ -import { intl } from "@common/i18n"; +import { i18n } from "@common"; import { Injector } from "@replugged"; import { t } from "src/renderer/modules/i18n"; import { ApplicationCommandOptionType } from "src/types"; import { INSTALLER_SOURCES, InstallerSource, installFlow, parseInstallLink } from "./util"; +const { intl } = i18n; + /** * A map of display names for installer sources. */ diff --git a/src/renderer/coremods/installer/util.tsx b/src/renderer/coremods/installer/util.tsx index 8893bea81..88698a80d 100644 --- a/src/renderer/coremods/installer/util.tsx +++ b/src/renderer/coremods/installer/util.tsx @@ -1,5 +1,4 @@ -import { modal, toast } from "@common"; -import { intl } from "@common/i18n"; +import { i18n, modal, toast } from "@common"; import { Button, Notice } from "@components"; import { Logger } from "@replugged"; import { setUpdaterState } from "src/renderer/managers/updater"; @@ -10,6 +9,8 @@ import * as themeManager from "../../managers/themes"; import { generalSettings, getAddonType, getSourceLink, label } from "../settings/pages"; import { t } from "src/renderer/modules/i18n"; +const { intl } = i18n; + const logger = Logger.coremod("Installer"); // First item is the default @@ -127,7 +128,7 @@ export async function loadNew(data: CheckResultSuccess): Promise { try { switch (data.manifest.type) { case "replugged-plugin": - await pluginManager.loadAll(); + pluginManager.loadAll(); await pluginManager.enable(data.manifest.id); return true; case "replugged-theme": diff --git a/src/renderer/coremods/language/index.tsx b/src/renderer/coremods/language/index.tsx index aa8c3a1e5..31de9a3c4 100644 --- a/src/renderer/coremods/language/index.tsx +++ b/src/renderer/coremods/language/index.tsx @@ -1,12 +1,14 @@ -import { getLanguages, intl } from "@common/i18n"; +import { i18n } from "@common"; import { Flex, FormNotice, Text } from "@components"; -import { messagesLoader } from "i18n/en-US.messages"; +import { messagesLoader } from "i18n/en-US.messages.js"; import React from "react"; import { WEBLATE_URL } from "src/constants"; import { t } from "../../modules/i18n"; export const percentages = new Map(); +const { getLanguages, intl } = i18n; + export function Card(): React.ReactElement { return ( >; } -const actualNoticeMod = await waitForModule>>( - filters.bySource(".colorPremiumTier1,"), -); - const remappedNoticeMod: NoticeMod = { - NoticeColors: Object.values(actualNoticeMod).find( + NoticeColors: {} as NoticeMod["NoticeColors"], + NoticeButton: () => null, + PrimaryCTANoticeButton: () => null, + NoticeButtonAnchor: () => null, + NoticeCloseButton: () => null, + Notice: () => null, +}; + +const mapNoticeMod = async (): Promise => { + const actualNoticeMod = await waitForModule>>( + filters.bySource(".colorPremiumTier1,"), + ); + + remappedNoticeMod.NoticeColors = Object.values(actualNoticeMod).find( (v) => typeof v === "object", - ) as NoticeMod["NoticeColors"], - NoticeButton: getFunctionBySource(actualNoticeMod, "buttonMinor")!, - PrimaryCTANoticeButton: getFunctionBySource(actualNoticeMod, "CTA")!, - NoticeButtonAnchor: getFunctionBySource(actualNoticeMod, ".Anchor")!, - NoticeCloseButton: getFunctionBySource(actualNoticeMod, "closeIcon")!, - Notice: getFunctionBySource(actualNoticeMod, "isMobile")!, + ) as NoticeMod["NoticeColors"]; + remappedNoticeMod.NoticeButton = getFunctionBySource(actualNoticeMod, "buttonMinor")!; + remappedNoticeMod.PrimaryCTANoticeButton = getFunctionBySource(actualNoticeMod, "CTA")!; + remappedNoticeMod.NoticeButtonAnchor = getFunctionBySource(actualNoticeMod, ".Anchor")!; + remappedNoticeMod.NoticeCloseButton = getFunctionBySource(actualNoticeMod, "closeIcon")!; + remappedNoticeMod.Notice = getFunctionBySource(actualNoticeMod, "isMobile")!; }; +void mapNoticeMod(); + export default remappedNoticeMod; diff --git a/src/renderer/coremods/settings/index.tsx b/src/renderer/coremods/settings/index.tsx index 96b4dda2c..d3d9627f1 100644 --- a/src/renderer/coremods/settings/index.tsx +++ b/src/renderer/coremods/settings/index.tsx @@ -1,10 +1,12 @@ -import { t as discordT, intl } from "@common/i18n"; +import { i18n } from "@common"; import { Text } from "@components"; import { Injector } from "@replugged"; import { t } from "src/renderer/modules/i18n"; import { Divider, Header, Section, insertSections, settingsTools } from "./lib"; import { General, Plugins, QuickCSS, Themes, Updater } from "./pages"; +const { t: discordT, intl } = i18n; + const injector = new Injector(); export { insertSections }; diff --git a/src/renderer/coremods/settings/pages/Addons.tsx b/src/renderer/coremods/settings/pages/Addons.tsx index 1b27cc782..c21d1c176 100644 --- a/src/renderer/coremods/settings/pages/Addons.tsx +++ b/src/renderer/coremods/settings/pages/Addons.tsx @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-unnecessary-condition */ -import { React, api, fluxDispatcher, modal, toast, users } from "@common"; -import { t as discordT, intl } from "@common/i18n"; +import { React, api, fluxDispatcher, i18n, modal, toast, users } from "@common"; import { + Breadcrumb, Button, Divider, ErrorBoundary, @@ -12,7 +12,7 @@ import { TextInput, Tooltip, } from "@components"; -import { Logger, plugins, themes, webpack } from "@replugged"; +import { Logger, plugins, themes } from "@replugged"; import { t } from "src/renderer/modules/i18n"; import { openExternal } from "src/renderer/util"; import type { RepluggedPlugin, RepluggedTheme } from "src/types"; @@ -22,23 +22,9 @@ import { generalSettings } from "./General"; import "./Addons.css"; -interface Breadcrumb { - id: string; - label: string; -} - -interface BreadcrumbProps { - activeId: string; - breadcrumbs: Breadcrumb[]; - onBreadcrumbClick: (breadcrumb: Breadcrumb) => void; - renderCustomBreadcrumb: (breadcrumb: Breadcrumb, active: boolean) => React.ReactNode; -} - const logger = Logger.coremod("AddonSettings"); -const Breadcrumbs = await webpack.waitForModule>( - webpack.filters.bySource(/\w+.breadcrumbFinalWrapper/), -); +const { t: discordT, intl } = i18n; export enum AddonType { Plugin = "plugin", @@ -158,7 +144,7 @@ async function loadMissing(type: AddonType): Promise { const manager = plugins; const disabled = manager.getDisabled(); const existingPlugins = new Set(manager.plugins.keys()); - await manager.loadAll(); + manager.loadAll(); const newPlugins = Array.from(manager.plugins.keys()).filter( (x) => !existingPlugins.has(x) && !disabled.includes(x), ); @@ -553,7 +539,7 @@ export const Addons = (type: AddonType): React.ReactElement => { })} ) : ( - ( +export const generalSettings = settings.init( "dev.replugged.Settings", defaultSettings, ); @@ -38,9 +36,9 @@ function restartModal(doRelaunch = false, onConfirm?: () => void, onCancel?: () const restart = doRelaunch ? relaunch : reload; void modal .confirm({ - title: intl.string(t.REPLUGGED_SETTINGS_RESTART_TITLE), - body: intl.string(t.REPLUGGED_SETTINGS_RESTART), - confirmText: intl.string(t.REPLUGGED_RESTART), + title: i18n.intl.string(t.REPLUGGED_SETTINGS_RESTART_TITLE), + body: i18n.intl.string(t.REPLUGGED_SETTINGS_RESTART), + confirmText: i18n.intl.string(t.REPLUGGED_RESTART), confirmColor: Button.Colors.RED, onConfirm, onCancel, @@ -87,35 +85,35 @@ export const General = (): React.ReactElement => { return ( <> - {intl.string(t.REPLUGGED_GENERAL_SETTINGS)} + {i18n.intl.string(t.REPLUGGED_GENERAL_SETTINGS)} - {intl.string(t.REPLUGGED_SETTINGS_BADGES)} + note={i18n.intl.string(t.REPLUGGED_SETTINGS_BADGES_DESC)}> + {i18n.intl.string(t.REPLUGGED_SETTINGS_BADGES)} - {intl.string(t.REPLUGGED_SETTINGS_ADDON_EMBEDS)} + note={i18n.intl.string(t.REPLUGGED_SETTINGS_ADDON_EMBEDS_DESC)}> + {i18n.intl.string(t.REPLUGGED_SETTINGS_ADDON_EMBEDS)} - {intl.string(t.REPLUGGED_SETTINGS_QUICKCSS_AUTO_APPLY)} + note={i18n.intl.string(t.REPLUGGED_SETTINGS_QUICKCSS_AUTO_APPLY_DESC)}> + {i18n.intl.string(t.REPLUGGED_SETTINGS_QUICKCSS_AUTO_APPLY)} + title={i18n.intl.string(t.REPLUGGED_SETTINGS_ADVANCED)} + note={i18n.intl.string(t.REPLUGGED_SETTINGS_ADVANCED_DESC)}> { expOnChange(value); restartModal(false); }} - note={intl.format(t.REPLUGGED_SETTINGS_DISCORD_EXPERIMENTS_DESC, {})}> - {intl.string(t.REPLUGGED_SETTINGS_DISCORD_EXPERIMENTS)} + note={i18n.intl.format(t.REPLUGGED_SETTINGS_DISCORD_EXPERIMENTS_DESC, {})}> + {i18n.intl.string(t.REPLUGGED_SETTINGS_DISCORD_EXPERIMENTS)} { .catch(() => { rdtOnChange(false); // Disable if failed toast.toast( - intl.string(t.REPLUGGED_SETTINGS_REACT_DEVTOOLS_FAILED), + i18n.intl.string(t.REPLUGGED_SETTINGS_REACT_DEVTOOLS_FAILED), toast.Kind.FAILURE, ); }); @@ -157,18 +155,18 @@ export const General = (): React.ReactElement => { restartModal(true); } }} - note={intl.format(t.REPLUGGED_SETTINGS_REACT_DEVTOOLS_DESC, {})}> - {intl.string(t.REPLUGGED_SETTINGS_REACT_DEVTOOLS)} + note={i18n.intl.format(t.REPLUGGED_SETTINGS_REACT_DEVTOOLS_DESC, {})}> + {i18n.intl.string(t.REPLUGGED_SETTINGS_REACT_DEVTOOLS)} { socket?.close(1000, "Reconnecting"); initWs(true); }}> - {intl.string(t.REPLUGGED_SETTINGS_DEV_COMPANION)} + {i18n.intl.string(t.REPLUGGED_SETTINGS_DEV_COMPANION)} diff --git a/src/renderer/coremods/settings/pages/QuickCSS.tsx b/src/renderer/coremods/settings/pages/QuickCSS.tsx index dae21f8c8..596f5fb25 100644 --- a/src/renderer/coremods/settings/pages/QuickCSS.tsx +++ b/src/renderer/coremods/settings/pages/QuickCSS.tsx @@ -1,7 +1,6 @@ import { css } from "@codemirror/lang-css"; import { EditorState } from "@codemirror/state"; -import { React, toast } from "@common"; -import { intl } from "@common/i18n"; +import { React, i18n, toast } from "@common"; import { Button, Divider, Flex, Text } from "@components"; import { webpack } from "@replugged"; import { EditorView, basicSetup } from "codemirror"; @@ -11,6 +10,8 @@ import { generalSettings } from "./General"; import "./QuickCSS.css"; +const { intl } = i18n; + interface UseCodeMirrorOptions { value?: string; onChange?: (code: string) => unknown; diff --git a/src/renderer/coremods/settings/pages/Updater.tsx b/src/renderer/coremods/settings/pages/Updater.tsx index a799f8250..0fad9f423 100644 --- a/src/renderer/coremods/settings/pages/Updater.tsx +++ b/src/renderer/coremods/settings/pages/Updater.tsx @@ -1,6 +1,4 @@ -import { toast } from "@common"; -import { intl } from "@common/i18n"; -import React from "@common/react"; +import { React, i18n, toast } from "@common"; import { Button, Divider, Flex, Notice, SliderItem, SwitchItem, Text, Tooltip } from "@components"; import { Logger } from "@replugged"; import { plugins } from "src/renderer/managers/plugins"; @@ -20,6 +18,8 @@ import { getAddonType, label } from "./Addons"; import "./Updater.css"; +const { intl } = i18n; + const logger = Logger.coremod("Settings:Updater"); export const Updater = (): React.ReactElement => { diff --git a/src/renderer/coremods/watcher/index.ts b/src/renderer/coremods/watcher/index.ts index b03614c54..a6874febd 100644 --- a/src/renderer/coremods/watcher/index.ts +++ b/src/renderer/coremods/watcher/index.ts @@ -1,4 +1,4 @@ -import { intl } from "@common/i18n"; +import { i18n } from "@common"; import toast from "@common/toast"; import { Logger, plugins, themes } from "@replugged"; import { t } from "src/renderer/modules/i18n"; @@ -8,6 +8,8 @@ const logger = Logger.coremod("Watcher"); const uninjectors: Array<() => void> = []; +const { intl } = i18n; + export function start(): void { let uninjectRpc = registerRPCCommand("REPLUGGED_ADDON_WATCHER", { scope: "REPLUGGED_LOCAL", diff --git a/src/renderer/coremods/welcome/index.ts b/src/renderer/coremods/welcome/index.ts index fbc3d795c..75639e9f4 100644 --- a/src/renderer/coremods/welcome/index.ts +++ b/src/renderer/coremods/welcome/index.ts @@ -1,4 +1,4 @@ -import { intl } from "@common/i18n"; +import { i18n } from "@common"; import { notices, util } from "@replugged"; import { DISCORD_INVITE } from "src/constants"; import { t } from "src/renderer/modules/i18n"; @@ -7,9 +7,9 @@ import { generalSettings } from "../settings/pages/General"; export function start(): void { if (!generalSettings.get("showWelcomeNoticeOnOpen")) return; notices.sendAnnouncement({ - message: intl.string(t.REPLUGGED_NOTICES_WELCOME_NEW_USER), + message: i18n.intl.string(t.REPLUGGED_NOTICES_WELCOME_NEW_USER), button: { - text: intl.string(t.REPLUGGED_NOTICES_JOIN_SERVER_BUTTON), + text: i18n.intl.string(t.REPLUGGED_NOTICES_JOIN_SERVER_BUTTON), onClick: () => { void util.goToOrJoinServer(DISCORD_INVITE); generalSettings.set("showWelcomeNoticeOnOpen", false); diff --git a/src/renderer/index.ts b/src/renderer/index.ts index 809cee494..5f7215c54 100644 --- a/src/renderer/index.ts +++ b/src/renderer/index.ts @@ -2,13 +2,9 @@ import * as replugged from "./replugged"; window.replugged = replugged; -type DiscordSplashWindow = Window & { - DiscordSplash?: object; -}; - // Splash screen -if ((window as DiscordSplashWindow).DiscordSplash) { - void replugged.ignition.startSplash(); +if (window.location.href.endsWith("/app_bootstrap/splash/index.html")) { + replugged.ignition.startSplash(); } else { - void replugged.ignition.ignite(); + replugged.ignition.ignite(); } diff --git a/src/renderer/managers/ignition.ts b/src/renderer/managers/ignition.ts index cb6721cb7..8692e4762 100644 --- a/src/renderer/managers/ignition.ts +++ b/src/renderer/managers/ignition.ts @@ -79,25 +79,24 @@ Load order: 5. Start coremods, plugins, and themes */ -export async function ignite(): Promise { +export function ignite(): void { // This is the function that will be called when loading the window. // Plaintext patches must run first. interceptChunksGlobal(); coremods.runPlaintextPatches(); - await plugins.loadAll(); - await plugins.runPlaintextPatches(); + plugins.loadAll(); + plugins.runPlaintextPatches(); + // At this point, Discord's code should run. // Wait for the designated common modules to load before continuing. - await Promise.all([commonReady(), componentsReady()]); - await start(); + void Promise.all([commonReady(), componentsReady()]).then(() => start()); } -export async function startSplash(): Promise { +export function startSplash(): void { log("Ignition", "Start", void 0, "Igniting Replugged Splash Screen..."); const startTime = performance.now(); - await themes.loadMissing(); - themes.loadAllSplash(); + void themes.loadMissing().then(() => themes.loadAllSplash()); log( "Ignition", diff --git a/src/renderer/managers/plugins.ts b/src/renderer/managers/plugins.ts index 0797d7cb1..2ef95211f 100644 --- a/src/renderer/managers/plugins.ts +++ b/src/renderer/managers/plugins.ts @@ -1,13 +1,13 @@ // btw, pluginID is the directory name, not the RDNN. We really need a better name for this. import { loadStyleSheet } from "../util"; -import type { PluginExports, RepluggedPlugin } from "../../types"; +import type { PlaintextPatch, PluginExports, RepluggedPlugin } from "../../types"; import { Logger } from "../modules/logger"; import { patchPlaintext } from "../modules/webpack/plaintext-patch"; import { init } from "../apis/settings"; import type { AddonSettings } from "src/types/addon"; const logger = Logger.api("Plugins"); -const settings = await init("plugins"); +const settings = init("plugins"); interface PluginWrapper extends RepluggedPlugin { exports: PluginExports | undefined; @@ -54,8 +54,8 @@ function register(plugin: RepluggedPlugin): void { * @remarks * You may need to reload Discord after adding a new plugin before it's available. */ -export async function loadAll(): Promise { - (await window.RepluggedNative.plugins.list()).forEach(register); +export function loadAll(): void { + window.RepluggedNative.plugins.list().forEach(register); } /** @@ -162,24 +162,51 @@ export async function stopAll(): Promise { * @hidden * @internal */ -export async function runPlaintextPatches(): Promise { +export function runPlaintextPatches(): void { const disabled: string[] = settings.get("disabled", []); const list = [...plugins.values()].filter((x) => !disabled.includes(x.manifest.id)); - await Promise.allSettled( - list.map(async (plugin) => { - if (plugin.manifest.plaintextPatches) { - patchPlaintext( - ( - await import( - `replugged://plugin/${plugin.path}/${ - plugin.manifest.plaintextPatches - }?t=${Date.now()}` - ) - ).default, - ); + + const getPlaintextPatch = (pluginName: string): { default: PlaintextPatch[] } => { + const wrapModule = (code: string, pluginName: string): string => `((module) => { + ${code}\nreturn module.exports + })({exports:{}})\n//# sourceURL=replugged://plugin/${pluginName}/plaintextPatches.js?t=${Date.now()}`; + + const code = RepluggedNative.plugins.readPlaintextPatch(pluginName); + + if (code) + try { + // eslint-disable-next-line no-eval + return (0, eval)(wrapModule(code, pluginName)); + } catch { + // CHANGED PLAINTEXT PATCHES TO CJS SO THIS CAN BE REMOVED!!! + const cjsCode = [ + { match: /export\s+default\s+/g, replace: () => "module.exports = " }, + { + match: /export[\s+]?\{([^}]+)\}[;]?/g, + replace: (_: string, exports: string) => + exports + .split(",") + .map((exp) => exp.split(" as ").map((s) => s.trim())) + .map(([original, alias]) => `module.exports.${alias || original} = ${original};`) + .join("\n"), + }, + ] + .reduce((code, { match, replace }) => code.replace(match, replace), code) + .trim(); + try { + // eslint-disable-next-line no-eval + return (0, eval)(wrapModule(cjsCode, pluginName)); + } catch (err) { + logger.error(`Error getting PlaintextPatches for ${pluginName}`, err); + } } - }), - ); + + return { default: [] }; + }; + + list.forEach((plugin) => { + if (plugin.manifest.plaintextPatches) patchPlaintext(getPlaintextPatch(plugin.path).default); + }); } /** @@ -197,7 +224,7 @@ export async function reload(id: string): Promise { } await stop(plugin.manifest.id); plugins.delete(plugin.manifest.id); - const newPlugin = await window.RepluggedNative.plugins.get(plugin.path); + const newPlugin = window.RepluggedNative.plugins.get(plugin.path); if (newPlugin) { register(newPlugin); await start(newPlugin.manifest.id); diff --git a/src/renderer/managers/themes.ts b/src/renderer/managers/themes.ts index eea566689..9108b1add 100644 --- a/src/renderer/managers/themes.ts +++ b/src/renderer/managers/themes.ts @@ -11,7 +11,7 @@ const themeElements = new Map(); */ export const themes = new Map(); let disabled: string[]; -const settings = await init("themes"); +const settings = init("themes"); /** * Load metadata for all themes that are added to the themes folder but not yet loaded, such as newly added themes. diff --git a/src/renderer/managers/updater.ts b/src/renderer/managers/updater.ts index 4c4335173..46271a365 100644 --- a/src/renderer/managers/updater.ts +++ b/src/renderer/managers/updater.ts @@ -55,12 +55,12 @@ const mainUpdaterDefaultSettings = { lastChecked: 0, } satisfies Partial; -export const updaterSettings = await init< - MainUpdaterSettings, - keyof typeof mainUpdaterDefaultSettings ->("dev.replugged.Updater", mainUpdaterDefaultSettings); +export const updaterSettings = init( + "dev.replugged.Updater", + mainUpdaterDefaultSettings, +); -const updaterState = await init>("dev.replugged.Updater.State"); +const updaterState = init>("dev.replugged.Updater.State"); const completedUpdates = new Set(); diff --git a/src/renderer/modules/common/api.ts b/src/renderer/modules/common/api.ts index eab5d198c..d31fb5f4d 100644 --- a/src/renderer/modules/common/api.ts +++ b/src/renderer/modules/common/api.ts @@ -124,42 +124,48 @@ export interface API { setAwaitOnline: (callback: (url: string) => Promise) => void; setRequestPatch: (patch: RequestPatch) => void; } - -const realApiModule = await waitForModule>( - filters.bySource("rateLimitExpirationHandler"), -); -const exportedValues = Object.values(realApiModule); - -type APIErrorClass = typeof V6OrEarlierAPIError | typeof APIError; -const exportedClasses = exportedValues.filter((v) => typeof v === "function" && v.prototype); -const v6ErrorClass = exportedClasses.find( - (c) => "getFieldMessage" in (c as APIErrorClass).prototype, -) as typeof V6OrEarlierAPIError; -const v8ErrorClass = exportedClasses.find( - (c) => "hasFieldErrors" in (c as APIErrorClass).prototype, -) as typeof APIError; -const http = exportedValues.find((v) => typeof v === "object") as HTTP; -const invalidFormBodyErrorCode = exportedValues.find((v) => typeof v === "number") as number; - -const getAPIBaseURL = getFunctionBySource( - realApiModule, - "GLOBAL_ENV.API_ENDPOINT", -)!; -const convertSkemaError = getFunctionBySource(realApiModule, "message")!; -// TODO: these suck. Make them better later. -const setAwaitOnline = getFunctionBySource(realApiModule, /v\s*=\s*e/)!; -const setRequestPatch = getFunctionBySource(realApiModule, /g\s*=\s*e/)!; - -// "If only, if only," the woodpecker sighs... -//export default await waitForProps("getAPIBaseURL", "HTTP"); - -export default { - INVALID_FORM_BODY_ERROR_CODE: invalidFormBodyErrorCode, - V6OrEarlierAPIError: v6ErrorClass, - V8APIError: v8ErrorClass, - HTTP: http, - getAPIBaseURL, - convertSkemaError, - setAwaitOnline, - setRequestPatch, -} as API; +const getAPI = async (): Promise => { + const realApiModule = await waitForModule>( + filters.bySource("rateLimitExpirationHandler"), + ); + const exportedValues = Object.values(realApiModule); + + type APIErrorClass = typeof V6OrEarlierAPIError | typeof APIError; + const exportedClasses = exportedValues.filter((v) => typeof v === "function" && v.prototype); + const v6ErrorClass = exportedClasses.find( + (c) => "getFieldMessage" in (c as APIErrorClass).prototype, + ) as typeof V6OrEarlierAPIError; + const v8ErrorClass = exportedClasses.find( + (c) => "hasFieldErrors" in (c as APIErrorClass).prototype, + ) as typeof APIError; + const http = exportedValues.find((v) => typeof v === "object") as HTTP; + const invalidFormBodyErrorCode = exportedValues.find((v) => typeof v === "number") as number; + + const getAPIBaseURL = getFunctionBySource( + realApiModule, + "GLOBAL_ENV.API_ENDPOINT", + )!; + const convertSkemaError = getFunctionBySource( + realApiModule, + "message", + )!; + // TODO: these suck. Make them better later. + const setAwaitOnline = getFunctionBySource(realApiModule, /v\s*=\s*e/)!; + const setRequestPatch = getFunctionBySource(realApiModule, /g\s*=\s*e/)!; + + // "If only, if only," the woodpecker sighs... + //export default await waitForProps("getAPIBaseURL", "HTTP"); + + return { + INVALID_FORM_BODY_ERROR_CODE: invalidFormBodyErrorCode, + V6OrEarlierAPIError: v6ErrorClass, + V8APIError: v8ErrorClass, + HTTP: http, + getAPIBaseURL, + convertSkemaError, + setAwaitOnline, + setRequestPatch, + }; +}; + +export default getAPI(); diff --git a/src/renderer/modules/common/channels.ts b/src/renderer/modules/common/channels.ts index 57dd370a5..225817af2 100644 --- a/src/renderer/modules/common/channels.ts +++ b/src/renderer/modules/common/channels.ts @@ -46,13 +46,16 @@ export interface ChannelStore { export type Channels = SelectedChannelStore & ChannelStore; -export default virtualMerge( - (await waitForProps( - "getChannelId", - "getLastSelectedChannelId", - "getVoiceChannelId", - ).then(Object.getPrototypeOf)) as SelectedChannelStore, - (await waitForProps("getChannel", "hasChannel").then( - Object.getPrototypeOf, - )) as ChannelStore, -); +const getChannels = async (): Promise & Channels> => + virtualMerge( + (await waitForProps( + "getChannelId", + "getLastSelectedChannelId", + "getVoiceChannelId", + ).then(Object.getPrototypeOf)) as SelectedChannelStore, + (await waitForProps("getChannel", "hasChannel").then( + Object.getPrototypeOf, + )) as ChannelStore, + ); + +export default getChannels(); diff --git a/src/renderer/modules/common/components.ts b/src/renderer/modules/common/components.ts index 8d3484076..a2f35b052 100644 --- a/src/renderer/modules/common/components.ts +++ b/src/renderer/modules/common/components.ts @@ -58,4 +58,4 @@ interface DiscordComponents { Tooltip: OriginalTooltipType; } -export default await waitForProps("FormText", "MenuItem"); +export default waitForProps("FormText", "MenuItem"); diff --git a/src/renderer/modules/common/constants.ts b/src/renderer/modules/common/constants.ts index 1ca61276c..9d386d0bc 100644 --- a/src/renderer/modules/common/constants.ts +++ b/src/renderer/modules/common/constants.ts @@ -3,75 +3,6 @@ import { filters, getExportsForProps, waitForModule, waitForProps } from "../web type StringConcat = (...rest: string[]) => string; -const ConstantsCommon = await waitForModule>( - filters.bySource("dis.gd/request"), -); -const Constants = await waitForModule>( - filters.bySource("users/@me/relationships"), -); -export const raw = virtualMerge(ConstantsCommon, Constants); - -export const Permissions = getExportsForProps>(ConstantsCommon, [ - "ADMINISTRATOR", - "MANAGE_GUILD", -]); -// OAuth2Scopes -export const Scopes = await waitForProps>("BOT", "GUILDS"); -// RPCCloseCodes -export const RPCErrors = getExportsForProps>(ConstantsCommon, [ - "RATELIMITED", - "TOKEN_REVOKED", -])!; -export const RPCCommands = getExportsForProps>(ConstantsCommon, [ - "AUTHENTICATE", - "AUTHORIZE", -])!; -export const RPCEvents = getExportsForProps>(ConstantsCommon, [ - "GUILD_CREATE", - "ERROR", -])!; -// StatusTypes -export const Status = getExportsForProps>(ConstantsCommon, [ - "ONLINE", - "IDLE", -])!; -// WebRoutes -export const Paths = getExportsForProps>(ConstantsCommon, [ - "INDEX", - "DOWNLOADS", -])!; - -export const ChannelTypes = getExportsForProps>(Constants, [ - "DM", - "GUILD_FORUM", -])!; -export const Endpoints = getExportsForProps>(Constants, [ - "USERS", - "INTEGRATIONS", -])!; -export const GuildFeatures = getExportsForProps>(Constants, [ - "VERIFIED", - "ANIMATED_BANNER", -])!; -export const MessageFlags = getExportsForProps>(Constants, [ - "EPHEMERAL", - "LOADING", -])!; -export const Routes = getExportsForProps>(Constants, [ - "INDEX", - "LOGIN", -])!; -export const UserFlags = getExportsForProps>(Constants, [ - "STAFF", - "SPAMMER", -])!; - -// ThemeColor -export const CSSVariables = await waitForProps>( - "TEXT_NORMAL", - "BACKGROUND_PRIMARY", -); - interface ColorResponse { hex: () => string; hsl: () => string; @@ -119,6 +50,116 @@ interface ColorMod { layout: Record; } -export const ColorGenerator = await waitForProps("unsafe_rawColors", "layout"); +interface returnType { + raw: ReturnType>; + Permissions: Record; + Scopes: Record; + RPCErrors: Record; + RPCCommands: Record; + RPCEvents: Record; + Status: Record; + Paths: Record; + ChannelTypes: Record; + Endpoints: Record; + GuildFeatures: Record; + MessageFlags: Record; + Routes: Record; + UserFlags: Record; + CSSVariables: Record; + ColorGenerator: ColorMod; + Themes: ColorMod["themes"]; +} + +const getConstants = async (): Promise => { + const ConstantsCommon = await waitForModule>( + filters.bySource("dis.gd/request"), + ); + const Constants = await waitForModule>( + filters.bySource("users/@me/relationships"), + ); + const raw = virtualMerge(ConstantsCommon, Constants); + + const Permissions = getExportsForProps>(ConstantsCommon, [ + "ADMINISTRATOR", + "MANAGE_GUILD", + ])!; + // OAuth2Scopes + const Scopes = await waitForProps>("BOT", "GUILDS"); + // RPCCloseCodes + const RPCErrors = getExportsForProps>(ConstantsCommon, [ + "RATELIMITED", + "TOKEN_REVOKED", + ])!; + const RPCCommands = getExportsForProps>(ConstantsCommon, [ + "AUTHENTICATE", + "AUTHORIZE", + ])!; + const RPCEvents = getExportsForProps>(ConstantsCommon, [ + "GUILD_CREATE", + "ERROR", + ])!; + // StatusTypes + const Status = getExportsForProps>(ConstantsCommon, ["ONLINE", "IDLE"])!; + // WebRoutes + const Paths = getExportsForProps>(ConstantsCommon, [ + "INDEX", + "DOWNLOADS", + ])!; + + const ChannelTypes = getExportsForProps>(Constants, [ + "DM", + "GUILD_FORUM", + ])!; + const Endpoints = getExportsForProps>(Constants, [ + "USERS", + "INTEGRATIONS", + ])!; + const GuildFeatures = getExportsForProps>(Constants, [ + "VERIFIED", + "ANIMATED_BANNER", + ])!; + const MessageFlags = getExportsForProps>(Constants, [ + "EPHEMERAL", + "LOADING", + ])!; + const Routes = getExportsForProps>(Constants, [ + "INDEX", + "LOGIN", + ])!; + const UserFlags = getExportsForProps>(Constants, [ + "STAFF", + "SPAMMER", + ])!; + + // ThemeColor + const CSSVariables = await waitForProps>( + "TEXT_NORMAL", + "BACKGROUND_PRIMARY", + ); + + const ColorGenerator = await waitForProps("unsafe_rawColors", "layout"); + + const Themes = ColorGenerator.themes; + + return { + raw, + Permissions, + Scopes, + RPCErrors, + RPCCommands, + RPCEvents, + Status, + Paths, + ChannelTypes, + Endpoints, + GuildFeatures, + MessageFlags, + Routes, + UserFlags, + CSSVariables, + ColorGenerator, + Themes, + }; +}; -export const Themes = ColorGenerator.themes; +export default getConstants(); diff --git a/src/renderer/modules/common/contextMenu.ts b/src/renderer/modules/common/contextMenu.ts index 319d7fbf7..74d2565d5 100644 --- a/src/renderer/modules/common/contextMenu.ts +++ b/src/renderer/modules/common/contextMenu.ts @@ -41,10 +41,13 @@ export interface ContextMenu { ) => void; } -const mod = await waitForModule(filters.bySource('type:"CONTEXT_MENU_OPEN"')); +const getContextMenu = async (): Promise => { + const mod = await waitForModule(filters.bySource('type:"CONTEXT_MENU_OPEN"')); + return { + open: getFunctionBySource(mod, "stopPropagation")!, + openLazy: getFunctionBySource(mod, (f) => f.toString().length < 50)!, + close: getFunctionBySource(mod, "CONTEXT_MENU_CLOSE")!, + }; +}; -export default { - open: getFunctionBySource(mod, "stopPropagation")!, - openLazy: getFunctionBySource(mod, (f) => f.toString().length < 50)!, - close: getFunctionBySource(mod, "CONTEXT_MENU_CLOSE")!, -} as ContextMenu; +export default getContextMenu(); diff --git a/src/renderer/modules/common/flux.ts b/src/renderer/modules/common/flux.ts index 2365ddb33..5d1ab43eb 100644 --- a/src/renderer/modules/common/flux.ts +++ b/src/renderer/modules/common/flux.ts @@ -169,8 +169,6 @@ export interface FluxMod { get initialized(): Promise; } -const FluxMod = await waitForProps("Store", "connectStores"); - interface Snapshot { data: Data; version: number; @@ -189,13 +187,19 @@ export declare class SnapshotStore> extends Store public save: () => void; } -const SnapshotStoreClass = await waitForModule( - filters.bySource("SnapshotStores"), -); - // In the future, fluxHooks will be removed from flux's exported object, as will SnapshotStore. // Meanwhile, it is available as a new common modules, fluxHooks. Developers should prefer using that over the hooks from here. // Merging distinct modules like this is a horrible idea, but *compatibility*. Yuck. export type Flux = FluxMod & { SnapshotStore: typeof SnapshotStore } & FluxHooks; -export default { ...FluxMod, SnapshotStore: SnapshotStoreClass, ...fluxHooks } as Flux; +const getFlux = async (): Promise => { + const FluxMod = await waitForProps("Store", "connectStores"); + + const SnapshotStoreClass = await waitForModule( + filters.bySource("SnapshotStores"), + ); + + return { ...FluxMod, SnapshotStore: SnapshotStoreClass, ...(await fluxHooks) }; +}; + +export default getFlux(); diff --git a/src/renderer/modules/common/fluxDispatcher.ts b/src/renderer/modules/common/fluxDispatcher.ts index f0c2abcc9..1904adc6e 100644 --- a/src/renderer/modules/common/fluxDispatcher.ts +++ b/src/renderer/modules/common/fluxDispatcher.ts @@ -163,7 +163,4 @@ export interface FluxDispatcher { ) => string; } -export default await waitForProps( - "_currentDispatchActionType", - "_processingWaitQueue", -); +export default waitForProps("_currentDispatchActionType", "_processingWaitQueue"); diff --git a/src/renderer/modules/common/fluxHooks.ts b/src/renderer/modules/common/fluxHooks.ts index 523cb90e3..e5797cc8e 100644 --- a/src/renderer/modules/common/fluxHooks.ts +++ b/src/renderer/modules/common/fluxHooks.ts @@ -32,24 +32,30 @@ type ShallowEqual = ( ) => boolean; type AreArraysShallowEqual = (a: T, b: T) => boolean; -const shallowEqualMod = await waitForModule(filters.bySource("shallowEqual: unequal key")); -const shallowEqual = getFunctionBySource(shallowEqualMod, "shallowEqual")!; -const areArraysShallowEqual = getFunctionBySource(shallowEqualMod, ".some")!; +const getFluxHooks = async (): Promise => { + const shallowEqualMod = await waitForModule(filters.bySource("shallowEqual: unequal key")); + const shallowEqual = getFunctionBySource(shallowEqualMod, "shallowEqual")!; + const areArraysShallowEqual = getFunctionBySource( + shallowEqualMod, + ".some", + )!; -const useStateFromStoresMod = await waitForModule>>( - filters.bySource("useStateFromStores"), -); + const useStateFromStoresMod = await waitForModule>>( + filters.bySource("useStateFromStores"), + ); -const useStateFromStores = getFunctionBySource( - useStateFromStoresMod, - "useStateFromStores", -)!; + const useStateFromStores = getFunctionBySource( + useStateFromStoresMod, + "useStateFromStores", + )!; + return { + useStateFromStores, + statesWillNeverBeEqual: getFunctionBySource(useStateFromStoresMod, "return!1"), + useStateFromStoresArray: (stores, callback, deps) => + useStateFromStores(stores, callback, deps, areArraysShallowEqual), + useStateFromStoresObject: (stores, callback, deps) => + useStateFromStores(stores, callback, deps, shallowEqual), + } as FluxHooks; +}; -export default { - useStateFromStores, - statesWillNeverBeEqual: getFunctionBySource(useStateFromStoresMod, "return!1"), - useStateFromStoresArray: (stores, callback, deps) => - useStateFromStores(stores, callback, deps, areArraysShallowEqual), - useStateFromStoresObject: (stores, callback, deps) => - useStateFromStores(stores, callback, deps, shallowEqual), -} as FluxHooks; +export default getFluxHooks(); diff --git a/src/renderer/modules/common/guilds.ts b/src/renderer/modules/common/guilds.ts index 85c8bab56..fc27003e0 100644 --- a/src/renderer/modules/common/guilds.ts +++ b/src/renderer/modules/common/guilds.ts @@ -32,20 +32,23 @@ export interface GuildStore { export type Guilds = SelectedGuildStore & GuildStore; -const GuildStore = await waitForProps("getGuild", "getGuildIds"); -const SelectedGuildStore = await waitForProps( - "getGuildId", - "getLastSelectedGuildId", -); - -export function getCurrentGuild(): Guild | undefined { - const guildId = SelectedGuildStore.getGuildId(); - if (!guildId) return undefined; - return GuildStore.getGuild(guildId); -} - -export default virtualMerge( - Object.getPrototypeOf(GuildStore) as GuildStore, - Object.getPrototypeOf(SelectedGuildStore) as SelectedGuildStore, - { getCurrentGuild }, -); +const getGuilds = async (): Promise & Guilds> => { + const GuildStore = await waitForProps("getGuild", "getGuildIds"); + const SelectedGuildStore = await waitForProps( + "getGuildId", + "getLastSelectedGuildId", + ); + + function getCurrentGuild(): Guild | undefined { + const guildId = SelectedGuildStore.getGuildId(); + if (!guildId) return undefined; + return GuildStore.getGuild(guildId); + } + return virtualMerge( + Object.getPrototypeOf(GuildStore) as GuildStore, + Object.getPrototypeOf(SelectedGuildStore) as SelectedGuildStore, + { getCurrentGuild }, + ); +}; + +export default getGuilds(); diff --git a/src/renderer/modules/common/hljs.ts b/src/renderer/modules/common/hljs.ts index 6760a4f78..1f2ea9139 100644 --- a/src/renderer/modules/common/hljs.ts +++ b/src/renderer/modules/common/hljs.ts @@ -1,4 +1,4 @@ import type HighlightJS from "highlight.js"; import { waitForProps } from "../webpack"; -export default await waitForProps("initHighlighting", "highlight"); +export default waitForProps("initHighlighting", "highlight"); diff --git a/src/renderer/modules/common/i18n.ts b/src/renderer/modules/common/i18n.ts index 0927122c1..34f01c000 100644 --- a/src/renderer/modules/common/i18n.ts +++ b/src/renderer/modules/common/i18n.ts @@ -45,18 +45,21 @@ export interface Hash { runtimeHashMessageKey: (key: string) => string; } -const { - getAvailableLocales, - getLanguages, - getSystemLocale, - international, - intl, - t: discordT, -} = await waitForProps("getAvailableLocales", "intl"); -const { runtimeHashMessageKey } = await waitForProps("runtimeHashMessageKey"); - -export const t = new Proxy(discordT, { - get: (t, key: string) => t[runtimeHashMessageKey(key)], -}); - -export { getAvailableLocales, getLanguages, getSystemLocale, international, intl }; +const getI18n = async (): Promise => { + const { + getAvailableLocales, + getLanguages, + getSystemLocale, + international, + intl, + t: discordT, + } = await waitForProps("getAvailableLocales", "intl"); + const { runtimeHashMessageKey } = await waitForProps("runtimeHashMessageKey"); + + const t = new Proxy(discordT, { + get: (t, key: string) => t[runtimeHashMessageKey(key)], + }); + return { getAvailableLocales, getLanguages, getSystemLocale, international, intl, t }; +}; + +export default getI18n(); diff --git a/src/renderer/modules/common/index.ts b/src/renderer/modules/common/index.ts index b89dbd1e9..c477a2c15 100644 --- a/src/renderer/modules/common/index.ts +++ b/src/renderer/modules/common/index.ts @@ -2,7 +2,11 @@ import { error } from "../logger"; const modulePromises: Array<() => Promise> = []; -function importTimeout(name: string, moduleImport: Promise, cb: (mod: T) => void): void { +function importTimeout( + name: string, + moduleImport: Promise, + cb: (mod: T) => Promise, +): void { modulePromises.push( () => new Promise((res, rej) => { @@ -11,9 +15,9 @@ function importTimeout(name: string, moduleImport: Promise, cb: (mod: T) = rej(new Error(`Module not found: "${name}`)); }, 10_000); void moduleImport - .then((mod) => { + .then(async (mod) => { clearTimeout(timeout); - cb(mod); + await cb(mod); res(); }) .catch((err) => { @@ -29,120 +33,159 @@ function importTimeout(name: string, moduleImport: Promise, cb: (mod: T) = import type { Channels } from "./channels"; export type { Channels }; export let channels: Channels; -importTimeout("channels", import("./channels"), (mod) => (channels = mod.default)); +importTimeout("channels", import("./channels"), async (mod) => { + channels = await mod.default; +}); import type { Guilds } from "./guilds"; export type { Guilds }; export let guilds: Guilds; -importTimeout("guilds", import("./guilds"), (mod) => (guilds = mod.default)); +importTimeout("guilds", import("./guilds"), async (mod) => { + guilds = await mod.default; +}); import type { Messages } from "./messages"; export type { Messages }; export let messages: Messages; -importTimeout("messages", import("./messages"), (mod) => (messages = mod.default)); +importTimeout("messages", import("./messages"), async (mod) => { + messages = await mod.default; +}); import type { Users } from "./users"; export type { Users }; export let users: Users; -importTimeout("users", import("./users"), (mod) => (users = mod.default)); +importTimeout("users", import("./users"), async (mod) => { + users = await mod.default; +}); // Utilities import type { API } from "./api"; export type { API }; export let api: API; -importTimeout("api", import("./api"), (mod) => (api = mod.default)); - -import * as Components from "./components"; -export type { Components }; -export let components: typeof import("./components").default; -importTimeout("components", import("./components"), (mod) => (components = mod.default)); - -import * as Constants from "./constants"; -export type { Constants }; -export let constants: typeof Constants; -importTimeout("constants", import("./constants"), (mod) => (constants = mod)); +importTimeout("api", import("./api"), async (mod) => { + api = await mod.default; +}); + +import type Components from "./components"; +export type Components = Awaited; +export let components: Components; +importTimeout("components", import("./components"), async (mod) => { + components = await mod.default; +}); + +import Constants from "./constants"; +export type Constants = Awaited; +export let constants: Constants; +importTimeout("constants", import("./constants"), async (mod) => { + constants = await mod.default; +}); import type { ContextMenu } from "./contextMenu"; export type { ContextMenu }; export let contextMenu: ContextMenu; -importTimeout("contextMenu", import("./contextMenu"), (mod) => (contextMenu = mod.default)); +importTimeout("contextMenu", import("./contextMenu"), async (mod) => { + contextMenu = await mod.default; +}); import type { Flux } from "./flux"; export type { Flux }; export let flux: Flux; -importTimeout("flux", import("./flux"), (mod) => (flux = mod.default)); +importTimeout("flux", import("./flux"), async (mod) => { + flux = await mod.default; +}); import type { FluxDispatcher } from "./fluxDispatcher"; export type { FluxDispatcher }; export let fluxDispatcher: FluxDispatcher; -importTimeout( - "fluxDispatcher", - import("./fluxDispatcher"), - (mod) => (fluxDispatcher = mod.default), -); +importTimeout("fluxDispatcher", import("./fluxDispatcher"), async (mod) => { + fluxDispatcher = await mod.default; +}); import type { FluxHooks } from "./fluxHooks"; export type { FluxHooks }; export let fluxHooks: FluxHooks; -importTimeout("fluxHooks", import("./fluxHooks"), (mod) => (fluxHooks = mod.default)); +importTimeout("fluxHooks", import("./fluxHooks"), async (mod) => { + fluxHooks = await mod.default; +}); import type { I18n } from "./i18n"; export type { I18n }; export let i18n: I18n; -importTimeout("i18n", import("./i18n"), (mod) => (i18n = mod)); +importTimeout("i18n", import("./i18n"), async (mod) => { + i18n = await mod.default; +}); import type { Modal } from "./modal"; export type { Modal }; export let modal: Modal; -importTimeout("modal", import("./modal"), (mod) => (modal = mod.default)); +importTimeout("modal", import("./modal"), async (mod) => { + modal = await mod.default; +}); import type { Parser } from "./parser"; export type { Parser }; export let parser: Parser; -importTimeout("parser", import("./parser"), (mod) => (parser = mod.default)); +importTimeout("parser", import("./parser"), async (mod) => { + parser = await mod.default; +}); import type { Toast } from "./toast"; export type { Toast }; export let toast: Toast; -importTimeout("toast", import("./toast"), (mod) => (toast = mod.default)); +importTimeout("toast", import("./toast"), (mod) => { + toast = mod.default; + return Promise.resolve(); +}); import type { Typing } from "./typing"; export type { Typing }; export let typing: Typing; -importTimeout("typing", import("./typing"), (mod) => (typing = mod.default)); +importTimeout("typing", import("./typing"), async (mod) => { + typing = await mod.default; +}); // External Libraries /** * @see {@link https://highlightjs.org/usage/} */ -export let hljs: typeof import("highlight.js").default; -importTimeout("hljs", import("./hljs"), (mod) => (hljs = mod.default)); +export let hljs: Awaited; +importTimeout("hljs", import("./hljs"), async (mod) => { + hljs = await mod.default; +}); /** * @see {@link https://lodash.com/docs} */ export let lodash: typeof window._; -importTimeout("lodash", import("./lodash"), (mod) => (lodash = mod.default)); +importTimeout("lodash", import("./lodash"), async (mod) => { + lodash = await mod.default; +}); /** * @see {@link https://momentjs.com/docs/} */ -export let moment: typeof import("moment"); -importTimeout("moment", import("./moment"), (mod) => (moment = mod.default)); +export let moment: Awaited; +importTimeout("moment", import("./moment"), async (mod) => { + moment = await mod.default; +}); /** * @see {@link https://react.dev/} */ -export let React: typeof import("react"); -importTimeout("React", import("./react"), (mod) => (React = mod.default)); +export let React: Awaited; +importTimeout("React", import("./react"), async (mod) => { + React = await mod.default; +}); /** * @see {@link https://react.dev/reference/react-dom} */ -export let ReactDOM: typeof import("react-dom"); -importTimeout("ReactDOM", import("./react-dom"), (mod) => (ReactDOM = mod.default)); +export let ReactDOM: Awaited; +importTimeout("ReactDOM", import("./react-dom"), async (mod) => { + ReactDOM = await mod.default; +}); /** * @internal diff --git a/src/renderer/modules/common/lodash.ts b/src/renderer/modules/common/lodash.ts index c12897111..2b3d746ee 100644 --- a/src/renderer/modules/common/lodash.ts +++ b/src/renderer/modules/common/lodash.ts @@ -1,4 +1,4 @@ import type Lodash from "lodash"; import { waitForProps } from "../webpack"; -export default await waitForProps("debounce"); +export default waitForProps("debounce"); diff --git a/src/renderer/modules/common/messages.ts b/src/renderer/modules/common/messages.ts index 22e3d000b..2d50d965e 100644 --- a/src/renderer/modules/common/messages.ts +++ b/src/renderer/modules/common/messages.ts @@ -492,22 +492,24 @@ interface MessageUtils { userRecordToServer: (user: User) => UserServer; } -const MessageStore = await waitForProps("getMessage", "getMessages"); - -const MessageUtilsMod = await waitForModule(filters.bySource('username:"Clyde"')); -const MessageUtils = { - createBotMessage: getFunctionBySource(MessageUtilsMod, 'username:"Clyde"'), - createMessage: getFunctionBySource(MessageUtilsMod, "createMessage"), - userRecordToServer: getFunctionBySource(MessageUtilsMod, "global_name:"), -} as MessageUtils; - export type Messages = PartialMessageStore & MessageActions & MessageUtils; - -export default virtualMerge( - await waitForProps("sendMessage", "editMessage", "deleteMessage"), - { - getMessage: MessageStore.getMessage, - getMessages: MessageStore.getMessages, - }, - MessageUtils, -); +const getMessages = async (): Promise & Messages> => { + const MessageStore = await waitForProps("getMessage", "getMessages"); + + const MessageUtilsMod = await waitForModule(filters.bySource('username:"Clyde"')); + const MessageUtils = { + createBotMessage: getFunctionBySource(MessageUtilsMod, 'username:"Clyde"'), + createMessage: getFunctionBySource(MessageUtilsMod, "createMessage"), + userRecordToServer: getFunctionBySource(MessageUtilsMod, "global_name:"), + } as MessageUtils; + return virtualMerge( + await waitForProps("sendMessage", "editMessage", "deleteMessage"), + { + getMessage: MessageStore.getMessage, + getMessages: MessageStore.getMessages, + }, + MessageUtils, + ); +}; + +export default getMessages(); diff --git a/src/renderer/modules/common/modal.ts b/src/renderer/modules/common/modal.ts index 752815573..cd5df4ded 100644 --- a/src/renderer/modules/common/modal.ts +++ b/src/renderer/modules/common/modal.ts @@ -63,38 +63,41 @@ export type Modal = { confirm: (props: AlertProps) => Promise; } & ModalClasses; -const mod = await waitForModule(filters.bySource("onCloseRequest:null!=")); -const alertMod = await waitForProps("show", "close"); +const getModal = async (): Promise => { + const mod = await waitForModule(filters.bySource("onCloseRequest:null!=")); + const alertMod = await waitForProps("show", "close"); -const classes = getBySource(".justifyStart")!; + const classes = getBySource(".justifyStart")!; + return { + openModal: getFunctionBySource(mod, "onCloseRequest:null!=")!, + closeModal: getFunctionBySource(mod, "onCloseCallback&&")!, + Direction: classes?.Direction, + Align: classes?.Align, + Justify: classes?.Justify, + Wrap: classes?.Wrap, + alert: alertMod.show, + confirm: (props: AlertProps) => + new Promise((resolve) => { + let didResolve = false; + const onConfirm = (): void => { + if (props.onConfirm) props.onConfirm(); + didResolve = true; + resolve(true); + }; + const onCancel = (): void => { + if (props.onCancel) props.onCancel(); + didResolve = true; + resolve(false); + }; + const onCloseCallback = (): void => { + if (props.onCloseCallback) props.onCloseCallback(); + setTimeout(() => { + if (!didResolve) resolve(null); + }, 0); + }; + alertMod.show({ ...defaultConfirmProps, ...props, onConfirm, onCancel, onCloseCallback }); + }), + }; +}; -export default { - openModal: getFunctionBySource(mod, "onCloseRequest:null!=")!, - closeModal: getFunctionBySource(mod, "onCloseCallback&&")!, - Direction: classes?.Direction, - Align: classes?.Align, - Justify: classes?.Justify, - Wrap: classes?.Wrap, - alert: alertMod.show, - confirm: (props: AlertProps) => - new Promise((resolve) => { - let didResolve = false; - const onConfirm = (): void => { - if (props.onConfirm) props.onConfirm(); - didResolve = true; - resolve(true); - }; - const onCancel = (): void => { - if (props.onCancel) props.onCancel(); - didResolve = true; - resolve(false); - }; - const onCloseCallback = (): void => { - if (props.onCloseCallback) props.onCloseCallback(); - setTimeout(() => { - if (!didResolve) resolve(null); - }, 0); - }; - alertMod.show({ ...defaultConfirmProps, ...props, onConfirm, onCancel, onCloseCallback }); - }), -} as Modal; +export default getModal(); diff --git a/src/renderer/modules/common/moment.ts b/src/renderer/modules/common/moment.ts index 447e7c6e8..80eeae8bd 100644 --- a/src/renderer/modules/common/moment.ts +++ b/src/renderer/modules/common/moment.ts @@ -1,4 +1,4 @@ import Moment from "moment"; import { waitForProps } from "../webpack"; -export default await waitForProps("isMoment"); +export default waitForProps("isMoment"); diff --git a/src/renderer/modules/common/parser.ts b/src/renderer/modules/common/parser.ts index 37772c45e..f3133c21d 100644 --- a/src/renderer/modules/common/parser.ts +++ b/src/renderer/modules/common/parser.ts @@ -97,4 +97,4 @@ export interface Parser { astParserFor(rules: SimpleMarkdown.ParserRules): ParseFn; } -export default await waitForProps("parse", "parseTopic"); +export default waitForProps("parse", "parseTopic"); diff --git a/src/renderer/modules/common/react-dom.ts b/src/renderer/modules/common/react-dom.ts index 9548f12e2..1b0b9a284 100644 --- a/src/renderer/modules/common/react-dom.ts +++ b/src/renderer/modules/common/react-dom.ts @@ -1,4 +1,4 @@ import type ReactDOM from "react-dom"; import { waitForProps } from "../webpack"; -export default await waitForProps("createPortal", "flushSync"); +export default waitForProps("createPortal", "flushSync"); diff --git a/src/renderer/modules/common/react.ts b/src/renderer/modules/common/react.ts index 0e0ec9693..6435cdaa6 100644 --- a/src/renderer/modules/common/react.ts +++ b/src/renderer/modules/common/react.ts @@ -1,4 +1,4 @@ import type React from "react"; import { waitForProps } from "../webpack"; -export default await waitForProps("createElement", "useState"); +export default waitForProps("createElement", "useState"); diff --git a/src/renderer/modules/common/typing.ts b/src/renderer/modules/common/typing.ts index 4874183ad..643952a2c 100644 --- a/src/renderer/modules/common/typing.ts +++ b/src/renderer/modules/common/typing.ts @@ -6,4 +6,4 @@ export type Typing = { stopTyping: (channelId: string) => void; }; -export default await waitForProps("startTyping", "stopTyping"); +export default waitForProps("startTyping", "stopTyping"); diff --git a/src/renderer/modules/common/users.ts b/src/renderer/modules/common/users.ts index 72753624e..4af533f18 100644 --- a/src/renderer/modules/common/users.ts +++ b/src/renderer/modules/common/users.ts @@ -56,11 +56,14 @@ export interface GuildMemberStore { export type Users = UserStore & GuildMemberStore; -export default virtualMerge( - (await waitForProps("getUser", "getCurrentUser").then( - Object.getPrototypeOf, - )) as UserStore, - (await waitForProps("getTrueMember", "getMember").then( - Object.getPrototypeOf, - )) as GuildMemberStore, -); +const getUsers = async (): Promise & Users> => + virtualMerge( + (await waitForProps("getUser", "getCurrentUser").then( + Object.getPrototypeOf, + )) as UserStore, + (await waitForProps("getTrueMember", "getMember").then( + Object.getPrototypeOf, + )) as GuildMemberStore, + ); + +export default getUsers(); diff --git a/src/renderer/modules/components/Breadcrumb.ts b/src/renderer/modules/components/Breadcrumb.ts new file mode 100644 index 000000000..d85a6b7e9 --- /dev/null +++ b/src/renderer/modules/components/Breadcrumb.ts @@ -0,0 +1,17 @@ +import { filters, waitForModule } from "../webpack"; + +interface Breadcrumb { + id: string; + label: string; +} + +interface BreadcrumbProps { + activeId: string; + breadcrumbs: Breadcrumb[]; + onBreadcrumbClick: (breadcrumb: Breadcrumb) => void; + renderCustomBreadcrumb: (breadcrumb: Breadcrumb, active: boolean) => React.ReactNode; +} + +export type BreadcrumbType = React.ComponentClass; + +export default waitForModule(filters.bySource(/\w+.breadcrumbFinalWrapper/)); diff --git a/src/renderer/modules/components/ButtonItem.tsx b/src/renderer/modules/components/ButtonItem.tsx index 1fc80c733..42bf673da 100644 --- a/src/renderer/modules/components/ButtonItem.tsx +++ b/src/renderer/modules/components/ButtonItem.tsx @@ -60,13 +60,6 @@ export type ButtonType = React.FC> & { Sizes: Record<"NONE" | "TINY" | "SMALL" | "MEDIUM" | "LARGE" | "MIN" | "MAX" | "ICON", string>; }; -export const { Button } = components; - -const classes = - await waitForProps>( - "dividerDefault", - ); - interface ButtonItemProps { onClick?: React.MouseEventHandler; button?: string; @@ -80,48 +73,66 @@ interface ButtonItemProps { } export type ButtonItemType = React.FC>; +const getButtonItems = async (): Promise<{ + ButtonItem: ButtonItemType; + Button: ButtonType; +}> => { + const { Button } = await components; + + const classes = + await waitForProps>( + "dividerDefault", + ); -export const ButtonItem = (props: React.PropsWithChildren): React.ReactElement => { - const { hideBorder = false } = props; + const ButtonItem = (props: React.PropsWithChildren): React.ReactElement => { + const { hideBorder = false } = props; - const button = ( - - ); + const button = ( + + ); - return ( -
- - -
- - {props.tooltipText ? ( - - {button} - - ) : ( - button + return ( +
+ + +
+ + {props.tooltipText ? ( + + {button} + + ) : ( + button + )} +
+ {props.note && ( + {props.note} )} -
- {props.note && ( - {props.note} - )} - - - {!hideBorder && } -
- ); +
+
+ {!hideBorder && } +
+ ); + }; + + return { + Button, + ButtonItem, + }; }; + +export default getButtonItems(); diff --git a/src/renderer/modules/components/Category.tsx b/src/renderer/modules/components/Category.tsx index 2f92ef3d8..9e8f9bdd9 100644 --- a/src/renderer/modules/components/Category.tsx +++ b/src/renderer/modules/components/Category.tsx @@ -1,12 +1,7 @@ -import React from "@common/react"; +import getReact from "@common/react"; import { Divider, FormText } from "."; import { waitForProps } from "../webpack"; -const classes = - await waitForProps>( - "dividerDefault", - ); - interface CategoryProps { title: string; open?: boolean; @@ -17,74 +12,83 @@ interface CategoryProps { export type CategoryType = React.FC>; -/** - * A category. It's opened state, by default, is automatically handled by the component. `open` and `onChange` both must be specified to override. - */ -export default ((props: React.PropsWithChildren) => { - const [open, setOpen] = React.useState(props.open || false); +const getCategory = async (): Promise => { + const React = await getReact; + const classes = + await waitForProps>( + "dividerDefault", + ); + return (props: React.PropsWithChildren) => { + const [open, setOpen] = React.useState(props.open || false); - const handleClick = (): void => { - if (props.disabled) return; + const handleClick = (): void => { + if (props.disabled) return; - if (typeof props.onChange === "function" && typeof props.open === "boolean") props.onChange(); - else setOpen(!open); - }; + if (typeof props.onChange === "function" && typeof props.open === "boolean") props.onChange(); + else setOpen(!open); + }; - return ( -
-
{ - handleClick(); - }}> - - - -
-
- -
- {props.note && ( - {props.note} - )} -
-
- {open ? ( + return ( +
{ + handleClick(); }}> - {props.children} + + + +
+
+ +
+ {props.note && ( + {props.note} + )} +
- ) : ( - - )} -
- ); -}) as CategoryType; + {open ? ( +
+ {props.children} +
+ ) : ( + + )} +
+ ); + }; +}; + +/** + * A category. It's opened state, by default, is automatically handled by the component. `open` and `onChange` both must be specified to override. + */ +export default getCategory(); diff --git a/src/renderer/modules/components/CheckboxItem.tsx b/src/renderer/modules/components/CheckboxItem.tsx index f844f3ebd..1f5ebdb84 100644 --- a/src/renderer/modules/components/CheckboxItem.tsx +++ b/src/renderer/modules/components/CheckboxItem.tsx @@ -30,16 +30,24 @@ export type CheckboxType = React.ComponentClass>; -export const { Checkbox } = components; +const getCheckboxItem = async (): Promise<{ + Checkbox: CheckboxType; + CheckboxItem: CheckboxItemType; +}> => { + const { Checkbox } = await components; -export const CheckboxItem = (props: React.PropsWithChildren): React.ReactElement => { - return ( - - {props.children && ( - - {props.children} - - )} - - ); + const CheckboxItem = (props: React.PropsWithChildren): React.ReactElement => { + return ( + + {props.children && ( + + {props.children} + + )} + + ); + }; + return { Checkbox, CheckboxItem }; }; + +export default getCheckboxItem(); diff --git a/src/renderer/modules/components/Clickable.tsx b/src/renderer/modules/components/Clickable.tsx index 7ca5a8225..b07822968 100644 --- a/src/renderer/modules/components/Clickable.tsx +++ b/src/renderer/modules/components/Clickable.tsx @@ -13,12 +13,15 @@ export type ClickableCompType = React.ComponentClass>; -export default (props: React.PropsWithChildren): React.ReactElement => { - const style = props.style || {}; - style.cursor = "pointer"; - return ; +const getClickable = async (): Promise => { + const { Clickable } = await components; + return (props: React.PropsWithChildren): React.ReactElement => { + const style = props.style || {}; + style.cursor = "pointer"; + return ; + }; }; + +export default getClickable(); diff --git a/src/renderer/modules/components/ContextMenu.tsx b/src/renderer/modules/components/ContextMenu.tsx index 46abf2bb7..775b5f01d 100644 --- a/src/renderer/modules/components/ContextMenu.tsx +++ b/src/renderer/modules/components/ContextMenu.tsx @@ -154,15 +154,15 @@ export interface ContextMenuType { MenuSeparator: React.FC; } -const Menu = { - ContextMenu: components.Menu, +const getMenu = async (): Promise => ({ + ContextMenu: (await components).Menu, ItemColors, - MenuCheckboxItem: components.MenuCheckboxItem, - MenuControlItem: components.MenuControlItem, - MenuGroup: components.MenuGroup, - MenuItem: components.MenuItem, - MenuRadioItem: components.MenuRadioItem, - MenuSeparator: components.MenuSeparator, -} as ContextMenuType; - -export default Menu; + MenuCheckboxItem: (await components).MenuCheckboxItem, + MenuControlItem: (await components).MenuControlItem, + MenuGroup: (await components).MenuGroup, + MenuItem: (await components).MenuItem, + MenuRadioItem: (await components).MenuRadioItem, + MenuSeparator: (await components).MenuSeparator, +}); + +export default getMenu(); diff --git a/src/renderer/modules/components/Divider.tsx b/src/renderer/modules/components/Divider.tsx index ffc29f085..cbd131e1c 100644 --- a/src/renderer/modules/components/Divider.tsx +++ b/src/renderer/modules/components/Divider.tsx @@ -8,4 +8,4 @@ interface DividerProps { export type DividerType = React.FC; -export default components.FormDivider; +export default components.then((v) => v.FormDivider); diff --git a/src/renderer/modules/components/ErrorBoundary.tsx b/src/renderer/modules/components/ErrorBoundary.tsx index d9b37d6d8..02bfbda47 100644 --- a/src/renderer/modules/components/ErrorBoundary.tsx +++ b/src/renderer/modules/components/ErrorBoundary.tsx @@ -1,5 +1,5 @@ -import { intl } from "@common/i18n"; -import React from "@common/react"; +import getReact from "@common/react"; +import getI18n from "@common/i18n"; import { plugins } from "@replugged"; import { t } from "../i18n"; import { Logger } from "../logger"; @@ -8,37 +8,6 @@ import "./ErrorBoundary.css"; const logger = new Logger("Components", "ErrorBoundary"); -function CollapsibleErrorStack(props: { stack: string }): React.ReactElement { - const { stack } = props; - - const [open, setOpen] = React.useState(false); - - const message = stack.split("\n")[0]; - - return ( -
-
setOpen(!open)}> -

{message}

- - - -
-
-
- {stack} -
-
-
- ); -} - export interface ErrorProps { children: React.ReactNode; silent?: boolean; @@ -55,60 +24,98 @@ export interface ErrorState { export type ErrorBoundaryType = React.ComponentClass; -export default class ErrorBoundary extends React.Component { - public constructor(props: ErrorProps) { - super(props); - this.state = { hasError: false }; +const getErrorBoundary = async (): Promise => { + const React = await getReact; + const { intl } = await getI18n; + + function CollapsibleErrorStack(props: { stack: string }): React.ReactElement { + const { stack } = props; + + const [open, setOpen] = React.useState(false); + + const message = stack.split("\n")[0]; + + return ( +
+
setOpen(!open)}> +

{message}

+ + + +
+
+
+ {stack} +
+
+
+ ); } - public static getDerivedStateFromError(error: Error): ErrorState { - let pluginName; - - const pluginMatch = error?.stack?.match(/replugged:\/\/plugin\/([\w.]+)\//); - if (pluginMatch) { - const pluginId = pluginMatch[1]; - const plugin = plugins.plugins.get(pluginId); - if (plugin) { - pluginName = plugin.manifest.name; - } + return class ErrorBoundary extends React.Component { + public constructor(props: ErrorProps) { + super(props); + this.state = { hasError: false }; } - return { hasError: true, error, pluginName }; - } + public static getDerivedStateFromError(error: Error): ErrorState { + let pluginName; - public componentDidCatch(error: Error, errorInfo: React.ErrorInfo): void { - if (!this.props.silent) { - logger.error("ErrorBoundary caught an error", error, errorInfo); + const pluginMatch = error?.stack?.match(/replugged:\/\/plugin\/([\w.]+)\//); + if (pluginMatch) { + const pluginId = pluginMatch[1]; + const plugin = plugins.plugins.get(pluginId); + if (plugin) { + pluginName = plugin.manifest.name; + } + } + + return { hasError: true, error, pluginName }; } - if (this.props.onError) { - this.props.onError(error, errorInfo); + + public componentDidCatch(error: Error, errorInfo: React.ErrorInfo): void { + if (!this.props.silent) { + logger.error("ErrorBoundary caught an error", error, errorInfo); + } + if (this.props.onError) { + this.props.onError(error, errorInfo); + } } - } - public render(): React.ReactNode { - if (this.state.hasError) { - const { error, pluginName } = this.state; - - return ( - this.props.fallback || ( -
-

{intl.string(t.REPLUGGED_SETTINGS_ERROR_HEADER)}

- {pluginName && ( -

- {intl.format(t.REPLUGGED_SETTINGS_ERROR_PLUGIN_NAME, { name: pluginName })} -

- )} -

{intl.string(t.REPLUGGED_SETTINGS_ERROR_SUB_HEADER)}

- {error?.stack && ( - }> - - - )} -
- ) - ); + public render(): React.ReactNode { + if (this.state.hasError) { + const { error, pluginName } = this.state; + + return ( + this.props.fallback || ( +
+

{intl.string(t.REPLUGGED_SETTINGS_ERROR_HEADER)}

+ {pluginName && ( +

+ {intl.format(t.REPLUGGED_SETTINGS_ERROR_PLUGIN_NAME, { name: pluginName })} +

+ )} +

{intl.string(t.REPLUGGED_SETTINGS_ERROR_SUB_HEADER)}

+ {error?.stack && ( + }> + + + )} +
+ ) + ); + } + + return this.props.children; } + }; +}; - return this.props.children; - } -} +export default getErrorBoundary(); diff --git a/src/renderer/modules/components/Flex.tsx b/src/renderer/modules/components/Flex.tsx index 96c220c29..9a6c77397 100644 --- a/src/renderer/modules/components/Flex.tsx +++ b/src/renderer/modules/components/Flex.tsx @@ -29,6 +29,6 @@ export type FlexType = React.FC> & { }; }; -export default await waitForModule( +export default waitForModule( filters.bySource(/HORIZONTAL_REVERSE:\w+?\.horizontalReverse./), ); diff --git a/src/renderer/modules/components/FormItem.tsx b/src/renderer/modules/components/FormItem.tsx index 620181bf6..61e7c52d4 100644 --- a/src/renderer/modules/components/FormItem.tsx +++ b/src/renderer/modules/components/FormItem.tsx @@ -19,9 +19,6 @@ interface FormItemCompProps extends Omit, export type FormItemCompType = React.ForwardRefExoticComponent & { render: React.ForwardRefRenderFunction; }; -const FormItemComp = components.FormItem; - -const classes = await waitForProps>("dividerDefault"); interface FormItemProps extends FormItemCompProps { note?: string; @@ -33,25 +30,39 @@ interface FormItemProps extends FormItemCompProps { export type FormItemType = React.FC; -export default ((props) => { - const { note, notePosition = "before", noteStyle, noteClassName, divider, ...compProps } = props; - - const noteStyleDefault = notePosition === "before" ? { marginBottom: 8 } : { marginTop: 8 }; - const noteComp = ( - - {note} - - ); - - return ( - - {note && notePosition === "before" && noteComp} - {props.children} - {note && notePosition === "after" && noteComp} - {divider && } - - ); -}) as FormItemType; +const getFormItem = async (): Promise => { + const FormItemComp = (await components).FormItem; + + const classes = await waitForProps>("dividerDefault"); + return (props) => { + const { + note, + notePosition = "before", + noteStyle, + noteClassName, + divider, + ...compProps + } = props; + + const noteStyleDefault = notePosition === "before" ? { marginBottom: 8 } : { marginTop: 8 }; + const noteComp = ( + + {note} + + ); + + return ( + + {note && notePosition === "before" && noteComp} + {props.children} + {note && notePosition === "after" && noteComp} + {divider && } + + ); + }; +}; + +export default getFormItem(); diff --git a/src/renderer/modules/components/FormNotice.tsx b/src/renderer/modules/components/FormNotice.tsx index 667aa5de5..45bfefb2d 100644 --- a/src/renderer/modules/components/FormNotice.tsx +++ b/src/renderer/modules/components/FormNotice.tsx @@ -24,4 +24,4 @@ export type FormNoticeType = React.FC & { Types: Record<"PRIMARY" | "DANGER" | "WARNING" | "SUCCESS" | "BRAND" | "CUSTOM", string>; }; -export default components.FormNotice; +export default components.then((v) => v.FormNotice); diff --git a/src/renderer/modules/components/FormText.tsx b/src/renderer/modules/components/FormText.tsx index c7caaa4aa..8ad13d866 100644 --- a/src/renderer/modules/components/FormText.tsx +++ b/src/renderer/modules/components/FormText.tsx @@ -22,9 +22,6 @@ export type FormTextCompType = React.FC>; export type FormTextType = Record; -const FormTextComp = components.FormText; -const types = components.FormTextTypes; - export const FormText: FormTextType = { DEFAULT: () => null, DESCRIPTION: () => null, @@ -36,11 +33,17 @@ export const FormText: FormTextType = { SUCCESS: () => null, }; -if (typeof types === "object" && types !== null) - Object.keys(types).forEach((key) => { - FormText[key] = (props: React.PropsWithChildren) => ( - - {props.children} - - ); - }); +const mapFormText = async (): Promise => { + const FormTextComp = (await components).FormText; + const types = (await components).FormTextTypes; + if (typeof types === "object" && types !== null) + Object.keys(types).forEach((key) => { + FormText[key] = (props: React.PropsWithChildren) => ( + + {props.children} + + ); + }); +}; + +void mapFormText(); diff --git a/src/renderer/modules/components/Loader.tsx b/src/renderer/modules/components/Loader.tsx index 8604c60db..bc3569416 100644 --- a/src/renderer/modules/components/Loader.tsx +++ b/src/renderer/modules/components/Loader.tsx @@ -26,4 +26,4 @@ export type LoaderType = React.FC & { Type: typeof Types; }; -export default components.Spinner; +export default components.then((v) => v.Spinner); diff --git a/src/renderer/modules/components/Modal.tsx b/src/renderer/modules/components/Modal.tsx index 1d2931c60..9b552c4fb 100644 --- a/src/renderer/modules/components/Modal.tsx +++ b/src/renderer/modules/components/Modal.tsx @@ -54,10 +54,12 @@ export interface ModalType { ModalCloseButton: React.FC; } -export default { - ModalRoot: components.ModalRoot, - ModalHeader: components.ModalHeader, - ModalContent: components.ModalContent, - ModalFooter: components.ModalFooter, - ModalCloseButton: components.ModalCloseButton, -} as ModalType; +const getModal = async (): Promise => ({ + ModalRoot: (await components).ModalRoot, + ModalHeader: (await components).ModalHeader, + ModalContent: (await components).ModalContent, + ModalFooter: (await components).ModalFooter, + ModalCloseButton: (await components).ModalCloseButton, +}); + +export default getModal(); diff --git a/src/renderer/modules/components/Notice.tsx b/src/renderer/modules/components/Notice.tsx index 202f3d43d..6109b58e4 100644 --- a/src/renderer/modules/components/Notice.tsx +++ b/src/renderer/modules/components/Notice.tsx @@ -21,9 +21,11 @@ export type NoticeType = React.FC & { Types: typeof HelpMessageTypes; // for backwards compat HelpMessageTypes: typeof HelpMessageTypes; }; +const getNotice = async (): Promise => { + const { HelpMessage } = await components; + HelpMessage.HelpMessageTypes = (await components).HelpMessageTypes; + HelpMessage.Types = HelpMessage.HelpMessageTypes; + return HelpMessage; +}; -const { HelpMessage } = components; -HelpMessage.HelpMessageTypes = components.HelpMessageTypes; -HelpMessage.Types = HelpMessage.HelpMessageTypes; - -export default HelpMessage; +export default getNotice(); diff --git a/src/renderer/modules/components/RadioItem.tsx b/src/renderer/modules/components/RadioItem.tsx index 6a853ccaf..d495dd641 100644 --- a/src/renderer/modules/components/RadioItem.tsx +++ b/src/renderer/modules/components/RadioItem.tsx @@ -38,8 +38,6 @@ export type RadioType = React.FC & { Sizes: Record<"NOT_SET" | "NONE" | "SMALL" | "MEDIUM", string>; }; -export const Radio = components.RadioGroup; - interface RadioItemProps extends RadioProps { note?: string; style?: React.CSSProperties; @@ -47,15 +45,25 @@ interface RadioItemProps extends RadioProps { export type RadioItemType = React.FC>; -export const RadioItem = (props: React.PropsWithChildren): React.ReactElement => { - return ( - - - - ); +const getRadioItem = async (): Promise<{ + RadioItem: RadioItemType; + Radio: RadioType; +}> => { + const Radio = (await components).RadioGroup; + + const RadioItem = (props: React.PropsWithChildren): React.ReactElement => { + return ( + + + + ); + }; + return { Radio, RadioItem }; }; + +export default getRadioItem(); diff --git a/src/renderer/modules/components/SelectItem.tsx b/src/renderer/modules/components/SelectItem.tsx index b9d4f6fff..a23f3abcb 100644 --- a/src/renderer/modules/components/SelectItem.tsx +++ b/src/renderer/modules/components/SelectItem.tsx @@ -43,8 +43,6 @@ interface SelectCompProps { export type SelectCompType = React.FC; -const SelectComp = components.Select; - interface SelectProps extends SelectCompProps { onChange?: (value: string) => void; onSelect?: (value: string) => void; @@ -57,21 +55,6 @@ export type SelectType = React.FC> & { Looks: typeof Looks; }; -export const Select = ((props) => { - if (!props.isSelected && props.value != null) props.isSelected = (value) => value === props.value; - if (!props.serialize) props.serialize = (value) => value; - - return ( - - ); -}) as SelectType; -Select.Looks = Looks; - interface SelectItemProps extends SelectProps { note?: string; style?: React.CSSProperties; @@ -79,16 +62,44 @@ interface SelectItemProps extends SelectProps { export type SelectItemType = React.FC>; -export const SelectItem = (props: React.PropsWithChildren): React.ReactElement => { - return ( - - + + ); + }; + + return { Select, SelectItem }; }; + +export default getSelectItem(); diff --git a/src/renderer/modules/components/SliderItem.tsx b/src/renderer/modules/components/SliderItem.tsx index cc5a8cdaa..6d885e959 100644 --- a/src/renderer/modules/components/SliderItem.tsx +++ b/src/renderer/modules/components/SliderItem.tsx @@ -42,8 +42,6 @@ interface SliderCompProps { export type SliderCompType = React.ComponentClass; -const SliderComp = components.Slider; - interface SliderProps extends SliderCompProps { value?: number; onChange?: (value: number) => void; @@ -53,13 +51,6 @@ export type SliderType = React.FC & { MarkerPositions: typeof MarkerPositions; }; -export const Slider = ((props) => { - return ; -}) as SliderType; -Slider.MarkerPositions = MarkerPositions; - -const classes = await waitForProps>("marginTop20"); - interface SliderItemProps extends SliderProps { note?: string; style?: React.CSSProperties; @@ -67,22 +58,37 @@ interface SliderItemProps extends SliderProps { export type SliderItemType = React.FC>; -export const SliderItem = (props: React.PropsWithChildren): React.ReactElement => { - const { children, className, ...compProps } = props; - return ( - - - - ); +const getSliderItem = async (): Promise<{ Slider: SliderType; SliderItem: SliderItemType }> => { + const SliderComp = (await components).Slider; + + const Slider = ((props) => { + return ; + }) as SliderType; + + Slider.MarkerPositions = MarkerPositions; + + const classes = await waitForProps>("marginTop20"); + + const SliderItem = (props: React.PropsWithChildren): React.ReactElement => { + const { children, className, ...compProps } = props; + return ( + + + + ); + }; + return { Slider, SliderItem }; }; + +export default getSliderItem(); diff --git a/src/renderer/modules/components/SwitchItem.tsx b/src/renderer/modules/components/SwitchItem.tsx index 0aa368eae..0c4bfb0a2 100644 --- a/src/renderer/modules/components/SwitchItem.tsx +++ b/src/renderer/modules/components/SwitchItem.tsx @@ -27,6 +27,9 @@ interface SwitchItemProps { export type SwitchItemType = React.FC>; -export const { Switch } = components; +const getSwitchItem = async (): Promise<{ Switch: SwitchType; SwitchItem: SwitchItemType }> => ({ + Switch: (await components).Switch, + SwitchItem: (await components).FormSwitch, +}); -export const SwitchItem = components.FormSwitch; +export default getSwitchItem(); diff --git a/src/renderer/modules/components/Text.tsx b/src/renderer/modules/components/Text.tsx index 4e377e4a1..fcc6d3399 100644 --- a/src/renderer/modules/components/Text.tsx +++ b/src/renderer/modules/components/Text.tsx @@ -90,38 +90,45 @@ export type OriginalTextType = React.FC; export type TextType = OriginalTextType & Record<"Normal" | "H1" | "H2" | "H3" | "H4" | "Eyebrow", OriginalTextType>; -const TextComp = components.Text; +const getText = async (): Promise => { + const TextComp = (await components).Text; -function TextWithDefaultProps(defaultProps: CustomTextProps) { - return (props: CustomTextProps) => { - props = { ...defaultProps, ...props }; - const { children } = props; - const newChildren = - props.markdown && typeof children === "string" - ? parser.parse(children, true, { - allowLinks: props.allowMarkdownLinks, - allowHeading: props.allowMarkdownHeading, - allowList: props.allowMarkdownList, - }) - : children; - delete props.markdown; - delete props.allowMarkdownLinks; - delete props.allowMarkdownHeading; - delete props.allowMarkdownList; - return {newChildren}; - }; -} + function TextWithDefaultProps(defaultProps: CustomTextProps) { + return (props: CustomTextProps) => { + props = { ...defaultProps, ...props }; + const { children } = props; + const newChildren = + props.markdown && typeof children === "string" + ? parser.parse(children, true, { + allowLinks: props.allowMarkdownLinks, + allowHeading: props.allowMarkdownHeading, + allowList: props.allowMarkdownList, + }) + : children; + delete props.markdown; + delete props.allowMarkdownLinks; + delete props.allowMarkdownHeading; + delete props.allowMarkdownList; + return {newChildren}; + }; + } -const Text = TextWithDefaultProps({}) as TextType; -Text.Normal = TextWithDefaultProps({ variant: "text-sm/normal", tag: "span" }); -Text.H1 = TextWithDefaultProps({ variant: "heading-xl/bold", color: "header-primary", tag: "h1" }); -Text.H2 = TextWithDefaultProps({ - variant: "heading-lg/semibold", - color: "header-primary", - tag: "h2", -}); -Text.H3 = TextWithDefaultProps({ variant: "heading-md/bold", tag: "h3" }); -Text.H4 = TextWithDefaultProps({ variant: "heading-sm/bold", tag: "h4" }); -Text.Eyebrow = TextWithDefaultProps({ variant: "eyebrow" }); + const Text = TextWithDefaultProps({}) as TextType; + Text.Normal = TextWithDefaultProps({ variant: "text-sm/normal", tag: "span" }); + Text.H1 = TextWithDefaultProps({ + variant: "heading-xl/bold", + color: "header-primary", + tag: "h1", + }); + Text.H2 = TextWithDefaultProps({ + variant: "heading-lg/semibold", + color: "header-primary", + tag: "h2", + }); + Text.H3 = TextWithDefaultProps({ variant: "heading-md/bold", tag: "h3" }); + Text.H4 = TextWithDefaultProps({ variant: "heading-sm/bold", tag: "h4" }); + Text.Eyebrow = TextWithDefaultProps({ variant: "eyebrow" }); + return Text; +}; -export default Text; +export default getText(); diff --git a/src/renderer/modules/components/TextArea.tsx b/src/renderer/modules/components/TextArea.tsx index 26730a97c..5ef01b082 100644 --- a/src/renderer/modules/components/TextArea.tsx +++ b/src/renderer/modules/components/TextArea.tsx @@ -36,4 +36,4 @@ export type TextAreaType = React.ComponentClass & { defaultProps: TextAreaProps; }; -export default components.TextArea; +export default components.then((v) => v.TextArea); diff --git a/src/renderer/modules/components/TextInput.tsx b/src/renderer/modules/components/TextInput.tsx index 7d6f0525c..dac1a7e18 100644 --- a/src/renderer/modules/components/TextInput.tsx +++ b/src/renderer/modules/components/TextInput.tsx @@ -25,4 +25,4 @@ export type TextInputType = React.ComponentClass & { Sizes: Record<"DEFAULT" | "MINI", string>; }; -export default components.TextInput; +export default components.then((v) => v.TextInput); diff --git a/src/renderer/modules/components/Tooltip.tsx b/src/renderer/modules/components/Tooltip.tsx index e59364245..22f04d5c2 100644 --- a/src/renderer/modules/components/Tooltip.tsx +++ b/src/renderer/modules/components/Tooltip.tsx @@ -65,32 +65,35 @@ export type OriginalTooltipType = React.ComponentClass export type TooltipType = React.FC & TooltipEnums; -const TooltipMod = components.Tooltip; +const getTooltip = async (): Promise => { + const TooltipMod = (await components).Tooltip; -const Tooltip: TooltipType = (props) => ( - - {(tooltipProps) => { - if (props.className) { - if (tooltipProps.className) { - tooltipProps.className += ` ${props.className}`; - } else { - tooltipProps.className = props.className; + const Tooltip: TooltipType = (props) => ( + + {(tooltipProps) => { + if (props.className) { + if (tooltipProps.className) { + tooltipProps.className += ` ${props.className}`; + } else { + tooltipProps.className = props.className; + } } - } - if (props.style) { - if (tooltipProps.style) { - tooltipProps.style = { ...tooltipProps.style, ...props.style }; - } else { - tooltipProps.style = props.style; + if (props.style) { + if (tooltipProps.style) { + tooltipProps.style = { ...tooltipProps.style, ...props.style }; + } else { + tooltipProps.style = props.style; + } } - } - return {props.children}; - }} - -); -Tooltip.Aligns = Aligns; -Tooltip.Colors = TooltipMod.Colors; -Tooltip.Positions = Positions; + return {props.children}; + }} + + ); + Tooltip.Aligns = Aligns; + Tooltip.Colors = TooltipMod.Colors; + Tooltip.Positions = Positions; + return Tooltip; +}; -export default Tooltip; +export default getTooltip(); diff --git a/src/renderer/modules/components/index.ts b/src/renderer/modules/components/index.ts index d5244f228..7a2ab57f7 100644 --- a/src/renderer/modules/components/index.ts +++ b/src/renderer/modules/components/index.ts @@ -6,7 +6,7 @@ const modulePromises: Array<() => Promise> = []; function importTimeout( name: string, moduleImport: Promise, - cb: (mod: T) => void, + cb: (mod: T) => Promise, ): void { modulePromises.push( () => @@ -16,9 +16,9 @@ function importTimeout( rej(new Error(`Module not found: "${name}`)); }, 10_000); void moduleImport - .then((mod) => { + .then(async (mod) => { clearTimeout(timeout); - cb(mod); + await cb(mod); res(); }) .catch((err) => { @@ -32,136 +32,183 @@ function importTimeout( import type { FlexType } from "./Flex"; export type { FlexType }; export let Flex: FlexType; -importTimeout("Flex", import("./Flex"), (mod) => (Flex = mod.default)); +importTimeout("Flex", import("./Flex"), async (mod) => { + Flex = await mod.default; +}); import type { ContextMenuType } from "./ContextMenu"; export type { ContextMenuType }; export let ContextMenu: ContextMenuType; -importTimeout("ContextMenu", import("./ContextMenu"), (mod) => (ContextMenu = mod.default)); +importTimeout("ContextMenu", import("./ContextMenu"), async (mod) => { + ContextMenu = await mod.default; +}); import type { SwitchItemType, SwitchType } from "./SwitchItem"; -export type { SwitchType }; +export type { SwitchType, SwitchItemType }; export let Switch: SwitchType; -importTimeout("Switch", import("./SwitchItem"), (mod) => (Switch = mod.Switch)); - -export type { SwitchItemType }; export let SwitchItem: SwitchItemType; -importTimeout("SwitchItem", import("./SwitchItem"), (mod) => (SwitchItem = mod.SwitchItem)); +importTimeout("SwitchItem", import("./SwitchItem"), async (mod) => { + const comps = await mod.default; + Switch = comps.Switch; + SwitchItem = comps.SwitchItem; +}); import type { RadioItemType, RadioType } from "./RadioItem"; -export type { RadioType }; +export type { RadioType, RadioItemType }; export let Radio: RadioType; -importTimeout("Radio", import("./RadioItem"), (mod) => (Radio = mod.Radio)); - -export type { RadioItemType }; export let RadioItem: RadioItemType; -importTimeout("RadioItem", import("./RadioItem"), (mod) => (RadioItem = mod.RadioItem)); + +importTimeout("RadioItem", import("./RadioItem"), async (mod) => { + const comps = await mod.default; + Radio = comps.Radio; + RadioItem = comps.RadioItem; +}); import type { SelectItemType, SelectType } from "./SelectItem"; -export type { SelectType }; +export type { SelectType, SelectItemType }; export let Select: SelectType; -importTimeout("Select", import("./SelectItem"), (mod) => (Select = mod.Select)); - -export type { SelectItemType }; export let SelectItem: SelectItemType; -importTimeout("SelectItem", import("./SelectItem"), (mod) => (SelectItem = mod.SelectItem)); +importTimeout("SelectItem", import("./SelectItem"), async (mod) => { + const comps = await mod.default; + Select = comps.Select; + SelectItem = comps.SelectItem; +}); import type { ModalType } from "./Modal"; export type { ModalType }; export let Modal: ModalType; -importTimeout("Modal", import("./Modal"), (mod) => (Modal = mod.default)); +importTimeout("Modal", import("./Modal"), async (mod) => { + Modal = await mod.default; +}); import type { DividerType } from "./Divider"; export type { DividerType }; export let Divider: DividerType; -importTimeout("Divider", import("./Divider"), (mod) => (Divider = mod.default)); +importTimeout("Divider", import("./Divider"), async (mod) => { + Divider = await mod.default; +}); import type { TooltipType } from "./Tooltip"; export type { TooltipType }; export let Tooltip: TooltipType; -importTimeout("Tooltip", import("./Tooltip"), (mod) => (Tooltip = mod.default)); +importTimeout("Tooltip", import("./Tooltip"), async (mod) => { + Tooltip = await mod.default; +}); import type { FormTextType } from "./FormText"; export type { FormTextType }; export let FormText: FormTextType; -importTimeout("FormText", import("./FormText"), (mod) => (FormText = mod.FormText)); +importTimeout("FormText", import("./FormText"), (mod) => { + FormText = mod.FormText; + return Promise.resolve(); +}); import type { FormItemType } from "./FormItem"; export type { FormItemType }; export let FormItem: FormItemType; -importTimeout("FormItem", import("./FormItem"), (mod) => (FormItem = mod.default)); +importTimeout("FormItem", import("./FormItem"), async (mod) => { + FormItem = await mod.default; +}); import type { FormNoticeType } from "./FormNotice"; export type { FormNoticeType }; export let FormNotice: FormNoticeType; -importTimeout("FormNotice", import("./FormNotice"), (mod) => (FormNotice = mod.default)); +importTimeout("FormNotice", import("./FormNotice"), async (mod) => { + FormNotice = await mod.default; +}); + +import type { BreadcrumbType } from "./Breadcrumb"; +export type { BreadcrumbType }; +export let Breadcrumb: BreadcrumbType; +importTimeout("Breadcrumb", import("./Breadcrumb"), async (mod) => { + Breadcrumb = await mod.default; +}); import type { ButtonItemType, ButtonType } from "./ButtonItem"; -export type { ButtonType }; +export type { ButtonType, ButtonItemType }; export let Button: ButtonType; -importTimeout("Button", import("./ButtonItem"), (mod) => (Button = mod.Button)); - -export type { ButtonItemType }; export let ButtonItem: ButtonItemType; -importTimeout("ButtonItem", import("./ButtonItem"), (mod) => (ButtonItem = mod.ButtonItem)); +importTimeout("ButtonItem", import("./ButtonItem"), async (mod) => { + const comps = await mod.default; + Button = comps.Button; + ButtonItem = comps.ButtonItem; +}); import type { ClickableType } from "./Clickable"; export type { ClickableType }; export let Clickable: ClickableType; -importTimeout("Clickable", import("./Clickable"), (mod) => (Clickable = mod.default)); +importTimeout("Clickable", import("./Clickable"), async (mod) => { + Clickable = await mod.default; +}); import type { CategoryType } from "./Category"; export type { CategoryType }; export let Category: CategoryType; -importTimeout("Category", import("./Category"), (mod) => (Category = mod.default)); +importTimeout("Category", import("./Category"), async (mod) => { + Category = await mod.default; +}); import type { TextInputType } from "./TextInput"; export type { TextInputType }; export let TextInput: TextInputType; -importTimeout("TextInput", import("./TextInput"), (mod) => (TextInput = mod.default)); +importTimeout("TextInput", import("./TextInput"), async (mod) => { + TextInput = await mod.default; +}); import type { TextAreaType } from "./TextArea"; export type { TextAreaType }; export let TextArea: TextAreaType; -importTimeout("TextArea", import("./TextArea"), (mod) => (TextArea = mod.default)); +importTimeout("TextArea", import("./TextArea"), async (mod) => { + TextArea = await mod.default; +}); import type { SliderItemType, SliderType } from "./SliderItem"; -export type { SliderType }; +export type { SliderType, SliderItemType }; export let Slider: SliderType; -importTimeout("Slider", import("./SliderItem"), (mod) => (Slider = mod.Slider)); - -export type { SliderItemType }; export let SliderItem: SliderItemType; -importTimeout("SliderItem", import("./SliderItem"), (mod) => (SliderItem = mod.SliderItem)); +importTimeout("SliderItem", import("./SliderItem"), async (mod) => { + const comps = await mod.default; + Slider = comps.Slider; + SliderItem = comps.SliderItem; +}); import type { TextType } from "./Text"; export type { TextType }; export let Text: TextType; -importTimeout("Text", import("./Text"), (mod) => (Text = mod.default)); +importTimeout("Text", import("./Text"), async (mod) => { + Text = await mod.default; +}); import type { ErrorBoundaryType } from "./ErrorBoundary"; export type { ErrorBoundaryType }; export let ErrorBoundary: ErrorBoundaryType; -importTimeout("ErrorBoundary", import("./ErrorBoundary"), (mod) => (ErrorBoundary = mod.default)); +importTimeout("ErrorBoundary", import("./ErrorBoundary"), async (mod) => { + ErrorBoundary = await mod.default; +}); import type { LoaderType } from "./Loader"; export type { LoaderType }; export let Loader: LoaderType; -importTimeout("Loader", import("./Loader"), (mod) => (Loader = mod.default)); +importTimeout("Loader", import("./Loader"), async (mod) => { + Loader = await mod.default; +}); import type { CheckboxItemType, CheckboxType } from "./CheckboxItem"; -export type { CheckboxType }; +export type { CheckboxType, CheckboxItemType }; export let Checkbox: CheckboxType; -importTimeout("Checkbox", import("./CheckboxItem"), (mod) => (Checkbox = mod.Checkbox)); - -export type { CheckboxItemType }; export let CheckboxItem: CheckboxItemType; -importTimeout("CheckboxItem", import("./CheckboxItem"), (mod) => (CheckboxItem = mod.CheckboxItem)); +importTimeout("CheckboxItem", import("./CheckboxItem"), async (mod) => { + const comps = await mod.default; + Checkbox = comps.Checkbox; + CheckboxItem = comps.CheckboxItem; +}); import type { NoticeType } from "./Notice"; export type { NoticeType }; export let Notice: NoticeType; -importTimeout("Notice", import("./Notice"), (mod) => (Notice = mod.default)); +importTimeout("Notice", import("./Notice"), async (mod) => { + Notice = await mod.default; +}); /** * @internal diff --git a/src/renderer/modules/i18n.ts b/src/renderer/modules/i18n.ts index 1db7537c0..9a828f9fc 100644 --- a/src/renderer/modules/i18n.ts +++ b/src/renderer/modules/i18n.ts @@ -2,7 +2,7 @@ import type { I18n } from "@common"; import type { loadAllMessagesInLocale as LoadAllMessagesInLocale } from "@discord/intl"; import { waitForProps } from "@webpack"; import { DEFAULT_LOCALE } from "src/constants"; -import type * as definitions from "../../../i18n/en-US.messages"; +import type * as definitions from "../../../i18n/en-US.messages.js"; export let locale: string | undefined; export let t: typeof definitions.default; @@ -14,7 +14,7 @@ export async function load(): Promise { // ! HACK: This is a workaround until ignition issues are fixed. // We need to delay the import of the messages for intl to be loaded and use that module instead of @discord/intl directly. const { default: messages, messagesLoader: loader } = await import( - "../../../i18n/en-US.messages" + "../../../i18n/en-US.messages.js" ); t = messages; messagesLoader = loader; diff --git a/src/renderer/modules/webpack/helpers.ts b/src/renderer/modules/webpack/helpers.ts index 62817c752..f8933d2b7 100644 --- a/src/renderer/modules/webpack/helpers.ts +++ b/src/renderer/modules/webpack/helpers.ts @@ -1,7 +1,8 @@ import type { GetModuleOptions, RawModule, WaitForOptions } from "src/types"; import { getExportsForProps, getModule } from "./get-modules"; import * as filters from "./filters"; -import Flux, { Store } from "../common/flux"; +import type { Store } from "../common/flux"; +import { flux } from "../common"; import { waitForModule } from "./lazy"; // Get by source @@ -247,6 +248,6 @@ export function getByValue( } export function getByStoreName(name: string): T | undefined { - const stores = Flux.Store.getAll(); + const stores = flux.Store.getAll(); return stores.find((store) => store.getName() === name) as T | undefined; } diff --git a/src/types/index.ts b/src/types/index.ts index 04f5e4185..1269ca4bf 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -8,6 +8,7 @@ export type RepluggedWebContents = WebContents & { export enum RepluggedIpcChannels { GET_DISCORD_PRELOAD = "REPLUGGED_GET_DISCORD_PRELOAD", + GET_REPLUGGED_RENDERER = "REPLUGGED_GET_REPLUGGED_RENDERER", GET_QUICK_CSS = "REPLUGGED_GET_QUICK_CSS", SAVE_QUICK_CSS = "REPLUGGED_SAVE_QUICK_CSS", GET_SETTING = "REPLUGGED_GET_SETTING", @@ -22,6 +23,7 @@ export enum RepluggedIpcChannels { UNINSTALL_THEME = "REPLUGGED_UNINSTALL_THEME", LIST_PLUGINS = "REPLUGGED_LIST_PLUGINS", GET_PLUGIN = "REPLUGGED_GET_PLUGIN", + READ_PLUGIN_PLAINTEXT_PATCHES = "REPLUGGED_READ_PLUGIN_PLAINTEXT_PATCHES", UNINSTALL_PLUGIN = "REPLUGGED_UNINSTALL_PLUGIN", REGISTER_RELOAD = "REPLUGGED_REGISTER_RELOAD", GET_ADDON_INFO = "REPLUGGED_GET_ADDON_INFO", diff --git a/src/types/settings.ts b/src/types/settings.ts index 374d349fe..8fa52d090 100644 --- a/src/types/settings.ts +++ b/src/types/settings.ts @@ -1,9 +1,8 @@ import { WEBSITE_URL } from "src/constants"; -import type { Promisable } from "type-fest"; export type SettingsMap = Map; -export type TransactionHandler = () => Promisable; -export type SettingsTransactionHandler = (settings: SettingsMap) => Promisable; +export type TransactionHandler = () => T; +export type SettingsTransactionHandler = (settings: SettingsMap) => T; // eslint-disable-next-line @typescript-eslint/consistent-type-definitions export type GeneralSettings = {