Skip to content

Commit

Permalink
better idk?
Browse files Browse the repository at this point in the history
  • Loading branch information
yofukashino committed Nov 14, 2024
1 parent 52f92b5 commit cbe31d3
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 30 deletions.
18 changes: 0 additions & 18 deletions src/renderer/modules/webpack/filters.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { getExportsForProps } from "./get-modules";
import { sourceStrings } from "./patch-load";
import type { RawModule } from "../../../types";
import { Store } from "../common/flux";

/**
* Get a module that has all the given properties on one of its exports
Expand Down Expand Up @@ -59,20 +58,3 @@ export const byValue = (match: string | RegExp) => {
return false;
};
};

/**
* Get a module that has the store with given name
* @param name Name of Store
*/
export const byStoreName = (name: string) => {
return (m: RawModule) => {
if (!m.exports || typeof m.exports !== "object" || typeof m.exports !== "object") {
return false;
}
return ["default", "Z", "ZP"].some(
(key) =>
(m.exports as { [key: string]: Store & { constructor: { displayName: string } } })[key]
?.constructor.displayName === name,
);
};
};
11 changes: 0 additions & 11 deletions src/renderer/modules/webpack/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,14 +250,3 @@ export function getByStoreName<T extends Store>(name: string): T | undefined {
const stores = Flux.Store.getAll();
return stores.find((store) => store.getName() === name) as T | undefined;
}

export async function waitForStoreName<T extends Store>(
name: string,
options?: { timeout: number },
): Promise<T | undefined> {
const module = await waitForModule<{ default?: T; ZP?: T; Z?: T } & T>(
filters.byStoreName(name),
options,
);
return module.default ?? module.ZP ?? module.Z ?? module;
}
2 changes: 1 addition & 1 deletion src/renderer/modules/webpack/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export { waitForModule } from "./lazy";
export { waitForModule, waitForStore } from "./lazy";

export { getFunctionBySource, getFunctionKeyBySource } from "./inner-search";

Expand Down
82 changes: 82 additions & 0 deletions src/renderer/modules/webpack/lazy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import type { Filter, LazyCallback, LazyListener, RawModule, WaitForOptions } fr

import { getExports, getModule } from "./get-modules";

import type { Store } from "@common/flux";

/**
* Listeners that will be checked when each module is initialized
*/
Expand Down Expand Up @@ -116,3 +118,83 @@ export async function waitForModule<T>(
clearTimeout(timeout);
});
}

export async function waitForStore<T extends Store>(
filter: string,
options?: { timeout?: number },
): Promise<T>;

/**
* Wait for a module that matches the given filter
* @param filter Store Name
* @param options Options
* @param options.timeout Timeout in milliseconds
*
* @see {@link filters}
*
* @remarks
* Some modules may not be available immediately when Discord starts and will take up to a few seconds.
* This is useful to ensure that the module is available before using it.
*/
export async function waitForStore<T extends Store>(
name: string,
options: { timeout?: number } = {},
): Promise<T> {
const { flux } = await import("@common");
const existingStores = flux.Store.getAll() as Store[] & {
listeners?: Set<Array<string | ((store: Store) => void)>>;
onPush?: (name: string, callback: (store: Store) => void) => () => void;
};
if (!existingStores.listeners) {
existingStores.listeners = new Set<[string, (store: Store) => void]>();
existingStores.onPush = (name, callback) => {
const store = existingStores.find((store) => store.getName() === name);
if (store) {
callback(store);
}
const listener = [name, callback];
existingStores.listeners!.add(listener);
return () => existingStores.listeners!.delete(listener);
};
existingStores.push = (...stores: Store[]) => {
for (const [name, callback] of existingStores.listeners!) {
const store = stores.find((store) => store.getName() === name);
if (store) {
(callback as (store: Store) => void)(store);
}
}
return Array.prototype.push.call(existingStores, ...stores);
};
}
const existingStore = existingStores.find((store) => store.getName() === name) as T | undefined;

if (existingStore) {
return existingStore;
}

// Promise that resolves with the module
const promise = new Promise<T>((resolve) => {
const unregister = existingStores.onPush!(name, (store: Store) => {
unregister();
resolve(store as T);
});
});

// If no timeout, then wait for as long as it takes
if (!options.timeout) return promise;

// Different in Node and browser environments--number in browser, NodeJS.Timeout in Node
let timeout: ReturnType<typeof setTimeout>;

// Promise that rejects if the module takes too long to appear
const timeoutPromise = new Promise<never>((_, reject) => {
timeout = setTimeout(() => {
reject(new Error(`waitForStore timed out after ${options.timeout}ms`));
}, options.timeout);
});

// Go with whichever happens first
return Promise.race([promise, timeoutPromise]).finally(() => {
clearTimeout(timeout);
});
}

0 comments on commit cbe31d3

Please sign in to comment.