Introduce shelter inspired api/flux

This shows a good example of adding to the Bunny APIs
This commit is contained in:
amsyarasyiq 2024-03-07 21:46:55 +08:00
parent 9fc5326c71
commit 4ffebc8671
8 changed files with 71 additions and 4 deletions

View file

@ -4,6 +4,7 @@
@bunny from BunnyObject {
api {
commands
flux
native {
fs
modules

View file

@ -1,6 +1,6 @@
{
"name": "bunny-types",
"version": "0.0.5",
"version": "0.0.6",
"description": "Types for Bunny, a mod for Discord's mobile apps",
"author": "pylixonly",
"publishConfig": {

View file

@ -3,6 +3,7 @@ import { initCorePlugins } from "@core/plugins";
import initSettings from "@core/ui/settings";
import { patchAssets } from "@lib/api/assets";
import { patchCommands } from "@lib/api/commands";
import { injectFluxInterceptor } from "@lib/api/flux";
import { isThemeSupported } from "@lib/api/native/loader";
import { patchLogHook } from "@lib/debug";
import { initPlugins } from "@lib/managers/plugins";
@ -23,6 +24,7 @@ export default async () => {
// Load everything in parallel
const unloads = await Promise.all([
injectFluxInterceptor(),
patchLogHook(),
patchAssets(),
patchCommands(),

42
src/lib/api/flux/index.ts Normal file
View file

@ -0,0 +1,42 @@
// shelter-mod inspired
import { FluxDispatcher } from "@lib/metro/common";
const blockedSym = Symbol.for("bunny.flux.blocked");
const modifiedSym = Symbol.for("bunny.flux.modified");
export const dispatcher = FluxDispatcher;
type Intercept = (payload: Record<string, any> & { type: string; }) => any;
let intercepts: Intercept[] = [];
export function injectFluxInterceptor() {
const cb = (payload: any) => {
for (const intercept of intercepts) {
const res = intercept(payload);
// nullish -> nothing, falsy -> block, object -> modify
if (res == null) {
continue;
} else if (!res) {
payload[blockedSym] = true;
} else if (typeof res === "object") {
Object.assign(payload, res);
payload[modifiedSym] = true;
}
}
return blockedSym in payload;
};
(dispatcher._interceptors ??= []).unshift(cb);
return () => dispatcher._interceptors &&= dispatcher._interceptors.filter(v => v !== cb);
}
export function intercept(cb: Intercept) {
intercepts.push(cb);
return () => {
intercepts = intercepts.filter(i => i !== cb);
};
}

View file

@ -1,5 +1,6 @@
export * as assets from "./assets";
export * as commands from "./commands";
export * as flux from "./flux";
export * as native from "./native";
export * as patcher from "./patcher";
export * as storage from "./storage";

View file

@ -1,4 +1,5 @@
import { find, findByProps } from "@metro/filters";
import { Dispatcher } from "@metro/types";
const colorModule = findByProps("colors", "unsafe_rawColors");
@ -19,7 +20,7 @@ export const NavigationNative = findByProps("NavigationContainer");
// Flux
export const Flux = findByProps("connectStores");
export const FluxDispatcher = findByProps("_currentDispatchActionType");
export const FluxDispatcher = findByProps("_currentDispatchActionType") as Dispatcher;
// React
export const React = window.React = findByProps("createElement") as typeof import("react");

19
src/lib/metro/types.ts Normal file
View file

@ -0,0 +1,19 @@
export interface Dispatcher {
_actionHandlers: unknown;
_interceptors?: ((payload: any) => void | boolean)[];
_currentDispatchActionType: undefined | string;
_processingWaitQueue: boolean;
_subscriptions: Record<string, Set<(payload: any) => void>>;
_waitQueue: unknown[];
addDependencies(node1: any, node2: any): void;
dispatch(payload: any): Promise<void>;
flushWaitQueue(): void;
isDispatching(): boolean;
register(name: string, actionHandler: Record<string, (e: any) => void>, storeDidChange: (e: any) => boolean): string;
setInterceptor(interceptor?: (payload: any) => void | boolean): void;
subscribe(actionType: string, callback: (payload: any) => void): () => void;
unsubscribe(actionType: string, callback: (payload: any) => void): () => void;
wait(cb: () => void): void;
}

View file

@ -48,6 +48,7 @@ export type BunnyObject = ReturnType<typeof _bunnyObject>;
const _bunnyObject = () => ({ // this function is never used, only exists for typings
api: {
...api,
flux: utils.without({ ...api.flux }, "injectFluxInterceptor"),
commands: { ...api.commands },
native: {
...api.native,
@ -59,7 +60,7 @@ const _bunnyObject = () => ({ // this function is never used, only exists for ty
assets: { ...api.assets },
patcher: utils.without({
...patcher,
unpatchAll: () => {}
unpatchAll: () => { }
}, "unpatchAll")
},
managers: {
@ -101,5 +102,5 @@ const _bunnyObject = () => ({ // this function is never used, only exists for ty
},
debug: { ...debug },
version: debug.versionHash,
unload: () => {},
unload: () => { },
});