Initial commit

This commit is contained in:
ryan-0324 2024-05-12 05:07:02 -04:00 committed by GitHub
parent bbec51fd19
commit d4783ffd70
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 442 additions and 97 deletions

View file

@ -18,7 +18,7 @@
import { Command } from "@api/Commands"; import { Command } from "@api/Commands";
import { NavContextMenuPatchCallback } from "@api/ContextMenu"; import { NavContextMenuPatchCallback } from "@api/ContextMenu";
import { FluxEvents } from "@webpack/types"; import type { FluxActionType } from "@webpack/types";
import { Promisable } from "type-fest"; import { Promisable } from "type-fest";
// exists to export default definePlugin({...}) // exists to export default definePlugin({...})
@ -114,7 +114,7 @@ export interface PluginDef {
* Allows you to subscribe to Flux events * Allows you to subscribe to Flux events
*/ */
flux?: { flux?: {
[E in FluxEvents]?: (event: any) => void; [E in FluxActionType]?: (event: any) => void;
}; };
/** /**
* Allows you to manipulate context menus * Allows you to manipulate context menus

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -19,39 +19,105 @@
import { DraftType } from "@webpack/common"; import { DraftType } from "@webpack/common";
import { Channel, Guild, Role } from "discord-types/general"; import { Channel, Guild, Role } from "discord-types/general";
import { FluxDispatcher, FluxEvents } from "./utils"; import type { FluxActionHandlers, FluxActionType, FluxDispatcher, FluxPayload } from "./utils";
type GenericFunction = (...args: any[]) => any; type Nullish = null | undefined;
export class FluxStore { type FluxChangeListener = () => boolean;
constructor(dispatcher: FluxDispatcher, eventHandlers?: Partial<Record<FluxEvents, (data: any) => void>>);
class FluxChangeListeners {
has(listener: FluxChangeListener): boolean;
hasAny(): boolean;
invokeAll(): void;
add: (listener: FluxChangeListener) => void;
addConditional: (listener: FluxChangeListener, immediatelyCall?: boolean | undefined /* = true */) => void;
listeners: Set<FluxChangeListener>;
remove: (listener: FluxChangeListener) => void;
}
export class FluxStore<
Dispatcher extends FluxDispatcher<infer P> = FluxDispatcher,
Payload = P<infer A>,
ActionType = A
> {
constructor(
dispatcher: Dispatcher,
actionHandlers: FluxActionHandlers<ActionType>,
band?: number | Nullish
);
static displayName: undefined;
static initialized: Promise<undefined>;
static destroy(): void;
static getAll(): FluxStore[];
static initialize(): void;
addChangeListener(callback: () => void): void;
addReactChangeListener(callback: () => void): void;
removeChangeListener(callback: () => void): void;
removeReactChangeListener(callback: () => void): void;
emitChange(): void; emitChange(): void;
getDispatchToken(): string; getDispatchToken(): string;
getName(): string; getName(): string;
initialize(): void; initialize(): void;
initializeIfNeeded(): void; initializeIfNeeded(): void;
registerActionHandlers: GenericFunction; mustEmitChanges(mustEmitChanges?: ((payload: Payload) => boolean) | Nullish /* = () => true */): void;
syncWith: GenericFunction; registerActionHandlers(actionHandlers: FluxActionHandlers<ActionType>, band?: number | Nullish): void;
waitFor: GenericFunction; syncWith(stores: FluxStore<Dispatcher>[], func: () => boolean | void, delay?: number | Nullish);
__getLocalVars(): Record<string, any>; waitFor(...stores: FluxStore<Dispatcher>[]): void;
__getLocalVars: undefined;
_changeCallbacks: FluxChangeListeners;
_dispatcher: Dispatcher;
_dispatchToken: string;
_isInitialized: boolean;
_mustEmitChanges: ((payload: Payload) => boolean) | Nullish;
_reactChangeCallbacks: FluxChangeListeners;
_syncWiths: {
func: () => boolean | void;
store: FluxStore<Dispatcher>;
}[];
addChangeListener: FluxChangeListeners["add"];
addConditionalChangeListener: FluxChangeListeners["addConditional"];
addReactChangeListener: FluxChangeListeners["add"];
removeChangeListener: FluxChangeListeners["remove"];
removeReactChangeListener: FluxChangeListeners["remove"];
} }
export interface Flux { export interface Flux {
Store: typeof FluxStore; Store: typeof FluxStore;
} }
export class WindowStore extends FluxStore { export type useStateFromStores = <T>(
isElementFullScreen(): boolean; stores: FluxStore[],
isFocused(): boolean; getStateFromStores: () => T,
windowSize(): Record<"width" | "height", number>; dependencies?: any[] | null | undefined,
areStatesEqual?: ((prevState: T, currState: T) => boolean) | undefined
) => T;
export interface DraftObject {
channelId: string;
timestamp: number;
draft: string;
}
interface DraftState {
[userId: string]: {
[channelId: string]: {
[key in DraftType]?: Omit<DraftObject, "channelId">;
} | undefined;
} | undefined;
}
export class DraftStore extends FluxStore {
getDraft(channelId: string, type: DraftType): string;
getRecentlyEditedDrafts(type: DraftType): DraftObject[];
getState(): DraftState;
getThreadDraftWithParentMessageId?(arg: any): any;
getThreadSettings(channelId: string): any | null;
} }
type Emoji = CustomEmoji | UnicodeEmoji; type Emoji = CustomEmoji | UnicodeEmoji;
export interface CustomEmoji { export interface CustomEmoji {
allNamesString: string; allNamesString: string;
animated: boolean; animated: boolean;
@ -150,29 +216,6 @@ export class EmojiStore extends FluxStore {
}; };
} }
export interface DraftObject {
channelId: string;
timestamp: number;
draft: string;
}
interface DraftState {
[userId: string]: {
[channelId: string]: {
[key in DraftType]?: Omit<DraftObject, "channelId">;
} | undefined;
} | undefined;
}
export class DraftStore extends FluxStore {
getDraft(channelId: string, type: DraftType): string;
getRecentlyEditedDrafts(type: DraftType): DraftObject[];
getState(): DraftState;
getThreadDraftWithParentMessageId?(arg: any): any;
getThreadSettings(channelId: string): any | null;
}
export class GuildStore extends FluxStore { export class GuildStore extends FluxStore {
getGuild(guildId: string): Guild; getGuild(guildId: string): Guild;
getGuildCount(): number; getGuildCount(): number;
@ -183,9 +226,151 @@ export class GuildStore extends FluxStore {
getAllGuildRoles(): Record<string, Record<string, Role>>; getAllGuildRoles(): Record<string, Record<string, Role>>;
} }
export type useStateFromStores = <T>( export class User {
stores: t.FluxStore[], constructor(user: object); // TEMP
mapper: () => T,
dependencies?: any, addGuildAvatarHash(guildId: string, avatarHash: string): User;
isEqual?: (old: T, newer: T) => boolean get avatarDecoration(): {
) => T; asset: string;
skuId: string;
} | null;
set avatarDecoration(avatarDecoration: {
asset: string;
skuId: string;
} | null): void;
get createdAt(): Date;
getAvatarSource(guildId?: string | Nullish, canAnimate?: boolean | undefined): { uri: string; };
getAvatarURL(guildId?: string | Nullish, avatarSize?: number | undefined, canAnimate?: boolean | undefined): string;
hasAvatarForGuild(guildId?: string | Nullish): boolean;
hasDisabledPremium(): boolean;
hasFreePremium(): boolean;
hasHadPremium(): boolean;
hasHadSKU(skuId: string): boolean;
hasPremiumUsageFlag(flag: number): boolean;
hasPurchasedFlag(flag: number): boolean;
hasUrgentMessages(): boolean;
hasVerifiedEmailOrPhone(): boolean;
isClaimed(): boolean;
isClyde(): boolean;
isLocalBot(): boolean;
isNonUserBot(): boolean;
isPhoneVerified(): boolean;
isPomelo(): boolean;
isSystemUser(): boolean;
isVerifiedBot(): boolean;
removeGuildAvatarHash(guildId: string): User;
get tag(): string;
toString(): string;
avatar: string;
avatarDecorationData: {
asset: string;
skuId: string;
} | null;
bot: boolean;
clan: null; // TEMP
desktop: boolean;
discriminator: string;
email: string | null;
flags: number;
globalName: string | null;
guildMemberAvatars: Record<string, string>;
hasAnyStaffLevel: () => boolean;
hasBouncedEmail: boolean;
hasFlag: (flag: number) => boolean;
id: string;
isStaff: () => boolean;
isStaffPersonal: () => boolean;
mfaEnabled: boolean;
mobile: boolean;
nsfwAllowed: boolean | null;
personalConnectionId: string | null;
phone: string | null;
premiumType: number | null | undefined;
premiumUsageFlags: number;
publicFlags: number;
purchasedFlags: number;
system: boolean;
username: string;
verified: boolean;
}
export class UserStore extends FluxStore {
static displayName: "UserStore";
static LATEST_SNAPSHOT_VERSION: number;
filter(predicate: (user: User) => any): User[];
findByTag(username: string, discriminator?: string | Nullish): User | undefined;
forEach(callback: (user: User) => void): void;
getCurrentUser(): User /* | undefined */;
getUser(userId: string): User | undefined;
getUsers(): Record<string, User>;
getUserStoreVersion(): number;
handleLoadCache(arg: object): void; // TEMP
takeSnapshot(): {
data: { users: [User] | []; };
version: number;
};
}
export interface UserProfile {
application: null; // TEMP
accentColor: number | null;
applicationRoleConnections: []; // TEMP
badges: {
description: string;
icon: string;
id: string;
link?: string;
}[];
banner: string | null | undefined;
bio: string;
connectedAccounts: {
id: string;
metadata?: Record<string, any>;
name: string;
type: string;
verified: boolean;
}[];
lastFetched: number;
legacyUsername: string | null;
popoutAnimationParticleType?: null | undefined; // TEMP
premiumGuildSince: Date | null;
premiumSince: Date | null;
premiumType: number | null | undefined;
profileEffectId: string | undefined;
profileFetchFailed: boolean;
pronouns: string;
themeColors?: [primaryColor: number, accentColor: number] | undefined;
userId: string;
}
export class UserProfileStore extends FluxStore {
static displayName: "UserProfileStore";
static LATEST_SNAPSHOT_VERSION: number;
getUserProfile(userId: string): UserProfile | undefined;
getGuildMemberProfile<T extends string | Nullish>(userId: string, guildId: T): T extends Nullish ? null : object | undefined; // TEMP
getIsAccessibilityTooltipViewed(): boolean;
getMutualFriends(userId: string): object; // TEMP
getMutualFriendsCount(userId: string): number;
getMutualGuilds(userId: string): object; // TEMP
isFetchingFriends(userId: string): boolean;
isFetchingProfile(userId: string): boolean;
get isSubmitting(): boolean;
takeSnapshot(): {
data: {
userId: string;
profile: UserProfile | undefined;
};
version: number;
};
loadCache: () => void;
}
export class WindowStore extends FluxStore {
isElementFullScreen(): boolean;
isFocused(): boolean;
windowSize(): Record<"width" | "height", number>;
}

View file

@ -16,22 +16,182 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { Guild, GuildMember } from "discord-types/general"; import type { EventEmitter } from "events"; // Discord uses a polyfill for node's EventEmitter
import type { Guild, GuildMember } from "discord-types/general";
import type { ReactNode } from "react"; import type { ReactNode } from "react";
import type { FluxEvents } from "./fluxEvents"; import type { FluxActionType } from "./fluxActionType";
import { i18nMessages } from "./i18nMessages"; import type { i18nMessages } from "./i18nMessages";
export { FluxEvents }; export { FluxActionType };
export interface FluxDispatcher { type Nullish = null | undefined;
_actionHandlers: any;
_subscriptions: any; export interface FluxPayload<ActionType extends FluxActionType = FluxActionType> extends Record<PropertyKey, any> {
dispatch(event: { [key: string]: unknown; type: FluxEvents; }): Promise<void>; type: ActionType;
}
export type FluxActionHandlers<T extends FluxActionType = FluxActionType> = {
[K in T]?: (payload: FluxPayload<K>) => void;
};
class DepGraph<Data = any> {
constructor(options?: { circular?: boolean | undefined; } | undefined);
addDependency(from: string, to: string): void;
addNode(name: string, data?: Data | undefined): void;
clone(): DepGraph<Data>;
dependantsOf(name: string, leavesOnly?: boolean | undefined): string[];
dependenciesOf(name: string, leavesOnly?: boolean | undefined): string[];
getNodeData(name: string): Data;
hasNode(name: string): Data;
overallOrder(leavesOnly?: boolean | undefined): string[];
removeDependency(from: string, to: string): void;
removeNode(name: string): void;
setNodeData(name: string, data?: Data | undefined): void;
size(): number;
circular: boolean | undefined;
nodes: Record<string, Data | string>;
outgoingEdges: Record<string, string[]>;
incomingEdges: Record<string, string[]>;
}
interface FluxActionHandlersGraphNode<
Payload extends FluxPayload = FluxPayload
> {
name: string;
band: number;
actionHandler: (payload: Payload) => void;
storeDidChange: (payload: Payload) => void;
}
type FluxOrderedActionHandlers<Payload extends FluxPayload = FluxPaylod> = Omit<FluxActionHandlersGraphNode<Payload>, "band">[];
class FluxActionHandlersGraph<
Payload extends FluxPayload<infer A> = FluxPayload,
ActionType = A
> {
_addToBand(token: string, band: number): void;
_bandToken(band: number): string;
_computeOrderedActionHandlers(actionType: ActionType): FluxOrderedActionHandlers<Payload>;
_computeOrderedCallbackTokens(): string[];
_invalidateCaches(): void;
_validateDependencies(fromToken: string, toToken: string): void;
addDependencies(fromToken: string, toTokens: string[]): void;
createToken(): string;
getOrderedActionHandlers(payload: Payload): FluxOrderedActionHandlers<Payload>;
register(
name: string,
actionHandlers: FluxActionHandlers<ActionType>,
storeDidChange: (payload: Payload) => void,
band: number,
token?: string | undefined
): string;
_dependencyGraph: DepGraph<FluxActionHandlersGraphNode<Payload>>;
_lastID: number;
_orderedActionHandlers: Record<ActionType, FluxOrderedActionHandlers<Payload> | null>;
_orderedCallbackTokens: string[] | null;
}
interface SentryUtils {
addBreadcrumb: (breadcrumb: {
category?: string | undefined;
data?: any;
level?: string | undefined;
message?: string | undefined;
type?: string | undefined;
}) => void;
}
class FluxActionLog<
Payload extends FluxPayload<infer A> = FluxPayload,
ActionType = A
> {
constructor(actionType: Payload);
get name(): ActionType;
toJSON(): Pick<FluxActionLog<ActionType>, "action" | "createdAt" | "traces"> & {
created_at: FluxActionLog["createdAt"];
};
action: Payload;
createdAt: Date;
error: Error | undefined;
id: number;
startTime: number;
totalTime: number;
traces: {
name: string;
time: number;
}[];
}
class FluxActionLogger<
Payload extends FluxPayload<infer A> = FluxPayload,
ActionType = A
> extends EventEmitter {
constructor(options?: { persist?: boolean | undefined; } | undefined);
getLastActionMetrics(title: string, quantity?: number | undefined /* = 20 */): [
storeName: string,
actionType: ActionType,
totalTime: number
];
getSlowestActions(actionType?: ActionType | Nullish, quantity?: number | undefined /* = 20 */): [];
log<T extends ActionType>(
actionType: T,
callback: (func: <U extends () => any>(storeName: string, func: U) => ReturnType<U>) => void
): FluxActionLog<T>;
logs: FluxActionLog<ActionType>[];
persist: boolean;
}
export class FluxDispatcher<
Payload extends FluxPayload<infer A> = FluxPayload,
ActionType = A
> {
constructor(
defaultBand?: number | undefined /* = 0 */,
actionLogger?: FluxActionLogger<Payload> | Nullish,
sentryUtils?: SentryUtils | Nullish
);
_dispatch(
payload: Payload,
func: <U extends () => any>(storeName: string, func: U) => ReturnType<U>
): false | void;
_dispatchWithDevtools(payload: Payload): void;
_dispatchWithLogging(payload: Payload): void;
addDependencies(fromToken: string, toTokens: string[]): void;
addInterceptor(interceptor: (payload: Payload) => boolean): void;
createToken(): string;
dispatch(payload: Payload): Promise<void>;
flushWaitQueue(): void;
isDispatching(): boolean; isDispatching(): boolean;
subscribe(event: FluxEvents, callback: (data: any) => void): void; register(
unsubscribe(event: FluxEvents, callback: (data: any) => void): void; name: string,
actionHandlers: FluxActionHandlers<ActionType>,
storeDidChange: (payload: Payload) => void,
band?: number | Nullish,
token?: string | undefined
): string;
subscribe(actionType: ActionType, listener: (payload: Payload) => void): void;
unsubscribe(actionType: ActionType, listener: (payload: Payload) => void): void;
wait(callback: () => void): void; wait(callback: () => void): void;
_actionHandlers: FluxActionHandlersGraph<Payload>;
_currentDispatchActionType: ActionType | Nullish;
_defaultBand: number;
_interceptors: ((payload: Payload) => boolean)[];
_processingWaitQueue: boolean;
_sentryUtils: SentryUtils | Nullish;
_subscriptions: Record<ActionType, Set<(payload: Payload) => void> | Nullish>;
_waitQueue: (() => void)[];
actionLogger: FluxActionLogger<Payload>;
functionCache: Record<ActionType, (payload: Payload) => void>;
} }
export type Parser = Record< export type Parser = Record<