Notification API (#467)

Co-authored-by: Ven <vendicated@riseup.net>
Co-authored-by: afn <hey@afn.lol>
Co-authored-by: afn <afnzmn@gmail.com>
This commit is contained in:
Ven 2023-02-10 22:33:34 +01:00 committed by GitHub
parent 6114bc6b16
commit 1d995e58f5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 533 additions and 106 deletions

View file

@ -49,5 +49,8 @@ export const Slider = waitForComponent<t.Slider>("Slider", filters.byCode("close
export const Flex = waitForComponent<t.Flex>("Flex", ["Justify", "Align", "Wrap"]);
export const ButtonWrapperClasses = findByPropsLazy("buttonWrapper", "buttonContent") as Record<string, string>;
/**
* @deprecated Use @utils/margins instead
*/
export const Margins: t.Margins = findByPropsLazy("marginTop20");
export const ButtonLooks: t.ButtonLooks = findByPropsLazy("BLANK", "FILLED", "INVERTED");

View file

@ -19,7 +19,7 @@
import { LazyComponent } from "@utils/misc";
// eslint-disable-next-line path-alias/no-relative
import { FilterFn, waitFor } from "../webpack";
import { FilterFn, filters, waitFor } from "../webpack";
export function waitForComponent<T extends React.ComponentType<any> = React.ComponentType<any> & Record<string, any>>(name: string, filter: FilterFn | string | string[]): T {
let myValue: T = function () {
@ -34,3 +34,7 @@ export function waitForComponent<T extends React.ComponentType<any> = React.Comp
return lazyComponent;
}
export function waitForStore(name: string, cb: (v: any) => void) {
waitFor(filters.byStoreName(name), cb);
}

View file

@ -25,7 +25,7 @@ export let useEffect: typeof React.useEffect;
export let useMemo: typeof React.useMemo;
export let useRef: typeof React.useRef;
export const ReactDOM: typeof import("react-dom") = findByPropsLazy("createPortal", "render");
export const ReactDOM: typeof import("react-dom") & typeof import("react-dom/client") = findByPropsLazy("createPortal", "render");
waitFor("useState", m => {
React = m;

View file

@ -19,36 +19,71 @@
import type * as Stores from "discord-types/stores";
// eslint-disable-next-line path-alias/no-relative
import { filters, findByPropsLazy, mapMangledModuleLazy, waitFor } from "../webpack";
import { filters, findByCodeLazy, findByPropsLazy, mapMangledModuleLazy } from "../webpack";
import { waitForStore } from "./internal";
import * as t from "./types/stores";
export const MessageStore = findByPropsLazy("getRawMessages") as Omit<Stores.MessageStore, "getMessages"> & {
export const Flux: t.Flux = findByPropsLazy("connectStores");
type GenericStore = t.FluxStore & Record<string, any>;
export let MessageStore: Omit<Stores.MessageStore, "getMessages"> & {
getMessages(chanId: string): any;
};
export const PermissionStore = findByPropsLazy("can", "getGuildPermissions");
export const PrivateChannelsStore = findByPropsLazy("openPrivateChannel");
export const GuildChannelStore = findByPropsLazy("getChannels");
export const ReadStateStore = findByPropsLazy("lastMessageId");
export const PresenceStore = findByPropsLazy("setCurrentUserOnConnectionOpen");
export let GuildStore: Stores.GuildStore;
export let UserStore: Stores.UserStore;
export let SelectedChannelStore: Stores.SelectedChannelStore;
export let SelectedGuildStore: any;
export let ChannelStore: Stores.ChannelStore;
export let GuildMemberStore: Stores.GuildMemberStore;
export let RelationshipStore: Stores.RelationshipStore & {
// this is not actually a FluxStore
export const PrivateChannelsStore = findByPropsLazy("openPrivateChannel");
export let PermissionStore: GenericStore;
export let GuildChannelStore: GenericStore;
export let ReadStateStore: GenericStore;
export let PresenceStore: GenericStore;
export let GuildStore: Stores.GuildStore & t.FluxStore;
export let UserStore: Stores.UserStore & t.FluxStore;
export let SelectedChannelStore: Stores.SelectedChannelStore & t.FluxStore;
export let SelectedGuildStore: t.FluxStore & Record<string, any>;
export let ChannelStore: Stores.ChannelStore & t.FluxStore;
export let GuildMemberStore: Stores.GuildMemberStore & t.FluxStore;
export let RelationshipStore: Stores.RelationshipStore & t.FluxStore & {
/** Get the date (as a string) that the relationship was created */
getSince(userId: string): string;
};
export let WindowStore: t.WindowStore;
export const MaskedLinkStore = mapMangledModuleLazy('"MaskedLinkStore"', {
openUntrustedLink: filters.byCode(".apply(this,arguments)")
});
waitFor(["getCurrentUser", "initialize"], m => UserStore = m);
waitFor("getSortedPrivateChannels", m => ChannelStore = m);
waitFor("getCurrentlySelectedChannelId", m => SelectedChannelStore = m);
waitFor("getLastSelectedGuildId", m => SelectedGuildStore = m);
waitFor("getGuildCount", m => GuildStore = m);
waitFor(["getMember", "initialize"], m => GuildMemberStore = m);
waitFor("getRelationshipType", m => RelationshipStore = m);
/**
* React hook that returns stateful data for one or more stores
* You might need a custom comparator (4th argument) if your store data is an object
*
* @param stores The stores to listen to
* @param mapper A function that returns the data you need
* @param idk some thing, idk just pass null
* @param isEqual A custom comparator for the data returned by mapper
*
* @example const user = useStateFromStores([UserStore], () => UserStore.getCurrentUser(), null, (old, current) => old.id === current.id);
*/
export const useStateFromStores: <T>(
stores: t.FluxStore[],
mapper: () => T,
idk?: any,
isEqual?: (old: T, newer: T) => boolean
) => T
= findByCodeLazy("useStateFromStores");
waitForStore("UserStore", s => UserStore = s);
waitForStore("ChannelStore", m => ChannelStore = m);
waitForStore("SelectedChannelStore", m => SelectedChannelStore = m);
waitForStore("SelectedGuildStore", m => SelectedGuildStore = m);
waitForStore("GuildStore", m => GuildStore = m);
waitForStore("GuildMemberStore", m => GuildMemberStore = m);
waitForStore("RelationshipStore", m => RelationshipStore = m);
waitForStore("PermissionStore", m => PermissionStore = m);
waitForStore("PresenceStore", m => PresenceStore = m);
waitForStore("ReadStateStore", m => ReadStateStore = m);
waitForStore("GuildChannelStore", m => GuildChannelStore = m);
waitForStore("MessageStore", m => MessageStore = m);
waitForStore("WindowStore", m => WindowStore = m);

View file

@ -215,9 +215,9 @@ export type Select = ComponentType<PropsWithChildren<{
closeOnSelect?: boolean;
hideIcon?: boolean;
select?(value: any): void;
isSelected?(value: any): boolean;
serialize?(value: any): string;
select(value: any): void;
isSelected(value: any): boolean;
serialize(value: any): string;
clear?(): void;
maxVisibleItems?: number;

View file

@ -19,5 +19,6 @@
export * from "./components";
export * from "./fluxEvents";
export * from "./menu";
export * from "./stores";
export * from "./utils";

40
src/webpack/common/types/stores.d.ts vendored Normal file
View file

@ -0,0 +1,40 @@
/*
* Vencord, a modification for Discord's desktop app
* Copyright (c) 2023 Vendicated and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { FluxDispatcher, FluxEvents } from "./utils";
export class FluxStore {
constructor(dispatcher: FluxDispatcher, eventHandlers?: Partial<Record<FluxEvents, (data: any) => void>>);
emitChange(): void;
getDispatchToken(): string;
getName(): string;
initialize(): void;
initializeIfNeeded(): void;
__getLocalVars(): Record<string, any>;
}
export interface Flux {
Store: typeof FluxStore;
}
export class WindowStore extends FluxStore {
isElementFullScreen(): boolean;
isFocused(): boolean;
windowSize(): Record<"width" | "height", number>;
}

View file

@ -31,20 +31,6 @@ export interface FluxDispatcher {
unsubscribe(event: FluxEvents, callback: (data: any) => void): void;
}
declare class FluxStore {
constructor(dispatcher: FluxDispatcher, eventHandlers?: Partial<Record<FluxEvents, (data: any) => void>>);
emitChange(): void;
getDispatchToken(): string;
getName(): string;
initialize(): void;
initializeIfNeeded(): void;
}
export interface Flux {
Store: typeof FluxStore;
}
export type Parser = Record<
| "parse"
| "parseTopic"

View file

@ -23,7 +23,6 @@ import { _resolveReady,filters, findByCodeLazy, findByPropsLazy, mapMangledModul
import type * as t from "./types/utils";
export let FluxDispatcher: t.FluxDispatcher;
export const Flux: t.Flux = findByPropsLazy("connectStores");
export const RestAPI: t.RestAPI = findByPropsLazy("getAPIBaseURL", "get");
export const moment: typeof import("moment") = findByPropsLazy("parseTwoDigitYear");

View file

@ -50,7 +50,7 @@ export const filters = {
}
return true;
},
byDisplayName: (name: string): FilterFn => m =>
byStoreName: (name: string): FilterFn => m =>
m.constructor?.displayName === name
};
@ -331,15 +331,15 @@ export function findByCodeLazy(...code: string[]) {
/**
* Find a store by its displayName
*/
export function findByDisplayName(name: string) {
return find(filters.byDisplayName(name));
export function findStore(name: string) {
return find(filters.byStoreName(name));
}
/**
* findByDisplayName but lazy
*/
export function findByDisplayNameLazy(name: string) {
return findLazy(filters.byDisplayName(name));
export function findStoreLazy(name: string) {
return findLazy(filters.byStoreName(name));
}
/**