feat(lib/discord)!: flatten api structure
This commit is contained in:
parent
3963db69b8
commit
7712b818f8
10 changed files with 223 additions and 206 deletions
|
|
@ -19,7 +19,7 @@
|
|||
"default": "./src/design.ts"
|
||||
},
|
||||
"./flux": {
|
||||
"default": "./src/flux.ts"
|
||||
"default": "./src/flux/index.ts"
|
||||
},
|
||||
"./native": {
|
||||
"default": "./src/native.ts"
|
||||
|
|
@ -46,4 +46,4 @@
|
|||
"default": "./src/modules/settings/_internal.ts"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,23 +1,9 @@
|
|||
import {
|
||||
byDependencies,
|
||||
byName,
|
||||
byProps,
|
||||
createFilterGenerator,
|
||||
preferExports,
|
||||
} from '@revenge-mod/modules/finders/filters'
|
||||
import {
|
||||
lookupModule,
|
||||
lookupModules,
|
||||
} from '@revenge-mod/modules/finders/lookup'
|
||||
import { waitForModules } from '@revenge-mod/modules/finders/wait'
|
||||
import { getModuleDependencies } from '@revenge-mod/modules/metro/utils'
|
||||
import { asap, noop } from '@revenge-mod/utils/callback'
|
||||
import { cached, cacheFilterResultForId } from '../../../modules/src/caches'
|
||||
import { FilterResultFlags } from '../../../modules/src/finders/_internal'
|
||||
import type {
|
||||
Filter,
|
||||
FilterGenerator,
|
||||
} from '@revenge-mod/modules/finders/filters'
|
||||
import { lookupModule } from '@revenge-mod/modules/finders/lookup'
|
||||
import type { Metro } from '@revenge-mod/modules/types'
|
||||
import type { DiscordModules } from '../types'
|
||||
|
||||
|
|
@ -41,132 +27,3 @@ export const [Dispatcher, DispatcherModuleId] = lookupModule(
|
|||
uninitialized: true,
|
||||
},
|
||||
) as [DiscordModules.Flux.Dispatcher, Metro.ModuleID]
|
||||
|
||||
const _stores: Record<string, DiscordModules.Flux.Store> = {}
|
||||
|
||||
/**
|
||||
* A proxy object that allows you to access Flux stores by their name, including uninitialized stores.
|
||||
*
|
||||
* Use `Reflect.ownKeys()` on this proxy to get a list of all initialized stores.
|
||||
*
|
||||
* @see {@link getStore} for a way to get stores lazily.
|
||||
*/
|
||||
export const Stores = new Proxy(_stores, {
|
||||
ownKeys: target => Reflect.ownKeys(target),
|
||||
get: (target, prop: string) =>
|
||||
target[prop] ??
|
||||
// @ts-expect-error: This always uses cache
|
||||
lookupModule(byStoreName(prop), { uninitialized: true })[0],
|
||||
})
|
||||
|
||||
/**
|
||||
* Gets a Flux store by its name, and calls the provided callback with the store.
|
||||
*
|
||||
* @param name The name of the store to get.
|
||||
* @param callback A callback that will be called with the store once it is found.
|
||||
* @returns A function that can be used to cancel the wait for the store.
|
||||
*/
|
||||
export function getStore<T>(
|
||||
name: string,
|
||||
callback: (store: DiscordModules.Flux.Store<T>) => void,
|
||||
) {
|
||||
const store = _stores[name]
|
||||
if (store) {
|
||||
callback(store as DiscordModules.Flux.Store<T>)
|
||||
return noop
|
||||
}
|
||||
|
||||
return waitForModules(byStoreName<T>(name), callback, { cached: true })
|
||||
}
|
||||
|
||||
/// FILTERING
|
||||
|
||||
/*
|
||||
Flux Store dependencies: [
|
||||
6, // _classCallCheck
|
||||
5, // _createClass
|
||||
14, // _possibleConstructorReturn
|
||||
16, // bound getPrototypeOf
|
||||
17, // _inherits
|
||||
(...), // (any amount of dependencies)
|
||||
2 // ImportTracker
|
||||
]
|
||||
*/
|
||||
|
||||
const [, _createClassModuleId] = lookupModule(byName('_createClass'))
|
||||
const [, _classCallCheckModuleId] = lookupModule(byName('_classCallCheck'))
|
||||
const [, _possibleConstructorReturnModuleId] = lookupModule(
|
||||
byName('_possibleConstructorReturn'),
|
||||
)
|
||||
const [, _bound_getPrototypeOfModuleId] = lookupModule(
|
||||
byName('bound getPrototypeOf'),
|
||||
)
|
||||
const [, _inheritsModuleId] = lookupModule(byName('_inherits'))
|
||||
|
||||
const FluxStoreLeadingDeps = [
|
||||
_classCallCheckModuleId,
|
||||
_createClassModuleId,
|
||||
_possibleConstructorReturnModuleId,
|
||||
_bound_getPrototypeOfModuleId,
|
||||
_inheritsModuleId,
|
||||
]
|
||||
|
||||
export type ByStore = FilterGenerator<
|
||||
<T>() => Filter<DiscordModules.Flux.Store<T>, boolean>
|
||||
>
|
||||
|
||||
/**
|
||||
* A dynamic filter that matches all Flux stores.
|
||||
*/
|
||||
export const byStore = createFilterGenerator(
|
||||
(_, id, exports) => {
|
||||
if (exports) return Boolean(exports._dispatchToken)
|
||||
else {
|
||||
const deps = getModuleDependencies(id)!
|
||||
if (deps.length < FluxStoreLeadingDeps.length) return false
|
||||
|
||||
for (let i = 0; i < FluxStoreLeadingDeps.length; i++)
|
||||
if (deps[i] !== FluxStoreLeadingDeps[i]) return false
|
||||
|
||||
return deps[deps.length - 1] === 2
|
||||
}
|
||||
},
|
||||
() => 'revenge.discord.byStore',
|
||||
) as ByStore
|
||||
|
||||
export type ByStoreName = FilterGenerator<
|
||||
<T>(name: string) => Filter<DiscordModules.Flux.Store<T>, true>
|
||||
>
|
||||
|
||||
/**
|
||||
* A with-exports filter that matches a Flux store by its name.
|
||||
*/
|
||||
export const byStoreName = createFilterGenerator(
|
||||
([name], _, exports) =>
|
||||
exports.getName?.length === 0 && exports.getName() === name,
|
||||
([name]) => `revenge.discord.byStoreName(${name})`,
|
||||
) as ByStoreName
|
||||
|
||||
/// CACHING
|
||||
|
||||
waitForModules(byStore(), (store, id) => {
|
||||
const name = store.getName()
|
||||
// Cache stores
|
||||
cacheFilterResultForId(
|
||||
byStoreName.keyFor([name]),
|
||||
id,
|
||||
FilterResultFlags.Default,
|
||||
)
|
||||
Stores[name] = store
|
||||
})
|
||||
|
||||
cached.then(cached => {
|
||||
if (!cached)
|
||||
asap(() => {
|
||||
const lookup = lookupModules(byStore(), {
|
||||
uninitialized: true,
|
||||
})
|
||||
|
||||
while (lookup.next().done);
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,38 +1,10 @@
|
|||
import { Dispatcher } from './common/flux'
|
||||
import type { DiscordModules } from './types'
|
||||
|
||||
const fPatchesAll: Set<FluxEventDispatchPatch<any>> = new Set()
|
||||
const fPatches: Map<string, Set<FluxEventDispatchPatch<any>>> = new Map()
|
||||
import { fPatches, fPatchesAll } from '../patches/flux'
|
||||
import type { DiscordModules } from '../types'
|
||||
|
||||
export type FluxEventDispatchPatch<T extends object = object> = (
|
||||
payload: DiscordModules.Flux.DispatcherPayload & T,
|
||||
) => (DiscordModules.Flux.DispatcherPayload & T) | undefined | void
|
||||
|
||||
const originalDispatch = Dispatcher.dispatch
|
||||
Dispatcher.dispatch = 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 {}
|
||||
}
|
||||
|
||||
if (res) return Reflect.apply(originalDispatch, Dispatcher, [res])
|
||||
// If res is undefined, the event is blocked
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a patch for all Flux events.
|
||||
*
|
||||
2
lib/discord/src/flux/index.ts
Normal file
2
lib/discord/src/flux/index.ts
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
export * from './dispatcher'
|
||||
export * from './stores'
|
||||
149
lib/discord/src/flux/stores.ts
Normal file
149
lib/discord/src/flux/stores.ts
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
import {
|
||||
byName,
|
||||
createFilterGenerator,
|
||||
} from '@revenge-mod/modules/finders/filters'
|
||||
import {
|
||||
lookupModule,
|
||||
lookupModules,
|
||||
} from '@revenge-mod/modules/finders/lookup'
|
||||
import { waitForModules } from '@revenge-mod/modules/finders/wait'
|
||||
import { getModuleDependencies } from '@revenge-mod/modules/metro/utils'
|
||||
import { asap, noop } from '@revenge-mod/utils/callback'
|
||||
import { cached, cacheFilterResultForId } from '../../../modules/src/caches'
|
||||
import { FilterResultFlags } from '../../../modules/src/finders/_internal'
|
||||
import type {
|
||||
Filter,
|
||||
FilterGenerator,
|
||||
} from '@revenge-mod/modules/finders/filters'
|
||||
import type { DiscordModules } from '../types'
|
||||
|
||||
/// STORES
|
||||
|
||||
const _stores: Record<string, DiscordModules.Flux.Store> = {}
|
||||
|
||||
/**
|
||||
* A proxy that allows you to access Flux stores by their name, including uninitialized stores.
|
||||
*
|
||||
* Use `Reflect.ownKeys()` on this proxy to get a list of all initialized stores.
|
||||
*
|
||||
* @see {@link getStore} for a way to get stores lazily.
|
||||
*/
|
||||
export const Stores = new Proxy(_stores, {
|
||||
ownKeys: target => Reflect.ownKeys(target),
|
||||
get: (target, prop: string) =>
|
||||
target[prop] ??
|
||||
// @ts-expect-error: This always uses cache
|
||||
lookupModule(byStoreName(prop), { uninitialized: true })[0],
|
||||
})
|
||||
|
||||
/**
|
||||
* Gets a Flux store by its name, and calls the provided callback with the store.
|
||||
*
|
||||
* @param name The name of the store to get.
|
||||
* @param callback A callback that will be called with the store once it is found.
|
||||
* @returns A function that can be used to cancel the wait for the store.
|
||||
*/
|
||||
export function getStore<T>(
|
||||
name: string,
|
||||
callback: (store: DiscordModules.Flux.Store<T>) => void,
|
||||
) {
|
||||
const store = _stores[name]
|
||||
if (store) {
|
||||
callback(store as DiscordModules.Flux.Store<T>)
|
||||
return noop
|
||||
}
|
||||
|
||||
return waitForModules(byStoreName<T>(name), callback, { cached: true })
|
||||
}
|
||||
|
||||
/// STORE FILTERING
|
||||
|
||||
/*
|
||||
Flux Store dependencies: [
|
||||
6, // _classCallCheck
|
||||
5, // _createClass
|
||||
14, // _possibleConstructorReturn
|
||||
16, // bound getPrototypeOf
|
||||
17, // _inherits
|
||||
(...), // (any amount of dependencies)
|
||||
2 // ImportTracker
|
||||
]
|
||||
*/
|
||||
|
||||
const [, _createClassModuleId] = lookupModule(byName('_createClass'))
|
||||
const [, _classCallCheckModuleId] = lookupModule(byName('_classCallCheck'))
|
||||
const [, _possibleConstructorReturnModuleId] = lookupModule(
|
||||
byName('_possibleConstructorReturn'),
|
||||
)
|
||||
const [, _bound_getPrototypeOfModuleId] = lookupModule(
|
||||
byName('bound getPrototypeOf'),
|
||||
)
|
||||
const [, _inheritsModuleId] = lookupModule(byName('_inherits'))
|
||||
|
||||
const FluxStoreLeadingDeps = [
|
||||
_classCallCheckModuleId,
|
||||
_createClassModuleId,
|
||||
_possibleConstructorReturnModuleId,
|
||||
_bound_getPrototypeOfModuleId,
|
||||
_inheritsModuleId,
|
||||
]
|
||||
|
||||
export type ByStore = FilterGenerator<
|
||||
<T>() => Filter<DiscordModules.Flux.Store<T>, boolean>
|
||||
>
|
||||
|
||||
/**
|
||||
* A dynamic filter that matches all Flux stores.
|
||||
*/
|
||||
export const byStore = createFilterGenerator(
|
||||
(_, id, exports) => {
|
||||
if (exports) return Boolean(exports._dispatchToken)
|
||||
else {
|
||||
const deps = getModuleDependencies(id)!
|
||||
if (deps.length < FluxStoreLeadingDeps.length) return false
|
||||
|
||||
for (let i = 0; i < FluxStoreLeadingDeps.length; i++)
|
||||
if (deps[i] !== FluxStoreLeadingDeps[i]) return false
|
||||
|
||||
return deps[deps.length - 1] === 2
|
||||
}
|
||||
},
|
||||
() => 'revenge.discord.byStore',
|
||||
) as ByStore
|
||||
|
||||
export type ByStoreName = FilterGenerator<
|
||||
<T>(name: string) => Filter<DiscordModules.Flux.Store<T>, true>
|
||||
>
|
||||
|
||||
/**
|
||||
* A with-exports filter that matches a Flux store by its name.
|
||||
*/
|
||||
export const byStoreName = createFilterGenerator(
|
||||
([name], _, exports) =>
|
||||
exports.getName?.length === 0 && exports.getName() === name,
|
||||
([name]) => `revenge.discord.byStoreName(${name})`,
|
||||
) as ByStoreName
|
||||
|
||||
/// STORE CACHING
|
||||
|
||||
waitForModules(byStore(), (store, id) => {
|
||||
const name = store.getName()
|
||||
// Cache stores
|
||||
cacheFilterResultForId(
|
||||
byStoreName.keyFor([name]),
|
||||
id,
|
||||
FilterResultFlags.Default,
|
||||
)
|
||||
Stores[name] = store
|
||||
})
|
||||
|
||||
cached.then(cached => {
|
||||
if (!cached)
|
||||
asap(() => {
|
||||
const lookup = lookupModules(byStore(), {
|
||||
uninitialized: true,
|
||||
})
|
||||
|
||||
while (lookup.next().done);
|
||||
})
|
||||
})
|
||||
30
lib/discord/src/patches/flux.ts
Normal file
30
lib/discord/src/patches/flux.ts
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
import { Dispatcher } from '../common/flux'
|
||||
import type { FluxEventDispatchPatch } from '../flux/dispatcher'
|
||||
|
||||
export const fPatchesAll: Set<FluxEventDispatchPatch<any>> = new Set()
|
||||
export const fPatches: Map<string, Set<FluxEventDispatchPatch<any>>> = new Map()
|
||||
|
||||
const originalDispatch = Dispatcher.dispatch
|
||||
Dispatcher.dispatch = 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 {}
|
||||
}
|
||||
|
||||
if (res) return Reflect.apply(originalDispatch, Dispatcher, [res])
|
||||
// If res is undefined, the event is blocked
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
|
@ -21,3 +21,5 @@ export const [AppStartPerformance] = lookupModule(
|
|||
) as [DiscordModules.AppStartPerformance, Metro.ModuleID]
|
||||
|
||||
AppStartPerformance.mark('👊', 'Pre-init')
|
||||
|
||||
import './patches/flux'
|
||||
|
|
|
|||
|
|
@ -18,9 +18,8 @@ export namespace PluginApiDiscord {
|
|||
|
||||
export interface Modules {
|
||||
mainTabsV2: typeof import('@revenge-mod/discord/modules/main_tabs_v2')
|
||||
settings: typeof import('@revenge-mod/discord/modules/settings') & {
|
||||
renderer: typeof import('@revenge-mod/discord/modules/settings/renderer')
|
||||
}
|
||||
settings: typeof import('@revenge-mod/discord/modules/settings') &
|
||||
typeof import('@revenge-mod/discord/modules/settings/renderer')
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,33 +26,39 @@ registerPlugin(
|
|||
)
|
||||
})
|
||||
|
||||
defineLazyProperties((unscoped.discord = {} as PluginApiDiscord), {
|
||||
actions: () => {
|
||||
return require('@revenge-mod/discord/actions')
|
||||
},
|
||||
common: () => {
|
||||
return require('@revenge-mod/discord/common')
|
||||
},
|
||||
design: () => {
|
||||
return require('@revenge-mod/discord/design')
|
||||
},
|
||||
flux: () => {
|
||||
return require('@revenge-mod/discord/flux')
|
||||
},
|
||||
native: () => {
|
||||
return require('@revenge-mod/discord/native')
|
||||
},
|
||||
modules: () =>
|
||||
defineLazyProperties({} as PluginApiDiscord.Modules, {
|
||||
mainTabsV2: () => {
|
||||
return require('@revenge-mod/discord/modules/main_tabs_v2')
|
||||
defineLazyProperties(
|
||||
(unscoped.discord = {
|
||||
modules: defineLazyProperties(
|
||||
{} as PluginApiDiscord.Modules,
|
||||
{
|
||||
mainTabsV2: () => {
|
||||
return require('@revenge-mod/discord/modules/main_tabs_v2')
|
||||
},
|
||||
settings: () => ({
|
||||
...require('@revenge-mod/discord/modules/settings'),
|
||||
...require('@revenge-mod/discord/modules/settings/renderer'),
|
||||
}),
|
||||
},
|
||||
settings: () => ({
|
||||
...require('@revenge-mod/discord/modules/settings'),
|
||||
renderer: require('@revenge-mod/discord/modules/settings/renderer'),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
),
|
||||
} as PluginApiDiscord),
|
||||
{
|
||||
actions: () => {
|
||||
return require('@revenge-mod/discord/actions')
|
||||
},
|
||||
common: () => {
|
||||
return require('@revenge-mod/discord/common')
|
||||
},
|
||||
flux: () => {
|
||||
return require('@revenge-mod/discord/flux')
|
||||
},
|
||||
design: () => {
|
||||
return require('@revenge-mod/discord/design')
|
||||
},
|
||||
native: () => {
|
||||
return require('@revenge-mod/discord/native')
|
||||
},
|
||||
},
|
||||
)
|
||||
},
|
||||
},
|
||||
PluginFlags.Enabled,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { ToastActionCreators } from '@revenge-mod/discord/actions'
|
||||
import { getStore, Stores } from '@revenge-mod/discord/common/flux'
|
||||
import { getStore, Stores } from '@revenge-mod/discord/flux'
|
||||
import { byProps } from '@revenge-mod/modules/finders/filters'
|
||||
import { getModule } from '@revenge-mod/modules/finders/get'
|
||||
import { instead } from '@revenge-mod/patcher'
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue