diff --git a/lib/discord/package.json b/lib/discord/package.json index e135c84..4292aaa 100644 --- a/lib/discord/package.json +++ b/lib/discord/package.json @@ -21,6 +21,9 @@ "./design": { "default": "./src/design.ts" }, + "./flux": { + "default": "./src/flux.ts" + }, "./native": { "default": "./src/native.ts" }, diff --git a/lib/discord/src/flux.ts b/lib/discord/src/flux.ts new file mode 100644 index 0000000..be3c0ce --- /dev/null +++ b/lib/discord/src/flux.ts @@ -0,0 +1,92 @@ +import { Dispatcher } from './common/flux' +import type { DiscordModules } from './types' + +const fPatchesAll: Set = new Set() +const fPatches: Map> = new Map() + +export type FluxEventDispatchPatch = ( + payload: DiscordModules.Flux.DispatcherPayload, +) => DiscordModules.Flux.DispatcherPayload | undefined | void + +;(Dispatcher._interceptors ??= []).unshift(payload => { + let res: typeof payload | undefined | void = payload + + for (const patch of fPatchesAll) + try { + res = patch(res!) + if (!res) break + } catch {} + + if (res) { + const specifics = fPatches.get(payload.type) + if (specifics?.size) + for (const patch of specifics) + try { + res = patch(res!) + if (!res) break + } catch {} + } + + return !res +}) + +/** + * Registers a patch for all Flux events. + * + * @see {@link onFluxEventDispatched} for more details. + * + * @param patch The patch function to apply when any Flux event is dispatched. + * @returns A function that can be used to remove the patch. + */ +export function onAnyFluxEventDispatched(patch: FluxEventDispatchPatch) { + fPatchesAll.add(patch) + + return () => { + fPatchesAll.delete(patch) + } +} + +/** + * Registers a patch for a specific Flux event type. + * @param type The type of the Flux event to patch. + * @param patch The patch function to apply when the event is dispatched. + * @returns A function that can be used to remove the patch. + * + * @example Blocking the disptach + * ```ts + * // Returning falsy values will prevent the event from being dispatched. + * onFluxEventDispatched('TYPING_START', () => {}) + * ``` + * + * @example Modifying the payload + * ```ts + * onFluxEventDispatched('TYPING_START', payload => { + * // Send the typing event to this channel instead. + * payload.channelId = '123456789012345678' + * // Make sure to return the modified payload! + * return payload + * }) + * ``` + * + * @example Reading and passing through the payload + * ```ts + * onFluxEventDispatched('TYPING_START', payload => { + * console.log('Typing started:', payload) + * // Do nothing, just return the payload. + * return payload + * }) + * ``` + */ +export function onFluxEventDispatched( + type: DiscordModules.Flux.DispatcherPayload['type'], + patch: FluxEventDispatchPatch, +) { + let set = fPatches.get(type) + if (!set) fPatches.set(type, (set = new Set())) + + set.add(patch) + + return () => { + set.delete(patch) + } +} diff --git a/lib/discord/src/types/index.ts b/lib/discord/src/types/index.ts index b8811b1..2b74e61 100644 --- a/lib/discord/src/types/index.ts +++ b/lib/discord/src/types/index.ts @@ -29,7 +29,10 @@ export * from './revenge' export namespace DiscordModules { export namespace Flux { - export type DispatcherPayload = any + export interface DispatcherPayload { + type: string + [key: PropertyKey]: any + } export type DispatcherDependency = any export interface StoreChangeCallbacks { diff --git a/lib/discord/src/types/revenge.ts b/lib/discord/src/types/revenge.ts index b8634c8..9ec20ae 100644 --- a/lib/discord/src/types/revenge.ts +++ b/lib/discord/src/types/revenge.ts @@ -4,6 +4,7 @@ export interface PluginApiDiscord { actions: PluginApiDiscord.Actions common: PluginApiDiscord.Common design: PluginApiDiscord.Design + flux: PluginApiDiscord.Flux modules: PluginApiDiscord.Modules native: PluginApiDiscord.Native } @@ -12,6 +13,7 @@ export namespace PluginApiDiscord { export type Actions = typeof import('@revenge-mod/discord/actions') export type Common = typeof import('@revenge-mod/discord/common') export type Design = typeof import('@revenge-mod/discord/design') + export type Flux = typeof import('@revenge-mod/discord/flux') export type Native = typeof import('@revenge-mod/discord/native') export interface Modules { diff --git a/src/plugins/init/api.discord/index.ts b/src/plugins/init/api.discord/index.ts index e9e6363..d8ac69e 100644 --- a/src/plugins/init/api.discord/index.ts +++ b/src/plugins/init/api.discord/index.ts @@ -36,6 +36,9 @@ registerPlugin( design: () => { return require('@revenge-mod/discord/design') }, + flux: () => { + return require('@revenge-mod/discord/flux') + }, native: () => { return require('@revenge-mod/discord/native') },