Type a bunch of stores (#3861)

Co-authored-by: prism <snawalt420@proton.me>
This commit is contained in:
thororen 2025-12-27 17:22:20 -05:00 committed by GitHub
parent d70d05b6c7
commit 746f31e412
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
69 changed files with 5167 additions and 245 deletions

View file

@ -12,4 +12,4 @@ export const enum ChannelType {
GUILD_DIRECTORY = 14,
GUILD_FORUM = 15,
GUILD_MEDIA = 16
}
}

View file

@ -3,3 +3,4 @@ export * from "./channel";
export * from "./commands";
export * from "./messages";
export * from "./misc";
export * from "./user";

View file

@ -2,3 +2,102 @@ export const enum CloudUploadPlatform {
REACT_NATIVE = 0,
WEB = 1,
}
export const enum DraftType {
ChannelMessage = 0,
ThreadSettings = 1,
FirstThreadMessage = 2,
ApplicationLauncherCommand = 3,
Poll = 4,
SlashCommand = 5,
ForwardContextMessage = 6,
}
export const enum GuildScheduledEventStatus {
SCHEDULED = 1,
ACTIVE = 2,
COMPLETED = 3,
CANCELED = 4,
}
export const enum GuildScheduledEventEntityType {
STAGE_INSTANCE = 1,
VOICE = 2,
EXTERNAL = 3,
}
export const enum GuildScheduledEventPrivacyLevel {
GUILD_ONLY = 2,
}
export const enum ParticipantType {
STREAM = 0,
HIDDEN_STREAM = 1,
USER = 2,
ACTIVITY = 3,
}
export const enum RTCPlatform {
DESKTOP = 0,
MOBILE = 1,
XBOX = 2,
PLAYSTATION = 3,
}
export const enum VideoSourceType {
VIDEO = 0,
CAMERA_PREVIEW = 1,
}
export const enum EmojiIntention {
REACTION = 0,
STATUS = 1,
COMMUNITY_CONTENT = 2,
CHAT = 3,
GUILD_STICKER_RELATED_EMOJI = 4,
GUILD_ROLE_BENEFIT_EMOJI = 5,
SOUNDBOARD = 6,
VOICE_CHANNEL_TOPIC = 7,
GIFT = 8,
AUTO_SUGGESTION = 9,
POLLS = 10,
PROFILE = 11,
MESSAGE_CONFETTI = 12,
GUILD_PROFILE = 13,
CHANNEL_NAME = 14,
DEFAULT_REACT_EMOJI = 15,
}
export const enum LoadState {
NOT_LOADED = 0,
LOADING = 1,
LOADED = 2,
ERROR = 3,
}
export const enum ConnectionStatsFlags {
TRANSPORT = 1,
OUTBOUND = 2,
INBOUND = 4,
ALL = 7,
}
export const enum SpeakingFlags {
NONE = 0,
VOICE = 1,
SOUNDSHARE = 2,
PRIORITY = 4,
HIDDEN = 8,
}
export const enum GoLiveQualityMode {
AUTO = 1,
FULL = 2,
}
export const enum VoiceProcessingStateReason {
CPU_OVERUSE = 1,
FAILED = 2,
VAD_CPU_OVERUSE = 3,
INITIALIZED = 4,
}

View file

@ -0,0 +1,22 @@
export const enum RelationshipType {
NONE = 0,
FRIEND = 1,
BLOCKED = 2,
INCOMING_REQUEST = 3,
OUTGOING_REQUEST = 4,
IMPLICIT = 5,
SUGGESTION = 6
}
export enum GiftIntentType {
FRIEND_ANNIVERSARY = 0
}
export const enum ReadStateType {
CHANNEL = 0,
GUILD_EVENT = 1,
NOTIFICATION_CENTER = 2,
GUILD_HOME = 3,
GUILD_ONBOARDING_QUESTION = 4,
MESSAGE_REQUESTS = 5,
}

View file

@ -1,23 +1,89 @@
import { Guild } from "./Guild";
import { User } from "./User";
export interface Application {
export interface ApplicationExecutable {
os: "win32" | "darwin" | "linux";
name: string;
isLauncher: boolean;
}
export interface ApplicationThirdPartySku {
id: string;
sku: string;
distributor: string;
}
export interface ApplicationDeveloper {
id: string;
name: string;
description?: string | null;
type: number | null;
icon: string | null | undefined;
is_discoverable: boolean;
is_monetized: boolean;
is_verified: boolean;
bot?: User;
deeplink_uri?: string;
flags?: number;
privacy_policy_url?: string;
terms_of_service_url?: string;
install_params?: ApplicationInstallParams;
}
export interface ApplicationInstallParams {
permissions: string | null;
scopes: string[];
}
export interface Application {
id: string;
name: string;
icon: string | null;
description: string;
type: number | null;
coverImage: string | null;
primarySkuId: string | undefined;
bot: User | null;
splash: string | undefined;
thirdPartySkus: ApplicationThirdPartySku[];
isMonetized: boolean;
isVerified: boolean;
roleConnectionsVerificationUrl: string | undefined;
parentId: string | undefined;
connectionEntrypointUrl: string | undefined;
overlay: boolean;
overlayWarn: boolean;
overlayCompatibilityHook: boolean;
overlayMethods: number;
hook: boolean;
aliases: string[];
publishers: ApplicationDeveloper[];
developers: ApplicationDeveloper[];
storeListingSkuId: string | undefined;
guildId: string | null;
guild: Guild | undefined;
executables: ApplicationExecutable[];
hashes: string[];
eulaId: string | undefined;
slug: string | undefined;
flags: number;
maxParticipants: number | undefined;
tags: string[];
embeddedActivityConfig: Record<string, unknown> | undefined;
team: ApplicationTeam | undefined;
integrationTypesConfig: Record<string, Record<string, unknown>>;
storefront_available: boolean;
termsOfServiceUrl: string | undefined;
privacyPolicyUrl: string | undefined;
isDiscoverable: boolean;
customInstallUrl: string | undefined;
installParams: ApplicationInstallParams | undefined;
directoryEntry: Record<string, unknown> | undefined;
categories: string[] | undefined;
linkedGames: string[] | undefined;
deepLinkUri: string | undefined;
}
export interface ApplicationTeam {
id: string;
name: string;
icon: string | null;
members: ApplicationTeamMember[];
ownerUserId: string;
}
export interface ApplicationTeamMember {
user: User;
teamId: string;
membershipState: number;
permissions: string[];
role: string;
}

View file

@ -1,42 +1,114 @@
/** Union type for both custom (guild) emojis and unicode emojis. */
export type Emoji = CustomEmoji | UnicodeEmoji;
/**
* Custom emoji uploaded to a guild.
*/
export interface CustomEmoji {
/** Discriminator for custom emojis. */
type: 1;
allNamesString: string;
/** Whether the emoji is animated (GIF). */
animated: boolean;
/** Whether the emoji is available for use. */
available: boolean;
/** Guild id this emoji belongs to. */
guildId: string;
/** Unique emoji id (snowflake). */
id: string;
/** Whether the emoji is managed by an integration (e.g. Twitch). */
managed: boolean;
/** Emoji name without colons. */
name: string;
/** Original name before any modifications. */
originalName?: string;
/** Whether the emoji requires colons to use. */
require_colons: boolean;
/** Role ids that can use this emoji (empty array means everyone). */
roles: string[];
/** Version number, incremented when emoji is updated. */
version?: number;
}
/**
* Built-in unicode emoji.
*/
export interface UnicodeEmoji {
/** Discriminator for unicode emojis. */
type: 0;
diversityChildren: Record<any, any>;
emojiObject: {
names: string[];
surrogates: string;
unicodeVersion: number;
};
/** Skin tone variant emojis keyed by diversity surrogate code (e.g. "1f3fb" for light skin). */
diversityChildren: Record<string, UnicodeEmoji>;
/** Raw emoji data from Discord's emoji dataset. */
emojiObject: EmojiObject;
/** Index position in the emoji list. */
index: number;
/** Unicode surrogate pair(s) for this emoji. */
surrogates: string;
/** Unique name identifier for this emoji. */
uniqueName: string;
/** Whether to render using sprite sheet. */
useSpriteSheet: boolean;
/** Original name if renamed in context. */
originalName?: string;
/** Emoji id when used in custom emoji context. */
id?: string;
/** Guild id when used in guild context. */
guildId?: string;
/** Formatted string of all emoji names. */
get allNamesString(): string;
get animated(): boolean;
get defaultDiversityChild(): any;
/** Always false for unicode emojis. */
get animated(): false;
/** Default skin tone variant or undefined if no diversity. */
get defaultDiversityChild(): UnicodeEmoji | undefined;
/** Whether this emoji supports skin tone modifiers. */
get hasDiversity(): boolean | undefined;
/** Whether this emoji is a skin tone variant of another. */
get hasDiversityParent(): boolean | undefined;
/** Whether this emoji supports multiple diversity modifiers (e.g. handshake with two skin tones). */
get hasMultiDiversity(): boolean | undefined;
/** Whether this emoji is a multi-diversity variant of another. */
get hasMultiDiversityParent(): boolean | undefined;
get managed(): boolean;
/** Always true for unicode emojis. */
get managed(): true;
/** Primary emoji name. */
get name(): string;
/** All names/aliases for this emoji. */
get names(): string[];
/** Surrogate sequence with optional diversity modifier. */
get optionallyDiverseSequence(): string | undefined;
/** Unicode version when this emoji was added. */
get unicodeVersion(): number;
/** CDN url for emoji image. */
get url(): string;
/**
* Iterates over all diversity variants of this emoji.
* @param callback Function called for each diversity variant.
*/
forEachDiversity(callback: (emoji: UnicodeEmoji) => void): void;
/**
* Iterates over all names/aliases of this emoji.
* @param callback Function called for each name.
*/
forEachName(callback: (name: string) => void): void;
}
/**
* Raw emoji data from Discord's emoji dataset.
*/
export interface EmojiObject {
/** All names/aliases for this emoji. */
names: string[];
/** Unicode surrogate pair(s). */
surrogates: string;
/** Unicode version when this emoji was added. */
unicodeVersion: number;
/** Index in the sprite sheet for rendering. */
spriteIndex?: number;
/** Whether this emoji supports multiple skin tone modifiers. */
hasMultiDiversity?: boolean;
/** Whether this emoji is a diversity variant with a multi-diversity parent. */
hasMultiDiversityParent?: boolean;
/** Skin tone modifier codes for this variant (e.g. ["1f3fb"] or ["1f3fb", "1f3fc"]). */
diversity?: string[];
/** Sprite indices of diversity children for parent emojis. */
diversityChildren?: number[];
}

View file

@ -0,0 +1,73 @@
import { FluxStore } from "..";
export type ReducedMotionPreference = "auto" | "reduce" | "no-preference";
export type ForcedColorsPreference = "none" | "active";
export type ContrastPreference = "no-preference" | "more" | "less" | "custom";
export type RoleStyle = "username" | "dot" | "hidden";
export interface AccessibilityState {
fontSize: number;
zoom: number;
keyboardModeEnabled: boolean;
contrastMode: string;
colorblindMode: boolean;
lowContrastMode: boolean;
saturation: number;
contrast: number;
desaturateUserColors: boolean;
forcedColorsModalSeen: boolean;
keyboardNavigationExplainerModalSeen: boolean;
messageGroupSpacing: number | null;
systemPrefersReducedMotion: ReducedMotionPreference;
systemPrefersCrossfades: boolean;
prefersReducedMotion: ReducedMotionPreference;
systemForcedColors: ForcedColorsPreference;
syncForcedColors: boolean;
systemPrefersContrast: ContrastPreference;
alwaysShowLinkDecorations: boolean;
roleStyle: RoleStyle;
displayNameStylesEnabled: boolean;
submitButtonEnabled: boolean;
syncProfileThemeWithUserTheme: boolean;
enableCustomCursor: boolean;
switchIconsEnabled: boolean;
}
export class AccessibilityStore extends FluxStore {
get fontScale(): number;
get fontSize(): number;
get isFontScaledUp(): boolean;
get isFontScaledDown(): boolean;
get fontScaleClass(): string;
get zoom(): number;
get isZoomedIn(): boolean;
get isZoomedOut(): boolean;
get keyboardModeEnabled(): boolean;
get colorblindMode(): boolean;
get lowContrastMode(): boolean;
get saturation(): number;
get contrast(): number;
get desaturateUserColors(): boolean;
get forcedColorsModalSeen(): boolean;
get keyboardNavigationExplainerModalSeen(): boolean;
get messageGroupSpacing(): number;
get isMessageGroupSpacingIncreased(): boolean;
get isMessageGroupSpacingDecreased(): boolean;
get isSubmitButtonEnabled(): boolean;
get syncProfileThemeWithUserTheme(): boolean;
get systemPrefersReducedMotion(): ReducedMotionPreference;
get rawPrefersReducedMotion(): ReducedMotionPreference;
get useReducedMotion(): boolean;
get systemForcedColors(): ForcedColorsPreference;
get syncForcedColors(): boolean;
get useForcedColors(): boolean;
get systemPrefersContrast(): ContrastPreference;
get systemPrefersCrossfades(): boolean;
get alwaysShowLinkDecorations(): boolean;
get enableCustomCursor(): boolean;
get roleStyle(): RoleStyle;
get displayNameStylesEnabled(): boolean;
get isHighContrastModeEnabled(): boolean;
get isSwitchIconsEnabled(): boolean;
getUserAgnosticState(): AccessibilityState;
}

View file

@ -0,0 +1,33 @@
import { Channel, FluxStore } from "..";
export interface ThreadJoined {
channel: Channel;
joinTimestamp: number;
}
export type ThreadsForParent = Record<string, ThreadJoined>;
export type ThreadsForGuild = Record<string, ThreadsForParent>;
export type AllActiveJoinedThreads = Record<string, ThreadsForGuild>;
export interface NewThreadCounts {
[parentChannelId: string]: number;
}
export class ActiveJoinedThreadsStore extends FluxStore {
computeAllActiveJoinedThreads(guildId?: string | null): Channel[];
getActiveJoinedRelevantThreadsForGuild(guildId: string): ThreadsForGuild;
getActiveJoinedRelevantThreadsForParent(guildId: string, parentChannelId: string): ThreadsForParent;
getActiveJoinedThreadsForGuild(guildId: string): ThreadsForGuild;
getActiveJoinedThreadsForParent(guildId: string, parentChannelId: string): ThreadsForParent;
getActiveJoinedUnreadThreadsForGuild(guildId: string): ThreadsForGuild;
getActiveJoinedUnreadThreadsForParent(guildId: string, parentChannelId: string): ThreadsForParent;
getActiveThreadCount(guildId: string, parentChannelId: string): number;
getActiveUnjoinedThreadsForGuild(guildId: string): ThreadsForGuild;
getActiveUnjoinedThreadsForParent(guildId: string, parentChannelId: string): ThreadsForParent;
getActiveUnjoinedUnreadThreadsForGuild(guildId: string): ThreadsForGuild;
getActiveUnjoinedUnreadThreadsForParent(guildId: string, parentChannelId: string): ThreadsForParent;
getAllActiveJoinedThreads(): AllActiveJoinedThreads;
getNewThreadCount(guildId: string, parentChannelId: string): number;
getNewThreadCountsForGuild(guildId: string): NewThreadCounts;
hasActiveJoinedUnreadThreads(guildId: string, parentChannelId: string): boolean;
}

View file

@ -0,0 +1,23 @@
import { Application, FluxStore } from "..";
export interface ApplicationStoreState {
botUserIdToAppUsage: Record<string, ApplicationUsage>;
}
export interface ApplicationUsage {
applicationId: string;
lastUsedMs: number;
}
export class ApplicationStore extends FluxStore {
getState(): ApplicationStoreState;
getApplication(applicationId: string): Application;
getApplicationByName(name: string): Application | undefined;
getApplicationLastUpdated(applicationId: string): number | undefined;
getGuildApplication(guildId: string, type: number): Application | undefined;
getGuildApplicationIds(guildId: string): string[];
getAppIdForBotUserId(botUserId: string): string | undefined;
getFetchingOrFailedFetchingIds(): string[];
isFetchingApplication(applicationId: string): boolean;
didFetchingApplicationFail(applicationId: string): boolean;
}

View file

@ -0,0 +1,24 @@
import { FluxStore } from "..";
export interface Call {
channelId: string;
messageId: string | null;
region: string | null;
ringing: string[];
unavailable: boolean;
regionUpdated: boolean;
}
export interface CallStoreState {
calls: Record<string, Call>;
enqueuedRings: Record<string, string[]>;
}
export class CallStore extends FluxStore {
getCall(channelId: string): Call;
getCalls(): Call[];
getMessageId(channelId: string): string | null;
isCallActive(channelId: string, messageId?: string): boolean;
isCallUnavailable(channelId: string): boolean;
getInternalState(): CallStoreState;
}

View file

@ -0,0 +1,105 @@
import { FluxStore, User, VoiceState } from "..";
import { ParticipantType, RTCPlatform } from "../../enums";
export type RTCLayout = "normal" | "minimum" | "no-chat" | "full-screen" | "haven";
export type RTCMode = "video" | "voice";
export type RTCLayoutContext = "OVERLAY" | "APP" | "POPOUT" | "CALL_TILE_POPOUT";
export type ParticipantFilterType = "VIDEO" | "STREAM" | "FILTERED" | "SPEAKING" | "ACTIVITY" | "NOT_POPPED_OUT";
export interface StreamResolution {
height: number;
width: number;
}
export interface Stream {
channelId: string;
guildId: string | null;
ownerId: string;
streamType: string;
}
export interface BaseParticipant {
id: string;
type: ParticipantType;
isPoppedOut?: boolean;
}
export interface UserParticipant extends BaseParticipant {
type: ParticipantType.USER;
user: User;
voiceState: VoiceState | null;
voicePlatform: RTCPlatform | null;
speaking: boolean;
voiceDb: number;
latched: boolean;
lastSpoke: number;
soundsharing: boolean;
ringing: boolean;
userNick: string;
// TODO: type
userAvatarDecoration: any | null;
localVideoDisabled: boolean;
userVideo?: boolean;
streamId?: string;
}
export interface StreamParticipant extends BaseParticipant {
type: ParticipantType.STREAM | ParticipantType.HIDDEN_STREAM;
user: User;
userNick: string;
userVideo: boolean;
stream: Stream;
maxResolution?: StreamResolution;
maxFrameRate?: number;
streamId?: string;
}
export interface ActivityParticipant extends BaseParticipant {
type: ParticipantType.ACTIVITY;
applicationId: string;
activityType: number;
activityUrl: string;
participants: string[];
guildId: string | null;
sortKey: string;
}
export type Participant = UserParticipant | StreamParticipant | ActivityParticipant;
export interface SelectedParticipantStats {
view_mode_grid_duration_ms?: number;
view_mode_focus_duration_ms?: number;
view_mode_toggle_count?: number;
}
export interface ChannelRTCState {
voiceParticipantsHidden: Record<string, boolean>;
}
export class ChannelRTCStore extends FluxStore {
getActivityParticipants(channelId: string): ActivityParticipant[];
getAllChatOpen(): Record<string, boolean>;
getChatOpen(channelId: string): boolean;
getFilteredParticipants(channelId: string): Participant[];
getGuildRingingUsers(channelId: string): Set<string>;
getLayout(channelId: string, context?: RTCLayoutContext): RTCLayout;
getMode(channelId: string): RTCMode;
getParticipant(channelId: string, participantId: string): Participant | null;
getParticipants(channelId: string): Participant[];
getParticipantsListOpen(channelId: string): boolean;
getParticipantsOpen(channelId: string): boolean;
getParticipantsVersion(channelId: string): number;
getSelectedParticipant(channelId: string): Participant | null;
getSelectedParticipantId(channelId: string): string | null;
getSelectedParticipantStats(channelId: string): SelectedParticipantStats;
getSpeakingParticipants(channelId: string): UserParticipant[];
getStageStreamSize(channelId: string): StreamResolution | undefined;
getStageVideoLimitBoostUpsellDismissed(channelId: string): boolean | undefined;
getState(): ChannelRTCState;
getStreamParticipants(channelId: string): StreamParticipant[];
getUserParticipantCount(channelId: string): number;
getVideoParticipants(channelId: string): UserParticipant[];
getVoiceParticipantsHidden(channelId: string): boolean;
isFullscreenInContext(): boolean;
isParticipantPoppedOut(channelId: string, participantId: string): boolean;
}

View file

@ -10,6 +10,7 @@ export class ChannelStore extends FluxStore {
getMutableGuildChannelsForGuild(guildId: string): Record<string, Channel>;
getAllThreadsForGuild(guildId: string): Channel[];
getAllThreadsForParent(channelId: string): Channel[];
getSortedLinkedChannelsForGuild(guildId: string): Channel[];
getDMFromUserId(userId: string): string;
getDMChannelFromUserId(userId: string): Channel | undefined;
@ -21,4 +22,10 @@ export class ChannelStore extends FluxStore {
getGuildChannelsVersion(guildId: string): number;
getPrivateChannelsVersion(): number;
getInitialOverlayState(): Record<string, Channel>;
getDebugInfo(): {
loadedGuildIds: string[];
pendingGuildLoads: string[];
guildSizes: string[];
};
}

View file

@ -1,14 +1,5 @@
import { FluxStore } from "..";
export enum DraftType {
ChannelMessage = 0,
ThreadSettings = 1,
FirstThreadMessage = 2,
ApplicationLauncherCommand = 3,
Poll = 4,
SlashCommand = 5,
ForwardContextMessage = 6
}
import { DraftType } from "../../enums";
export interface Draft {
timestamp: number;

View file

@ -1,57 +1,467 @@
import { Channel, CustomEmoji, Emoji, FluxStore } from "..";
import { EmojiIntention, LoadState } from "../../enums";
export class EmojiStore extends FluxStore {
getCustomEmojiById(id?: string | null): CustomEmoji | undefined;
getUsableCustomEmojiById(id?: string | null): CustomEmoji | undefined;
getGuilds(): Record<string, {
id: string;
get emojis(): CustomEmoji[];
get rawEmojis(): CustomEmoji[];
get usableEmojis(): CustomEmoji[];
get emoticons(): any[];
getEmoji(id: string): CustomEmoji | undefined;
isUsable(emoji: CustomEmoji): boolean;
}>;
getGuildEmoji(guildId?: string | null): CustomEmoji[];
getNewlyAddedEmoji(guildId?: string | null): CustomEmoji[];
getTopEmoji(guildId?: string | null): CustomEmoji[];
getTopEmojisMetadata(guildId?: string | null): {
emojiIds: string[];
topEmojisTTL: number;
};
hasPendingUsage(): boolean;
hasUsableEmojiInAnyGuild(): boolean;
searchWithoutFetchingLatest(data: any): any;
getSearchResultsOrder(...args: any[]): any;
getState(): {
pendingUsages: { key: string, timestamp: number; }[];
};
searchWithoutFetchingLatest(data: {
channel: Channel;
query: string;
count?: number;
intention: number;
includeExternalGuilds?: boolean;
matchComparator?(name: string): boolean;
}): Record<"locked" | "unlocked", Emoji[]>;
/** Emoji picker category names. */
export type EmojiCategory =
| "top guild emoji"
| "favorites"
| "recent"
| "custom"
| "people"
| "nature"
| "food"
| "activity"
| "travel"
| "objects"
| "symbols"
| "flags";
getDisambiguatedEmojiContext(): {
backfillTopEmojis: Record<any, any>;
customEmojis: Record<string, CustomEmoji>;
emojisById: Record<string, CustomEmoji>;
emojisByName: Record<string, CustomEmoji>;
emoticonRegex: RegExp | null;
emoticonsByName: Record<string, any>;
escapedEmoticonNames: string;
favoriteNamesAndIds?: any;
favorites?: any;
frequentlyUsed?: any;
groupedCustomEmojis: Record<string, CustomEmoji[]>;
guildId?: string;
isFavoriteEmojiWithoutFetchingLatest(e: Emoji): boolean;
newlyAddedEmoji: Record<string, CustomEmoji[]>;
topEmojis?: any;
unicodeAliases: Record<string, string>;
get favoriteEmojisWithoutFetchingLatest(): Emoji[];
};
/**
* Tracks usage statistics for a single emoji to compute frecency scores.
*/
export interface EmojiUsageRecord {
/** Total number of times this emoji has been used. */
totalUses: number;
/** Array of recent usage timestamps in milliseconds. */
recentUses: number[];
/** Computed frecency score combining frequency and recency, -1 when dirty. */
frecency: number;
/** Raw score before frecency computation. */
score: number;
}
/**
* Options for tracking emoji usage.
*/
export interface TrackOptions {
/** Timestamp of the usage in milliseconds. */
timestamp?: number;
/** Number of uses since last track call. */
usesSinceLastTrack?: number;
}
/**
* Frecency tracker for emoji usage, combines frequency and recency to rank emojis.
* Used by both regular emoji picker and reaction emoji picker.
*/
export interface EmojiFrecency {
/** True when data has been modified and needs recomputation. */
dirty: boolean;
/** Cached array of frequently used emojis after computation. */
_frequently: Emoji[];
/** Maximum number of frequently used items to track (default 42). */
numFrequentlyItems: number;
/** Maximum number of recent usage samples to keep per emoji (default 10). */
maxSamples: number;
/** Computes bonus score for frecency calculation (returns 100). */
computeBonus: () => number;
/**
* Computes weight multiplier based on recency index.
* Returns 100 for index <= 3, 70 for <= 15, 50 for <= 30, 30 for <= 45, 10 for <= 80.
*/
computeWeight: (index: number) => number;
/**
* Computes frecency score for an emoji.
* @param totalUses Total number of times emoji was used.
* @param score Raw score value.
* @param config Configuration for frecency calculation.
*/
computeFrecency: (totalUses: number, score: number, config: {
/** Number of recent uses to consider. */
numOfRecentUses?: number;
/** Maximum total uses to cap at. */
maxTotalUse?: number;
}) => number;
/** Whether to calculate max total use dynamically. */
calculateMaxTotalUse: boolean;
/**
* Looks up an emoji by name or id.
* @param name Emoji name or id to look up.
* @returns The emoji if found.
*/
lookupKey: (name: string) => Emoji | undefined;
/** Usage history keyed by emoji name (for unicode) or id (for custom). */
usageHistory: Record<string, EmojiUsageRecord>;
/** Callback invoked after frecency computation completes. */
afterCompute: () => void;
/**
* Overwrites the usage history with new data.
* @param history New usage history to set.
* @param pendingUsages Pending usages to track after overwriting.
*/
overwriteHistory(history: Record<string, EmojiUsageRecord> | null, pendingUsages?: PendingUsage[]): void;
/** Marks the frecency data as dirty, requiring recomputation. */
markDirty(): void;
/** Returns whether the frecency data needs recomputation. */
isDirty(): boolean;
/**
* Tracks usage of an emoji.
* @param key Emoji name or id.
* @param options Track options including timestamp.
*/
track(key: string, options?: TrackOptions): void;
/**
* Gets the usage record for an emoji, computing if dirty.
* @param key Emoji name or id.
* @returns The usage record or null if not found.
*/
getEntry(key: string): EmojiUsageRecord | null;
/**
* Gets the score for an emoji.
* @param key Emoji name or id.
* @returns The score or null if not found.
*/
getScore(key: string): number | null;
/**
* Gets the frecency for an emoji.
* @param key Emoji name or id.
* @returns The frecency or null if not found.
*/
getFrecency(key: string): number | null;
/** Recomputes frecency scores for all emojis. */
compute(): void;
/** Gets the frequently used emojis, computing if necessary. */
get frequently(): Emoji[];
}
/**
* Container for a guild's emoji collection with usability checks.
*/
export interface GuildEmojis {
/** Guild id this emoji collection belongs to. */
id: string;
/** User id for permission checks. */
_userId: string;
/** Internal emoji array. */
_emojis: CustomEmoji[];
/** Fast lookup map of emoji id to emoji. */
_emojiMap: Record<string, CustomEmoji>;
/** Internal emoticons array. */
_emoticons: Emoticon[];
/** Internal usable emojis cache. */
_usableEmojis: CustomEmoji[];
/** Whether user can see server subscription IAP. */
_canSeeServerSubIAP: boolean;
/** All custom emojis in this guild. */
get emojis(): CustomEmoji[];
/** Custom emojis the current user can use in this guild. */
get usableEmojis(): CustomEmoji[];
/** Text emoticons configured for this guild. */
get emoticons(): Emoticon[];
/**
* Gets an emoji by id from this guild.
* @param id Emoji id to look up.
*/
getEmoji(id: string): CustomEmoji | undefined;
/**
* Gets a usable emoji by id from this guild.
* @param id Emoji id to look up.
*/
getUsableEmoji(id: string): CustomEmoji | undefined;
/**
* Checks if an emoji is usable by the current user.
* @param emoji Emoji to check.
*/
isUsable(emoji: CustomEmoji): boolean;
/** Returns array of all emoji ids in this guild. */
emojiIds(): string[];
}
/**
* Text emoticon that can be converted to emoji.
*/
export interface Emoticon {
/** Names/aliases for this emoticon. */
names: string[];
/** The text representation (e.g. ":)" or ":D"). */
surrogates: string;
/** Whether this emoticon should use sprite sheet rendering. */
useSpriteSheet: boolean;
}
/**
* Pending emoji usage waiting to be recorded.
*/
export interface PendingUsage {
/** Emoji key (name for unicode, id for custom). */
key: string;
/** Timestamp in milliseconds when usage occurred. */
timestamp: number;
}
/**
* Serializable state for EmojiStore persistence.
*/
export interface EmojiStoreState {
/** Pending emoji usages not yet committed. */
pendingUsages: PendingUsage[];
/** Pending reaction emoji usages not yet committed. */
emojiReactionPendingUsages: PendingUsage[];
/** Guild ids with expanded emoji sections in picker. */
expandedSectionsByGuildIds: Set<string>;
}
/**
* Context for emoji disambiguation, caching resolved emoji data for a guild context.
* Provides fast lookup of emojis without triggering data fetches.
*/
export interface DisambiguatedEmojiContext {
/** User's favorite emojis or null if not loaded. */
favorites: Emoji[] | null;
/** Set of favorite emoji names and ids for fast lookup, or null if not loaded. */
favoriteNamesAndIds: Set<string> | null;
/** Top emojis for the current guild or null if not loaded. */
topEmojis: Emoji[] | null;
/** Current guild id context or null for DMs. */
guildId: string | null;
/** Regex-escaped emoticon names for matching. */
escapedEmoticonNames: string;
/** All emojis with disambiguation applied (unique names). */
disambiguatedEmoji: Emoji[];
/** Compiled regex for matching emoticons or null if none. */
emoticonRegex: RegExp | null;
/** Frequently used emojis or null if not loaded. */
frequentlyUsed: Emoji[] | null;
/** Frequently used reaction emojis or null if not loaded. */
frequentlyUsedReactionEmojis: Emoji[] | null;
/** Set of frequently used reaction emoji names and ids, or null if not loaded. */
frequentlyUsedReactionNamesAndIds: Set<string> | null;
/** Unicode emoji aliases keyed by alias name, maps to primary name. */
unicodeAliases: Record<string, string>;
/** Custom emojis keyed by emoji id. */
customEmojis: Record<string, CustomEmoji>;
/** Custom emojis grouped by guild id. */
groupedCustomEmojis: Record<string, CustomEmoji[]>;
/** Emoticons keyed by name for fast lookup. */
emoticonsByName: Record<string, Emoticon>;
/** All emojis keyed by name for fast lookup. */
emojisByName: Record<string, Emoji>;
/** Custom emojis keyed by id for fast lookup. */
emojisById: Record<string, CustomEmoji>;
/** Newly added emojis grouped by guild id. */
newlyAddedEmoji: Record<string, CustomEmoji[]>;
/**
* Checks if an emoji is a favorite without triggering a fetch.
* @param emoji Emoji to check.
*/
isFavoriteEmojiWithoutFetchingLatest(emoji: Emoji): boolean;
/** Gets favorite emojis without triggering a fetch. */
get favoriteEmojisWithoutFetchingLatest(): Emoji[];
/** Gets all disambiguated emojis. */
getDisambiguatedEmoji(): Emoji[];
/** Gets all custom emojis keyed by name. */
getCustomEmoji(): Record<string, CustomEmoji>;
/** Gets custom emojis grouped by guild id. */
getGroupedCustomEmoji(): Record<string, CustomEmoji[]>;
/**
* Gets an emoji by name.
* @param name Emoji name to look up.
*/
getByName(name: string): Emoji | undefined;
/**
* Gets an emoticon by name.
* @param name Emoticon name to look up.
*/
getEmoticonByName(name: string): Emoticon | undefined;
/**
* Gets an emoji by id.
* @param id Emoji id to look up.
*/
getById(id: string): Emoji | undefined;
/**
* Gets the regex for matching custom emoticons.
* @returns RegExp or null if no emoticons.
*/
getCustomEmoticonRegex(): RegExp | null;
/** Gets frequently used emojis without triggering a fetch. */
getFrequentlyUsedEmojisWithoutFetchingLatest(): Emoji[];
/** Rebuilds the frequently used reaction emojis cache and returns it. */
rebuildFrequentlyUsedReactionsEmojisWithoutFetchingLatest(): {
frequentlyUsedReactionEmojis: Emoji[];
frequentlyUsedReactionNamesAndIds: Set<string>;
};
/** Gets frequently used reaction emojis without triggering a fetch. */
getFrequentlyUsedReactionEmojisWithoutFetchingLatest(): Emoji[];
/**
* Checks if an emoji is frequently used for reactions.
* @param emoji Emoji to check.
*/
isFrequentlyUsedReactionEmojiWithoutFetchingLatest(emoji: Emoji): boolean;
/** Rebuilds the favorite emojis cache and returns it. */
rebuildFavoriteEmojisWithoutFetchingLatest(): {
favorites: Emoji[];
favoriteNamesAndIds: Set<string>;
};
/**
* Gets emojis in priority order (favorites, frequent, top) without fetching.
* @returns Array of emojis in priority order.
*/
getEmojiInPriorityOrderWithoutFetchingLatest(): Emoji[];
/**
* Gets top emojis for a guild without triggering a fetch.
* @param guildId Guild id to get top emojis for.
*/
getTopEmojiWithoutFetchingLatest(guildId: string): Emoji[];
/**
* Gets newly added emojis for a specific guild.
* @param guildId Guild id.
*/
getNewlyAddedEmojiForGuild(guildId: string): CustomEmoji[];
/** Gets escaped custom emoticon names for regex matching. */
getEscapedCustomEmoticonNames(): string;
/**
* Checks if a name matches an emoji name chain.
* @param name Name to match.
*/
nameMatchesChain(name: string): boolean;
}
/**
* Search options for emoji search.
*/
export interface EmojiSearchOptions {
/** Channel context for permission checks. */
channel: Channel;
/** Search query string. */
query: string;
/** Maximum number of results to return. */
count?: number;
/** Intention for using the emoji, affects availability filtering. */
intention: EmojiIntention;
/** Whether to include emojis from guilds the user is not in. */
includeExternalGuilds?: boolean;
/** Whether to only show unicode emojis in results. */
showOnlyUnicode?: boolean;
/**
* Custom comparator for matching emoji names.
* @param name Emoji name to compare.
* @returns True if the name matches.
*/
matchComparator?(name: string): boolean;
}
/**
* Search results split by availability.
*/
export interface EmojiSearchResults {
/** Emojis that are locked (require Nitro or permissions). */
locked: Emoji[];
/** Emojis that are available for use. */
unlocked: Emoji[];
}
/**
* Metadata about top emojis for a guild.
*/
export interface TopEmojisMetadata {
/** Array of top emoji ids. */
emojiIds: string[];
/** Time-to-live for this data in milliseconds. */
topEmojisTTL: number;
}
/**
* Flux store managing all emoji data including custom guild emojis,
* unicode emojis, favorites, frecency, and search functionality.
*/
export class EmojiStore extends FluxStore {
/** Array of emoji category names for the picker. */
get categories(): EmojiCategory[];
/**
* Current skin tone modifier surrogate for emoji diversity.
* Empty string for default yellow, or skin tone modifier (🏻🏼🏽🏾🏿).
*/
get diversitySurrogate(): string;
/** Frecency tracker for emoji picker usage. */
get emojiFrecencyWithoutFetchingLatest(): EmojiFrecency;
/** Frecency tracker for reaction emoji usage. */
get emojiReactionFrecencyWithoutFetchingLatest(): EmojiFrecency;
/** Guild ids with expanded emoji sections in picker. */
get expandedSectionsByGuildIds(): Set<string>;
/** Current load state of the emoji store. */
get loadState(): LoadState;
/**
* Gets a custom emoji by its id.
* @param id Emoji id to look up.
* @returns The custom emoji if found.
*/
getCustomEmojiById(id?: string | null): CustomEmoji | undefined;
/**
* Gets a usable custom emoji by its id.
* @param id Emoji id to look up.
* @returns The custom emoji if found and usable by current user.
*/
getUsableCustomEmojiById(id?: string | null): CustomEmoji | undefined;
/**
* Gets all guild emoji collections keyed by guild id.
* @returns Record of guild id to GuildEmojis.
*/
getGuilds(): Record<string, GuildEmojis>;
/**
* Gets all custom emojis for a guild.
* @param guildId Guild id to get emojis for, or null for all guilds.
* @returns Array of custom emojis.
*/
getGuildEmoji(guildId?: string | null): CustomEmoji[];
/**
* Gets usable custom emojis for a guild.
* @param guildId Guild id to get emojis for.
* @returns Array of usable custom emojis.
*/
getUsableGuildEmoji(guildId?: string | null): CustomEmoji[];
/**
* Gets newly added emojis for a guild.
* @param guildId Guild id to get emojis for.
* @returns Array of newly added custom emojis.
*/
getNewlyAddedEmoji(guildId?: string | null): CustomEmoji[];
/**
* Gets top emojis for a guild based on usage.
* @param guildId Guild id to get emojis for.
* @returns Array of top custom emojis.
*/
getTopEmoji(guildId?: string | null): CustomEmoji[];
/**
* Gets metadata about top emojis for a guild.
* @param guildId Guild id to get metadata for.
* @returns Metadata including emoji ids and TTL, or undefined if not cached.
*/
getTopEmojisMetadata(guildId?: string | null): TopEmojisMetadata | undefined;
/**
* Checks if user has any favorite emojis in a guild context.
* @param guildId Guild id to check.
* @returns True if user has favorites.
*/
hasFavoriteEmojis(guildId?: string | null): boolean;
/**
* Checks if there are pending emoji usages to be recorded.
* @returns True if there are pending usages.
*/
hasPendingUsage(): boolean;
/**
* Checks if user has any usable custom emojis in any guild.
* @returns True if user has usable emojis.
*/
hasUsableEmojiInAnyGuild(): boolean;
/** Internal method for ordering search results. */
getSearchResultsOrder(...args: any[]): any;
/**
* Gets the serializable state for persistence.
* @returns Current store state.
*/
getState(): EmojiStoreState;
/**
* Searches for emojis without triggering data fetches.
* @param options Search options including query and filters.
* @returns Search results split by locked/unlocked.
*/
searchWithoutFetchingLatest(options: EmojiSearchOptions): EmojiSearchResults;
/**
* Gets the disambiguated emoji context for a guild.
* @param guildId Guild id to get context for, or null/undefined for global context.
*/
getDisambiguatedEmojiContext(guildId?: string | null): DisambiguatedEmojiContext;
}

View file

@ -1,6 +1,7 @@
import { FluxDispatcher, FluxEvents } from "..";
type Callback = () => void;
type SyncCallback = () => boolean | void;
/*
For some reason, this causes type errors when you try to destructure it:
@ -14,31 +15,75 @@ type Callback = () => void;
export type FluxEvent = any;
export type ActionHandler = (event: FluxEvent) => void;
/** keyed by FluxEvents action type */
export type ActionHandlers = Partial<Record<FluxEvents, ActionHandler>>;
/**
* Base class for all Discord Flux stores.
* Provides change notification, action handling, and store synchronization.
*/
export class FluxStore {
constructor(dispatcher: FluxDispatcher, actionHandlers?: ActionHandlers);
/**
* @param dispatcher the FluxDispatcher instance to register with
* @param actionHandlers handlers for Flux actions, keyed by action type
* @param band priority band for action handling (default 2), lower runs first
*/
constructor(dispatcher: FluxDispatcher, actionHandlers?: ActionHandlers, band?: number);
/** returns displayName if set, otherwise constructor.name */
getName(): string;
/** adds listener to _changeCallbacks, invoked before react listeners and triggers syncWith processing */
addChangeListener(callback: Callback): void;
/** Listener will be removed once the callback returns false. */
/**
* adds a listener that auto-removes when callback returns false.
* @param callback returning false removes the listener
* @param preemptive if true (default), calls callback immediately and skips adding if it returns false
*/
addConditionalChangeListener(callback: () => boolean, preemptive?: boolean): void;
/** adds listener to _reactChangeCallbacks, invoked after all regular change listeners complete */
addReactChangeListener(callback: Callback): void;
removeChangeListener(callback: Callback): void;
removeReactChangeListener(callback: Callback): void;
/** called by dispatcher after action handlers run, marks changed if listeners exist and may resume paused dispatch */
doEmitChanges(event: FluxEvent): void;
/** marks store as changed for batched listener notification */
emitChange(): void;
/** unique token identifying this store in the dispatcher */
getDispatchToken(): string;
/** override to set up initial state, called once by initializeIfNeeded */
initialize(): void;
/** calls initialize() if not already initialized, adds performance mark if init takes >5ms */
initializeIfNeeded(): void;
/** this is a setter */
mustEmitChanges(actionHandler: ActionHandler | undefined): void;
registerActionHandlers(actionHandlers: ActionHandlers): void;
syncWith(stores: FluxStore[], callback: Callback, timeout?: number): void;
/**
* sets callback to determine if changes must emit during paused dispatch.
* @param callback if omitted, defaults to () => true (always emit)
*/
mustEmitChanges(callback?: ActionHandler): void;
/**
* registers additional action handlers after construction.
* @param actionHandlers handlers keyed by action type
* @param band priority band, lower runs first
*/
registerActionHandlers(actionHandlers: ActionHandlers, band?: number): void;
/**
* syncs this store with other stores, re-emitting when they change.
* without timeout: synchronous, callback runs during emitNonReactOnce.
* with timeout: debounced, adds regular change listener to each source store.
* @param stores stores to sync with
* @param callback returning false skips emitChange on this store
* @param timeout if provided, debounces the sync callback
*/
syncWith(stores: FluxStore[], callback: SyncCallback, timeout?: number): void;
/** adds dispatcher dependencies so this store's handlers run after the specified stores */
waitFor(...stores: FluxStore[]): void;
/** initializes all registered stores, called once at app startup */
static initialize(): void;
/** clears all registered stores and destroys the change listener system */
static destroy(): void;
/** returns all registered FluxStore instances */
static getAll(): FluxStore[];
}

View file

@ -0,0 +1,67 @@
import { Activity, FluxStore, Guild, User } from "..";
import { GiftIntentType, RelationshipType } from "../../enums";
export type FriendsSection = "ADD_FRIEND" | "ALL" | "ONLINE" | "PENDING" | "PENDING_IGNORED" | "SPAM" | "SUGGESTIONS";
export type StatusType = "online" | "offline" | "idle" | "dnd" | "invisible" | "streaming" | "unknown";
export interface ApplicationStream {
channelId: string;
guildId: string | null;
ownerId: string;
streamType: string;
}
export interface FriendsRow {
key: string;
userId: string;
/**
* 99 means contact based friend suggestions from FriendSuggestionStore,
* shown in SUGGESTIONS tab. different from RelationshipType.SUGGESTION
* which is for implicit suggestions in RelationshipStore
*/
type: RelationshipType | 99;
status: StatusType;
isMobile: boolean;
activities: Activity[];
applicationStream: ApplicationStream | null;
user: User | null;
usernameLower: string | null;
mutualGuildsLength: number;
mutualGuilds: Guild[];
nickname: string | null;
spam: boolean;
giftIntentType: GiftIntentType | undefined;
ignoredUser: boolean;
applicationId: string | undefined;
isGameRelationship: boolean;
comparator: [RelationshipType | 99, string | null];
}
export interface RelationshipCounts {
[RelationshipType.FRIEND]: number;
[RelationshipType.INCOMING_REQUEST]: number;
[RelationshipType.OUTGOING_REQUEST]: number;
[RelationshipType.BLOCKED]: number;
/** contact based friend suggestions from FriendSuggestionStore */
99: number;
}
export interface FriendsRows {
_rows: FriendsRow[];
reset(): FriendsRows;
clone(): FriendsRows;
update(updater: (userId: string) => Partial<FriendsRow>): boolean;
filter(section: FriendsSection, searchQuery?: string | null): FriendsRow[];
getRelationshipCounts(): RelationshipCounts;
}
export interface FriendsState {
fetching: boolean;
section: FriendsSection;
rows: FriendsRows;
}
export class FriendsStore extends FluxStore {
getState(): FriendsState;
}

View file

@ -0,0 +1,46 @@
import { Channel, FluxStore, ThreadJoined } from "..";
import { ChannelType } from "../../enums";
export interface ChannelWithComparator {
channel: Channel;
comparator: number;
}
export interface GuildChannels {
[ChannelType.GUILD_CATEGORY]: ChannelWithComparator[];
id: string;
SELECTABLE: ChannelWithComparator[] | ThreadJoined[];
VOCAL: ChannelWithComparator[];
count: number;
}
export interface ChannelNameDisambiguation {
id: string;
name: string;
}
export class GuildChannelStore extends FluxStore {
getAllGuilds(): Record<string, GuildChannels>;
getChannels(guildId: string): GuildChannels;
getDefaultChannel(guildId: string): Channel | null;
getDirectoryChannelIds(guildId: string): string[];
getFirstChannel(
guildId: string,
predicate: (item: ChannelWithComparator) => boolean,
includeVocal?: boolean
): Channel | null;
getFirstChannelOfType(
guildId: string,
predicate: (item: ChannelWithComparator) => boolean,
type: "SELECTABLE" | "VOCAL" | ChannelType.GUILD_CATEGORY
): Channel | null;
getSFWDefaultChannel(guildId: string): Channel | null;
getSelectableChannelIds(guildId: string): string[];
getSelectableChannels(guildId: string): ChannelWithComparator[];
getTextChannelNameDisambiguations(guildId: string): Record<string, ChannelNameDisambiguation>;
getVocalChannelIds(guildId: string): string[];
hasCategories(guildId: string): boolean;
hasChannels(guildId: string): boolean;
hasElevatedPermissions(guildId: string): boolean;
hasSelectableChannel(guildId: string, channelId: string): boolean;
}

View file

@ -0,0 +1,7 @@
import { FluxStore } from "..";
export class GuildMemberCountStore extends FluxStore {
getMemberCounts(): Record<string, number>;
getMemberCount(guildId: string): number;
getOnlineCount(guildId: string): number;
}

View file

@ -1,5 +1,10 @@
import { FluxStore, GuildMember } from "..";
export interface PendingRoleUpdates {
added: string[];
removed: string[];
}
export class GuildMemberStore extends FluxStore {
/** @returns Format: [guildId-userId: Timestamp (string)] */
getCommunicationDisabledUserMap(): Record<string, string>;
@ -11,6 +16,10 @@ export class GuildMemberStore extends FluxStore {
getTrueMember(guildId: string, userId: string): GuildMember | null;
getMemberIds(guildId: string): string[];
getMembers(guildId: string): GuildMember[];
getMemberVersion(): number;
getMemberRoleWithPendingUpdates(guildId: string, userId: string): string[];
getPendingRoleUpdates(guildId: string): PendingRoleUpdates;
memberOf(userId: string): string[];
getCachedSelfMember(guildId: string): GuildMember | null;
getSelfMember(guildId: string): GuildMember | null;
@ -20,7 +29,6 @@ export class GuildMemberStore extends FluxStore {
getNicknameGuildsMapping(userId: string): Record<string, string[]>;
getNicknames(userId: string): string[];
isMember(guildId: string, userId: string): boolean;
isMember(guildId: string, userId: string): boolean;
isGuestOrLurker(guildId: string, userId: string): boolean;
isCurrentUserGuest(guildId: string): boolean;

View file

@ -1,8 +1,13 @@
import { FluxStore, Role } from "..";
import { FluxStore, Guild, Role } from "..";
// TODO: add the rest of the methods for GuildRoleStore
export class GuildRoleStore extends FluxStore {
getRole(guildId: string, roleId: string): Role;
getSortedRoles(guildId: string): Role[];
getRolesSnapshot(guildId: string): Record<string, Role>;
getSortedRoles(guildId: string): Role[];
getEveryoneRole(guild: Guild): Role;
getManyRoles(guildId: string, roleIds: string[]): Role[];
getNumRoles(guildId: string): number;
getRole(guildId: string, roleId: string): Role;
getUnsafeMutableRoles(guildId: string): Record<string, Role>;
serializeAllGuildRoles(): Array<{ partitionKey: string; values: Record<string, Role>; }>;
}

View file

@ -0,0 +1,67 @@
import { FluxStore } from "..";
import { GuildScheduledEventEntityType, GuildScheduledEventPrivacyLevel, GuildScheduledEventStatus } from "../../enums";
export interface GuildScheduledEventEntityMetadata {
location?: string;
}
export interface GuildScheduledEventRecurrenceRule {
start: string;
end: string | null;
frequency: number;
interval: number;
byWeekday: number[] | null;
byNWeekday: { n: number; day: number; }[] | null;
byMonth: number[] | null;
byMonthDay: number[] | null;
byYearDay: number[] | null;
count: number | null;
}
export interface GuildScheduledEvent {
id: string;
guild_id: string;
channel_id: string | null;
creator_id: string | null;
name: string;
description: string | null;
image: string | null;
scheduled_start_time: string;
scheduled_end_time: string | null;
privacy_level: GuildScheduledEventPrivacyLevel;
status: GuildScheduledEventStatus;
entity_type: GuildScheduledEventEntityType;
entity_id: string | null;
entity_metadata: GuildScheduledEventEntityMetadata | null;
sku_ids: string[];
recurrence_rule: GuildScheduledEventRecurrenceRule | null;
// TODO: type
guild_scheduled_event_exceptions: any[];
auto_start: boolean;
}
export interface GuildScheduledEventRsvp {
guildScheduledEventId: string;
userId: string;
interested: boolean;
}
export interface GuildScheduledEventUsers {
// TODO: finish typing
[userId: string]: any;
}
export class GuildScheduledEventStore extends FluxStore {
getGuildScheduledEvent(eventId: string): GuildScheduledEvent | null;
getGuildScheduledEventsForGuild(guildId: string): GuildScheduledEvent[];
getGuildScheduledEventsByIndex(status: GuildScheduledEventStatus): GuildScheduledEvent[];
getGuildEventCountByIndex(status: GuildScheduledEventStatus): number;
getRsvpVersion(): number;
getRsvp(eventId: string, recurrenceId: string | null, userId: string | null): GuildScheduledEventRsvp | null;
isInterestedInEventRecurrence(eventId: string, recurrenceId: string | null): boolean;
getUserCount(eventId: string, recurrenceId: string | null): number;
hasUserCount(eventId: string, recurrenceId: string | null): boolean;
isActive(eventId: string): boolean;
getActiveEventByChannel(channelId: string): GuildScheduledEvent | null;
getUsersForGuildEvent(eventId: string, recurrenceId: string | null): GuildScheduledEventUsers;
}

View file

@ -4,5 +4,6 @@ export class GuildStore extends FluxStore {
getGuild(guildId: string): Guild;
getGuildCount(): number;
getGuilds(): Record<string, Guild>;
getGuildsArray(): Guild[];
getGuildIds(): string[];
}

View file

@ -0,0 +1,17 @@
import { FluxStore } from "..";
import { Invite } from "./InviteStore";
export interface FriendInvite extends Invite {
max_age: number;
max_uses: number;
uses: number;
created_at: string;
revoked?: boolean;
}
export class InstantInviteStore extends FluxStore {
getInvite(channelId: string): Invite;
getFriendInvite(): FriendInvite | null;
getFriendInvitesFetching(): boolean;
canRevokeFriendInvite(): boolean;
}

View file

@ -0,0 +1,27 @@
import { Channel, FluxStore, Guild, User } from "..";
export interface Invite {
code: string;
guild: Guild | null;
channel: Channel | null;
inviter: User | null;
approximate_member_count?: number;
approximate_presence_count?: number;
expires_at?: string | null;
flags?: number;
target_type?: number;
target_user?: User;
// TODO: type these
target_application?: any;
stage_instance?: any;
guild_scheduled_event?: any;
}
export class InviteStore extends FluxStore {
getInvite(code: string): Invite;
// TODO: finish typing
getInviteError(code: string): any | undefined;
getInvites(): Record<string, Invite>;
getInviteKeyForGuildId(guildId: string): string | undefined;
getFriendMemberIds(code: string): string[] | undefined;
}

View file

@ -0,0 +1,6 @@
import { FluxStore } from "..";
export class LocaleStore extends FluxStore {
get locale(): string;
get systemLocale(): string;
}

File diff suppressed because it is too large Load diff

View file

@ -1,13 +1,55 @@
import { MessageJSON, FluxStore, Message } from "..";
import { FluxStore, Message } from "..";
export type JumpType = "ANIMATED" | "INSTANT";
export interface MessageCache {
_messages: Message[];
_map: Record<string, Message>;
_wasAtEdge: boolean;
_isCacheBefore: boolean;
}
export interface ChannelMessages {
channelId: string;
ready: boolean;
cached: boolean;
jumpType: JumpType;
jumpTargetId: string | null;
jumpTargetOffset: number;
jumpSequenceId: number;
jumped: boolean;
jumpedToPresent: boolean;
jumpFlash: boolean;
jumpReturnTargetId: string | null;
focusTargetId: string | null;
focusSequenceId: number;
initialScrollSequenceId: number;
hasMoreBefore: boolean;
hasMoreAfter: boolean;
loadingMore: boolean;
revealedMessageId: string | null;
hasFetched: boolean;
error: boolean;
_array: Message[];
_before: MessageCache;
_after: MessageCache;
_map: Record<string, Message>;
}
export class MessageStore extends FluxStore {
focusedMessageId(channelId: string): string | undefined;
getLastChatCommandMessage(channelId: string): Message | undefined;
getLastEditableMessage(channelId: string): Message | undefined;
getLastMessage(channelId: string): Message | undefined;
getLastNonCurrentUserMessage(channelId: string): Message | undefined;
getMessage(channelId: string, messageId: string): Message;
/** @returns This return object is fucking huge; I'll type it later. */
getMessages(channelId: string): unknown;
getRawMessages(channelId: string): Record<string | number, MessageJSON>;
/** @see {@link ChannelMessages} */
getMessages(channelId: string): ChannelMessages;
hasCurrentUserSentMessage(channelId: string): boolean;
hasCurrentUserSentMessageSinceAppStart(channelId: string): boolean;
hasPresent(channelId: string): boolean;
isLoadingMessages(channelId: string): boolean;
isReady(channelId: string): boolean;
jumpedMessageId(channelId: string): string | undefined;
whenReady(channelId: string, callback: () => void): void;
}

View file

@ -0,0 +1,26 @@
import { FluxStore } from "..";
export type DesktopNotificationType = "ALL" | "ONLY_MENTIONS" | "NEVER";
export type TTSNotificationType = "ALL" | "ONLY_MENTIONS" | "NEVER";
export interface NotificationSettingsState {
desktopType: DesktopNotificationType;
disableAllSounds: boolean;
disabledSounds: string[];
ttsType: TTSNotificationType;
disableUnreadBadge: boolean;
taskbarFlash: boolean;
notifyMessagesInSelectedChannel: boolean;
}
export class NotificationSettingsStore extends FluxStore {
get taskbarFlash(): boolean;
getUserAgnosticState(): NotificationSettingsState;
getDesktopType(): DesktopNotificationType;
getTTSType(): TTSNotificationType;
getDisabledSounds(): string[];
getDisableAllSounds(): boolean;
getDisableUnreadBadge(): boolean;
getNotifyMessagesInSelectedChannel(): boolean;
isSoundDisabled(sound: string): boolean;
}

View file

@ -0,0 +1,15 @@
import { FluxStore } from "..";
export interface OverridePremiumTypeState {
createdAtOverride: Date | undefined;
premiumTypeActual: number | null;
premiumTypeOverride: number | undefined;
}
export class OverridePremiumTypeStore extends FluxStore {
getState(): OverridePremiumTypeState;
getCreatedAtOverride(): Date | undefined;
getPremiumTypeActual(): number | null;
getPremiumTypeOverride(): number | undefined;
get premiumType(): number | undefined;
}

View file

@ -0,0 +1,57 @@
import { Channel, Guild, Role, FluxStore } from "..";
export interface GuildPermissionProps {
canManageGuild: boolean;
canManageChannels: boolean;
canManageRoles: boolean;
canManageBans: boolean;
canManageNicknames: boolean;
canManageGuildExpressions: boolean;
canViewAuditLog: boolean;
canViewAuditLogV2: boolean;
canManageWebhooks: boolean;
canViewGuildAnalytics: boolean;
canAccessMembersPage: boolean;
isGuildAdmin: boolean;
isOwner: boolean;
isOwnerWithRequiredMfaLevel: boolean;
guild: Guild;
}
export interface PartialChannelContext {
channelId: string;
}
export interface PartialGuildContext {
guildId: string;
}
export type PartialContext = PartialChannelContext | PartialGuildContext;
type PartialChannel = Channel | { id: string; };
type PartialGuild = Guild | { id: string; };
export class PermissionStore extends FluxStore {
// TODO: finish typing these
can(permission: bigint, channelOrGuild: PartialChannel | PartialGuild, guildId?: string, overwrites?: Record<string, any>, userId?: string): boolean;
canBasicChannel(permission: bigint, channel: PartialChannel, guildId?: string, overwrites?: Record<string, any>, userId?: string): boolean;
canWithPartialContext(permission: bigint, context: PartialContext): boolean;
canManageUser(permission: bigint, userOrUserId: string, guild: PartialGuild): boolean;
canAccessGuildSettings(guild: PartialGuild): boolean;
canAccessMemberSafetyPage(guild: PartialGuild): boolean;
canImpersonateRole(guild: PartialGuild, role: Role): boolean;
// TODO: finish typing
computePermissions(channel: PartialChannel, guildId?: string, overwrites?: Record<string, any>, userId?: string): bigint;
computeBasicPermissions(channel: PartialChannel): number;
getChannelPermissions(channel: PartialChannel): bigint;
getGuildPermissions(guild: PartialGuild): bigint;
getGuildPermissionProps(guild: PartialGuild): GuildPermissionProps;
getHighestRole(guild: PartialGuild): Role | null;
isRoleHigher(guild: PartialGuild, firstRole: Role | null, secondRole: Role | null): boolean;
getGuildVersion(guildId: string): number;
getChannelsVersion(): number;
}

View file

@ -8,17 +8,29 @@ export interface UserAndActivity {
export type DiscordPlatform = "desktop" | "mobile" | "web" | "embedded";
export interface PresenceStoreState {
presencesForGuilds: Record<string, Record<string, { status: OnlineStatus; activities: Activity[]; clientStatus: Partial<Record<DiscordPlatform, OnlineStatus>>; }>>;
statuses: Record<string, OnlineStatus>;
activities: Record<string, Activity[]>;
filteredActivities: Record<string, Activity[]>;
hiddenActivities: Record<string, Activity[]>;
// TODO: finish typing
activityMetadata: Record<string, any>;
clientStatuses: Record<string, Partial<Record<DiscordPlatform, OnlineStatus>>>;
}
export class PresenceStore extends FluxStore {
findActivity(userId: string, predicate: (activity: Activity) => boolean, guildId?: string): Activity | undefined;
getActivities(userId: string, guildId?: string): Activity[];
// TODO: finish typing
getActivityMetadata(userId: string): any;
getAllApplicationActivities(applicationId: string): UserAndActivity[];
getApplicationActivity(userId: string, applicationId: string, guildId?: string): Activity | null;
getClientStatus(userId: string): Record<DiscordPlatform, OnlineStatus>;
getHiddenActivities(): any;
getHiddenActivities(): Activity[];
/** literally just getActivities(...)[0] */
getPrimaryActivity(userId: string, guildId?: string): Activity | null;
getState(): any;
getState(): PresenceStoreState;
getStatus(userId: string, guildId?: string | null, defaultStatus?: OnlineStatus): OnlineStatus;
getUnfilteredActivities(userId: string, guildId?: string): Activity[];
getUserIds(): string[];

View file

@ -0,0 +1,82 @@
import { FluxStore } from "..";
export type RTCConnectionState =
| "DISCONNECTED"
| "AWAITING_ENDPOINT"
| "AUTHENTICATING"
| "CONNECTING"
| "RTC_DISCONNECTED"
| "RTC_CONNECTING"
| "RTC_CONNECTED"
| "NO_ROUTE"
| "ICE_CHECKING"
| "DTLS_CONNECTING";
export type RTCConnectionQuality = "unknown" | "bad" | "average" | "fine";
export interface LastRTCConnectionState {
duration: number | null;
mediaSessionId: string | null;
rtcConnectionId: string | null;
wasEverMultiParticipant: boolean;
wasEverRtcConnected: boolean;
// TODO: type
voiceStateAnalytics: any;
channelId: string;
}
export interface RTCConnectionPacketStats {
inbound: number;
outbound: number;
lost: number;
}
export interface VoiceStateStats {
max_voice_state_count: number;
}
export interface SecureFramesState {
state: string;
}
export interface SecureFramesRosterMapEntry {
pendingVerifyState: number;
verifiedState: number;
}
export class RTCConnectionStore extends FluxStore {
// TODO: type
getRTCConnection(): any | null;
getState(): RTCConnectionState;
isConnected(): boolean;
isDisconnected(): boolean;
getRemoteDisconnectVoiceChannelId(): string | null;
getLastSessionVoiceChannelId(): string | null;
setLastSessionVoiceChannelId(channelId: string | null): void;
getGuildId(): string | undefined;
getChannelId(): string | undefined;
getHostname(): string;
getQuality(): RTCConnectionQuality;
getPings(): number[];
getAveragePing(): number;
getLastPing(): number | undefined;
getOutboundLossRate(): number | undefined;
getMediaSessionId(): string | undefined;
getRTCConnectionId(): string | undefined;
getDuration(): number | undefined;
getLastRTCConnectionState(): LastRTCConnectionState | null;
getVoiceFilterSpeakingDurationMs(): number | undefined;
getPacketStats(): RTCConnectionPacketStats | undefined;
getVoiceStateStats(): VoiceStateStats | undefined;
// TODO: finish typing
getUserVoiceSettingsStats(userId: string): any | undefined;
getWasEverMultiParticipant(): boolean;
getWasEverRtcConnected(): boolean;
getUserIds(): string[] | undefined;
getJoinVoiceId(): string | null;
isUserConnected(userId: string): boolean | undefined;
getSecureFramesState(): SecureFramesState | undefined;
getSecureFramesRosterMapEntry(oderId: string): SecureFramesRosterMapEntry | undefined;
getLastNonZeroRemoteVideoSinkWantsTime(): number | null;
getWasMoved(): boolean;
}

View file

@ -0,0 +1,70 @@
import { Channel, FluxStore } from "..";
import { ReadStateType } from "../../enums";
export interface GuildChannelUnreadState {
mentionCount: number;
unread: boolean;
isMentionLowImportance: boolean;
}
export interface ReadStateSnapshot {
unread: boolean;
mentionCount: number;
guildUnread: boolean | null;
guildMentionCount: number | null;
takenAt: number;
}
export interface SerializedReadState {
channelId: string;
type: ReadStateType;
_guildId: string;
_persisted: boolean;
_lastMessageId: string;
_lastMessageTimestamp: number;
_ackMessageId: string;
_ackMessageTimestamp: number;
ackPinTimestamp: number;
lastPinTimestamp: number;
_mentionCount: number;
flags: number;
lastViewed: number;
}
export class ReadStateStore extends FluxStore {
ackMessageId(channelId: string, type?: ReadStateType): string | null;
getAllReadStates(includePrivate?: boolean): SerializedReadState[];
getChannelIdsForWindowId(windowId: string): string[];
getForDebugging(channelId: string): object | undefined;
getGuildChannelUnreadState(
channel: Channel,
isOptInEnabled: boolean,
guildHasActiveThreads: boolean,
isChannelMuted: boolean,
isGuildHome: boolean
): GuildChannelUnreadState;
getGuildUnreadsSentinel(guildId: string): number;
getIsMentionLowImportance(channelId: string, type?: ReadStateType): boolean;
getMentionChannelIds(): string[];
getMentionCount(channelId: string, type?: ReadStateType): number;
getNonChannelAckId(type: ReadStateType): string | null;
getNotifCenterReadState(channelId: string): object | undefined;
getOldestUnreadMessageId(channelId: string, type?: ReadStateType): string | null;
getOldestUnreadTimestamp(channelId: string, type?: ReadStateType): number;
getReadStatesByChannel(): Record<string, object>;
getSnapshot(channelId: string, maxAge: number): ReadStateSnapshot;
getTrackedAckMessageId(channelId: string, type?: ReadStateType): string | null;
getUnreadCount(channelId: string, type?: ReadStateType): number;
hasOpenedThread(channelId: string): boolean;
hasRecentlyVisitedAndRead(channelId: string): boolean;
hasTrackedUnread(channelId: string): boolean;
hasUnread(channelId: string, type?: ReadStateType): boolean;
hasUnreadOrMentions(channelId: string, type?: ReadStateType): boolean;
hasUnreadPins(channelId: string): boolean;
isEstimated(channelId: string, type?: ReadStateType): boolean;
isForumPostUnread(channelId: string): boolean;
isNewForumThread(threadId: string, parentChannelId: string, guildId: string): boolean;
lastMessageId(channelId: string, type?: ReadStateType): string | null;
lastMessageTimestamp(channelId: string, type?: ReadStateType): number;
lastPinTimestamp(channelId: string): number;
}

View file

@ -1,26 +1,42 @@
import { FluxStore } from "..";
import { RelationshipType } from "../../enums";
export class RelationshipStore extends FluxStore {
getBlockedIDs(): string[];
getBlockedOrIgnoredIDs(): string[];
getFriendCount(): number;
getFriendIDs(): string[];
getIgnoredIDs(): string[];
getBlockedIDs(): string[];
getMutableRelationships(): Map<string, RelationshipType>;
getNickname(userId: string): string;
getOriginApplicationId(userId: string): string | undefined;
getOutgoingCount(): number;
getPendingCount(): number;
getPendingIgnoredCount(): number;
getRelationshipCount(): number;
/** Related to friend nicknames. */
getNickname(userId: string): string;
/** @returns Enum value from constants.RelationshipTypes */
getRelationshipType(userId: string): number;
isFriend(userId: string): boolean;
getRelationshipType(userId: string): RelationshipType;
getSince(userId: string): string;
getSinces(): Record<string, string>;
getSpamCount(): number;
getVersion(): number;
isBlocked(userId: string): boolean;
isIgnored(userId: string): boolean;
isBlockedForMessage(userId: string): boolean;
/**
* @see {@link isBlocked}
* @see {@link isIgnored}
*/
isBlockedOrIgnored(userId: string): boolean;
getSince(userId: string): string;
isBlockedOrIgnoredForMessage(userId: string): boolean;
getMutableRelationships(): Map<string, number>;
isFriend(userId: string): boolean;
isIgnored(userId: string): boolean;
isIgnoredForMessage(userId: string): boolean;
isSpam(userId: string): boolean;
isStranger(userId: string): boolean;
isUnfilteredPendingIncoming(userId: string): boolean;
}

View file

@ -0,0 +1,60 @@
import { FluxStore } from "..";
export interface RunningGame {
id?: string;
name: string;
exePath: string;
cmdLine: string;
distributor: string;
lastFocused: number;
lastLaunched: number;
nativeProcessObserverId: number;
pid?: number;
hidden?: boolean;
isLauncher?: boolean;
elevated?: boolean;
sandboxed?: boolean;
}
export interface GameOverlayStatus {
enabledLegacy: boolean;
enabledOOP: boolean;
}
export interface SystemServiceStatus {
state: string;
}
export class RunningGameStore extends FluxStore {
canShowAdminWarning: boolean;
addExecutableTrackedByAnalytics(exe: string): void;
getCandidateGames(): RunningGame[];
getCurrentGameForAnalytics(): RunningGame | null;
getCurrentNonGameForAnalytics(): RunningGame | null;
getGameForName(name: string): RunningGame | null;
getGameForPID(pid: number): RunningGame | null;
getGameOrTransformedSubgameForPID(pid: number): RunningGame | null;
getGameOverlayStatus(game: RunningGame): GameOverlayStatus | null;
getGamesSeen(includeHidden?: boolean): RunningGame[];
getLauncherForPID(pid: number): RunningGame | null;
getObservedAppNameForWindow(windowHandle: number): string | null;
getOverlayEnabledForGame(game: RunningGame): boolean;
getOverlayOptionsForPID(pid: number): object | null;
getOverrideForGame(game: RunningGame): object | null;
getOverrides(): object[];
getRunningDiscordApplicationIds(): string[];
getRunningGames(): RunningGame[];
getRunningNonGames(): RunningGame[];
getRunningVerifiedApplicationIds(): string[];
getSeenGameByName(name: string): RunningGame | null;
getSystemServiceStatus(service: string): SystemServiceStatus;
getVisibleGame(): RunningGame | null;
getVisibleRunningGames(): RunningGame[];
isDetectionEnabled(type?: string): boolean;
isGamesSeenLoaded(): boolean;
isObservedAppRunning(app: string): boolean;
isSystemServiceInitialized(service: string): boolean;
shouldContinueWithoutElevatedProcessForPID(pid: number): boolean;
shouldElevateProcessForPID(pid: number): boolean;
}

View file

@ -1,14 +1,16 @@
import { FluxStore } from "..";
export interface ChannelFollowingDestination {
guildId?: string;
channelId?: string;
}
export class SelectedChannelStore extends FluxStore {
getChannelId(guildId?: string | null): string;
getVoiceChannelId(): string | undefined;
getCurrentlySelectedChannelId(guildId?: string): string | undefined;
getMostRecentSelectedTextChannelId(guildId: string): string | undefined;
getLastSelectedChannelId(guildId?: string): string;
// yes this returns a string
getLastSelectedChannels(guildId?: string): string;
/** If you follow an announcement channel, this will return whichever channel you chose as destination */
getLastChannelFollowingDestination(): { guildId?: string; channelId?: string; } | undefined;
getLastChannelFollowingDestination(): ChannelFollowingDestination | undefined;
}

View file

@ -0,0 +1,47 @@
import { FluxStore } from "..";
export interface SoundboardSound {
soundId: string;
name: string;
volume: number;
emojiId: string | null;
emojiName: string | null;
available: boolean;
guildId: string;
userId?: string;
}
export interface TopSoundForGuild {
soundId: string;
rank: number;
}
export interface SoundboardOverlayState {
soundboardSounds: Record<string, SoundboardSound[]>;
favoritedSoundIds: string[];
localSoundboardMutes: string[];
}
export class SoundboardStore extends FluxStore {
getOverlaySerializedState(): SoundboardOverlayState;
getSounds(): Map<string, SoundboardSound[]>;
getSoundsForGuild(guildId: string): SoundboardSound[] | null;
getSound(guildId: string, soundId: string): SoundboardSound;
getSoundById(soundId: string): SoundboardSound;
isFetchingSounds(): boolean;
isFetchingDefaultSounds(): boolean;
isFetching(): boolean;
shouldFetchDefaultSounds(): boolean;
hasFetchedDefaultSounds(): boolean;
isUserPlayingSounds(userId: string): boolean;
isPlayingSound(soundId: string): boolean;
isFavoriteSound(soundId: string): boolean;
getFavorites(): Set<string>;
getAllTopSoundsForGuilds(): Map<string, TopSoundForGuild[]>;
isLocalSoundboardMuted(userId: string): boolean;
hasHadOtherUserPlaySoundInSession(): boolean;
shouldFetchTopSoundsForGuilds(): boolean;
hasFetchedTopSoundsForGuilds(): boolean;
hasFetchedAllSounds(): boolean;
isFetchingAnySounds(): boolean;
}

View file

@ -0,0 +1,6 @@
import { FluxStore } from "..";
export class SpellCheckStore extends FluxStore {
hasLearnedWord(word: string): boolean;
isEnabled(): boolean;
}

View file

@ -0,0 +1,106 @@
import { FluxStore } from "..";
export interface SpotifyDevice {
id: string;
is_active: boolean;
is_private_session: boolean;
is_restricted: boolean;
name: string;
supports_volume: boolean;
type: string;
volume_percent: number;
}
export interface SpotifySocket {
accessToken: string;
accountId: string;
connectionId: string;
isPremium: boolean;
socket: WebSocket;
}
export interface SpotifySocketAndDevice {
socket: SpotifySocket;
device: SpotifyDevice;
}
export interface SpotifyArtist {
id: string;
name: string;
}
export interface SpotifyImage {
url: string;
height: number;
width: number;
}
export interface SpotifyAlbum {
id: string;
name: string;
type: string;
image: SpotifyImage | null;
}
export interface SpotifyTrack {
id: string;
name: string;
duration: number;
isLocal: boolean;
type: string;
album: SpotifyAlbum;
artists: SpotifyArtist[];
}
export interface SpotifyPlayerState {
track: SpotifyTrack;
startTime: number;
context: { uri: string } | null;
}
export interface SpotifyActivity {
name: string;
assets: {
large_image?: string;
large_text?: string;
};
details: string;
state: string | undefined;
timestamps: {
start: number;
end: number;
};
party: {
id: string;
};
sync_id?: string;
flags?: number;
metadata?: {
context_uri: string | undefined;
album_id: string;
artist_ids: string[];
type: string;
button_urls: string[];
};
}
export interface SpotifySyncingWith {
oderId: string;
partyId: string;
sessionId: string;
userId: string;
}
export class SpotifyStore extends FluxStore {
hasConnectedAccount(): boolean;
getActiveSocketAndDevice(): SpotifySocketAndDevice | null;
getPlayableComputerDevices(): SpotifySocketAndDevice[];
canPlay(deviceId: string): boolean;
getSyncingWith(): SpotifySyncingWith | undefined;
wasAutoPaused(): boolean;
getLastPlayedTrackId(): string | undefined;
getTrack(): SpotifyTrack | null;
getPlayerState(accountId: string): SpotifyPlayerState | null;
shouldShowActivity(): boolean;
getActivity(): SpotifyActivity | null;
}

View file

@ -1,15 +1,22 @@
import { FluxStore, GuildSticker, PremiumStickerPack, Sticker } from "..";
export type StickerGuildMap = Map<string, GuildSticker[]>;
export type StickerPackMap = Map<string, Sticker[]>;
export class StickersStore extends FluxStore {
getAllGuildStickers(): StickerGuildMap;
getRawStickersByGuild(): StickerGuildMap;
getPremiumPacks(): PremiumStickerPack[];
hasLoadedStickerPacks: boolean;
isFetchingStickerPacks: boolean;
isLoaded: boolean;
loadState: number;
getAllGuildStickers(): StickerGuildMap;
getAllPackStickers(): StickerPackMap;
getPremiumPacks(): PremiumStickerPack[];
getRawStickersByGuild(): StickerGuildMap;
getStickerById(id: string): Sticker | undefined;
// TODO: type
getStickerMetadataArrays(): any[];
getStickerPack(id: string): PremiumStickerPack | undefined;
getStickersByGuildId(guildId: string): Sticker[] | undefined;
isPremiumPack(id: string): boolean;
}

View file

@ -1,4 +1,14 @@
import { FluxStore } from "@vencord/discord-types";
import { FluxStore } from "..";
export interface StreamerModeSettings {
enabled: boolean;
autoToggle: boolean;
hideInstantInvites: boolean;
hidePersonalInformation: boolean;
disableSounds: boolean;
disableNotifications: boolean;
enableContentProtection: boolean;
}
export class StreamerModeStore extends FluxStore {
get autoToggle(): boolean;
@ -8,4 +18,7 @@ export class StreamerModeStore extends FluxStore {
get enabled(): boolean;
get hideInstantInvites(): boolean;
get hidePersonalInformation(): boolean;
getSettings(): StreamerModeSettings;
getState(): Record<string, StreamerModeSettings>;
}

View file

@ -6,13 +6,14 @@ export type Theme = "light" | "dark" | "darker" | "midnight";
export interface ThemeState {
theme: Theme;
/** 0 = not loaded, 1 = loaded */
status: 0 | 1;
preferences: Record<ThemePreference, Theme>;
}
export class ThemeStore extends FluxStore {
get theme(): Theme;
get darkSidebar(): boolean;
get systemTheme(): SystemTheme;
themePreferenceForSystemTheme(preference: ThemePreference): Theme;
get theme(): Theme;
getState(): ThemeState;
themePreferenceForSystemTheme(preference: ThemePreference): Theme;
}

View file

@ -0,0 +1,11 @@
import { CloudUpload, FluxStore } from "..";
import { DraftType } from "../../enums";
export class UploadAttachmentStore extends FluxStore {
getFirstUpload(channelId: string, draftType: DraftType): CloudUpload | null;
hasAdditionalUploads(channelId: string, draftType: DraftType): boolean;
getUploads(channelId: string, draftType: DraftType): CloudUpload[];
getUploadCount(channelId: string, draftType: DraftType): number;
getUpload(channelId: string, uploadId: string, draftType: DraftType): CloudUpload;
findUpload(channelId: string, draftType: DraftType, predicate: (upload: CloudUpload) => boolean): CloudUpload | undefined;
}

View file

@ -0,0 +1,91 @@
import { Channel, FluxStore } from "..";
export interface MuteConfig {
selected_time_window: number;
end_time: string | null;
}
export interface ChannelOverride {
muted: boolean;
mute_config: MuteConfig | null;
message_notifications: number;
flags: number;
collapsed: boolean;
channel_id: string;
}
export interface GuildSettings {
suppress_everyone: boolean;
suppress_roles: boolean;
mute_scheduled_events: boolean;
mobile_push: boolean;
muted: boolean;
message_notifications: number;
flags: number;
channel_overrides: Record<string, ChannelOverride>;
notify_highlights: number;
hide_muted_channels: boolean;
version: number;
mute_config: MuteConfig | null;
guild_id: string;
}
export interface AccountNotificationSettings {
flags: number;
}
export interface UserGuildSettingsState {
useNewNotifications: boolean;
}
export class UserGuildSettingsStore extends FluxStore {
get accountNotificationSettings(): AccountNotificationSettings;
get mentionOnAllMessages(): boolean;
get useNewNotifications(): boolean;
allowAllMessages(guildId: string): boolean;
allowNoMessages(guildId: string): boolean;
getAddedToMessages(): string[];
// TODO: finish typing
getAllSettings(): { userGuildSettings: Record<string, GuildSettings>; };
getChannelFlags(channel: Channel): number;
getChannelIdFlags(guildId: string, channelId: string): number;
getChannelMessageNotifications(guildId: string, channelId: string): number | null;
getChannelMuteConfig(guildId: string, channelId: string): MuteConfig | null;
getChannelOverrides(guildId: string): Record<string, ChannelOverride>;
getChannelRecordUnreadSetting(channel: Channel): number;
getChannelUnreadSetting(guildId: string, channelId: string): number;
getGuildFavorites(guildId: string): string[];
getGuildFlags(guildId: string): number;
getGuildUnreadSetting(guildId: string): number;
getMessageNotifications(guildId: string): number;
getMuteConfig(guildId: string): MuteConfig | null;
getMutedChannels(guildId: string): string[];
getNewForumThreadsCreated(guildId: string): boolean;
getNotifyHighlights(guildId: string): number;
getOptedInChannels(guildId: string): string[];
// TODO: finish typing these
getOptedInChannelsWithPendingUpdates(guildId: string): Record<string, any>;
getPendingChannelUpdates(guildId: string): Record<string, any>;
getState(): UserGuildSettingsState;
isAddedToMessages(channelId: string): boolean;
isCategoryMuted(guildId: string, channelId: string): boolean;
isChannelMuted(guildId: string, channelId: string): boolean;
isChannelOptedIn(guildId: string, channelId: string, usePending?: boolean): boolean;
isChannelOrParentOptedIn(guildId: string, channelId: string, usePending?: boolean): boolean;
isChannelRecordOrParentOptedIn(channel: Channel, usePending?: boolean): boolean;
isFavorite(guildId: string, channelId: string): boolean;
isGuildCollapsed(guildId: string): boolean;
isGuildOrCategoryOrChannelMuted(guildId: string, channelId: string): boolean;
isMessagesFavorite(guildId: string): boolean;
isMobilePushEnabled(guildId: string): boolean;
isMuteScheduledEventsEnabled(guildId: string): boolean;
isMuted(guildId: string): boolean;
isOptInEnabled(guildId: string): boolean;
isSuppressEveryoneEnabled(guildId: string): boolean;
isSuppressRolesEnabled(guildId: string): boolean;
isTemporarilyMuted(guildId: string): boolean;
resolveGuildUnreadSetting(guildId: string): number;
resolveUnreadSetting(channel: Channel): number;
resolvedMessageNotifications(guildId: string): number;
}

View file

@ -101,28 +101,21 @@ export interface UserProfile extends UserProfileBase, Pick<User, "premiumType">
premiumSince: Date | null;
}
export class UserProfileStore extends FluxStore {
/**
* @param userId the user ID of the profile being fetched.
* @param guildId the guild ID to of the profile being fetched.
* defaults to the internal symbol `NO GUILD ID` if nullish
*
* @returns true if the profile is being fetched, false otherwise.
*/
isFetchingProfile(userId: string, guildId?: string): boolean;
/**
* Check if mutual friends for {@link userId} are currently being fetched.
*
* @param userId the user ID of the mutual friends being fetched.
*
* @returns true if mutual friends are being fetched, false otherwise.
*/
isFetchingFriends(userId: string): boolean;
export interface ApplicationWidgetConfig {
applicationId: string;
widgetType: number;
}
export interface WishlistSettings {
privacy: number;
}
export class UserProfileStore extends FluxStore {
get applicationWidgetApplicationConfigs(): Record<string, ApplicationWidgetConfig>;
get isSubmitting(): boolean;
getUserProfile(userId: string): UserProfile | undefined;
getApplicationWidgetApplicationConfig(applicationId: string): ApplicationWidgetConfig | undefined;
getFirstWishlistId(userId: string): string | null;
getGuildMemberProfile(userId: string, guildId: string | undefined): UserProfileBase | null;
/**
* Get the mutual friends of a user.
@ -148,4 +141,26 @@ export class UserProfileStore extends FluxStore {
* @returns an array of mutual guilds, or undefined if the user has no mutual guilds
*/
getMutualGuilds(userId: string): MutualGuild[] | undefined;
getUserProfile(userId: string): UserProfile | undefined;
// TODO: finish typing
getWidgets(userId: string): any[] | undefined;
getWishlistIds(userId: string): string[];
getWishlistSettings(userId: string): WishlistSettings | null;
/**
* Check if mutual friends for {@link userId} are currently being fetched.
*
* @param userId the user ID of the mutual friends being fetched.
*
* @returns true if mutual friends are being fetched, false otherwise.
*/
isFetchingFriends(userId: string): boolean;
/**
* @param userId the user ID of the profile being fetched.
* @param guildId the guild ID to of the profile being fetched.
* defaults to the internal symbol `NO GUILD ID` if nullish
*
* @returns true if the profile is being fetched, false otherwise.
*/
isFetchingProfile(userId: string, guildId?: string): boolean;
takeSnapshot(): Record<string, UserProfile>;
}

View file

@ -0,0 +1,223 @@
import { FluxStore } from "..";
export interface GuildFolder {
guildIds: string[];
folderId?: number;
folderName?: string;
folderColor?: number;
}
export interface GuildProto {
// TODO: finish typing
channels: Record<string, any>;
hubProgress: number;
guildOnboardingProgress: number;
dismissedGuildContent: Record<string, number>;
disableRaidAlertPush: boolean;
disableRaidAlertNag: boolean;
leaderboardsDisabled: boolean;
// TODO: finish typing
guildDismissibleContentStates: Record<string, any>;
}
export interface UserSettingsVersions {
clientVersion: number;
serverVersion: number;
dataVersion: number;
}
export interface InboxSettings {
currentTab: number;
viewedTutorial: boolean;
}
export interface GuildsSettings {
guilds: Record<string, GuildProto>;
}
export interface UserContentSettings {
dismissedContents: string;
lastReceivedChangelogId: string;
// TODO: finish typing
recurringDismissibleContentStates: Record<string, any>;
// TODO: type
lastDismissedOutboundPromotionStartDate: any;
premiumTier0ModalDismissedAt: any;
}
export interface VoiceAndVideoSettings {
// TODO: type
videoBackgroundFilterDesktop: any;
alwaysPreviewVideo: boolean;
afkTimeout: number;
streamNotificationsEnabled: boolean;
nativePhoneIntegrationEnabled: boolean;
disableStreamPreviews: boolean;
soundmojiVolume: number;
}
export interface TextAndImagesSettings {
emojiPickerCollapsedSections: string[];
stickerPickerCollapsedSections: string[];
soundboardPickerCollapsedSections: string[];
dmSpamFilterV2: number;
viewImageDescriptions: boolean;
inlineAttachmentMedia: boolean;
inlineEmbedMedia: boolean;
gifAutoPlay: boolean;
renderEmbeds: boolean;
renderReactions: boolean;
animateEmoji: boolean;
animateStickers: number;
enableTtsCommand: boolean;
messageDisplayCompact: boolean;
explicitContentFilter: number;
viewNsfwGuilds: boolean;
convertEmoticons: boolean;
viewNsfwCommands: boolean;
includeStickersInAutocomplete: boolean;
// TODO: type these
explicitContentSettings: any;
goreContentSettings: any;
showMentionSuggestions: boolean;
}
export interface NotificationsSettings {
notificationCenterAckedBeforeId: string;
focusModeExpiresAtMs: string;
reactionNotifications: number;
gameActivityNotifications: boolean;
customStatusPushNotifications: boolean;
showInAppNotifications: boolean;
notifyFriendsOnGoLive: boolean;
enableVoiceActivityNotifications: boolean;
enableUserResurrectionNotifications: boolean;
}
export interface PrivacySettings {
restrictedGuildIds: string[];
defaultGuildsRestricted: boolean;
allowAccessibilityDetection: boolean;
activityRestrictedGuildIds: string[];
defaultGuildsActivityRestricted: boolean;
activityJoiningRestrictedGuildIds: string[];
messageRequestRestrictedGuildIds: string[];
guildsLeaderboardOptOutDefault: boolean;
slayerSdkReceiveDmsInGame: boolean;
defaultGuildsActivityRestrictedV2: boolean;
detectPlatformAccounts: boolean;
passwordless: boolean;
contactSyncEnabled: boolean;
friendSourceFlags: number;
friendDiscoveryFlags: number;
dropsOptedOut: boolean;
hideLegacyUsername: boolean;
defaultGuildsRestrictedV2: boolean;
quests3PDataOptedOut: boolean;
}
export interface GameLibrarySettings {
disableGamesTab: boolean;
}
export interface StatusSettings {
statusExpiresAtMs: string;
status: { status: string; } | null;
showCurrentGame: boolean;
statusCreatedAtMs: string;
}
export interface LocalizationSettings {
locale: { localeCode: string; } | null;
timezoneOffset: { offset: number; } | null;
}
export interface AppearanceSettings {
theme: number;
developerMode: boolean;
mobileRedesignDisabled: boolean;
timestampHourCycle: number;
launchPadMode: number;
uiDensity: number;
swipeRightToLeftMode: number;
// TODO: type
clientThemeSettings: any;
}
export interface GuildFoldersSettings {
folders: GuildFolder[];
guildPositions: string[];
}
export interface AudioContextSettings {
// TODO: finish these
user: Record<string, any>;
stream: Record<string, any>;
}
export interface ClipsSettings {
allowVoiceRecording: boolean;
}
export interface InAppFeedbackSettings {
// TODO: finish typing
inAppFeedbackStates: Record<string, any>;
}
export interface UserSettings {
versions: UserSettingsVersions;
inbox: InboxSettings;
guilds: GuildsSettings;
userContent: UserContentSettings;
voiceAndVideo: VoiceAndVideoSettings;
textAndImages: TextAndImagesSettings;
notifications: NotificationsSettings;
privacy: PrivacySettings;
// TODO: finish typing
debug: Record<string, any>;
gameLibrary: GameLibrarySettings;
status: StatusSettings;
localization: LocalizationSettings;
appearance: AppearanceSettings;
guildFolders: GuildFoldersSettings;
audioContextSettings: AudioContextSettings;
clips: ClipsSettings;
inAppFeedbackSettings: InAppFeedbackSettings;
}
export interface FrecencySettings {
// TODO: type all of these
versions: any;
favoriteGifs: any;
favoriteStickers: any;
stickerFrecency: any;
favoriteEmojis: any;
emojiFrecency: any;
applicationCommandFrecency: any;
favoriteSoundboardSounds: any;
applicationFrecency: any;
playedSoundFrecency: any;
guildAndChannelFrecency: any;
emojiReactionFrecency: any;
}
export interface ProtoState {
// TODO: type
proto: any;
}
export class UserSettingsProtoStore extends FluxStore {
settings: UserSettings;
frecencyWithoutFetchingLatest: FrecencySettings;
wasMostRecentUpdateFromServer: boolean;
getState(): Record<string, ProtoState>;
computeState(): Record<string, ProtoState>;
getFullState(): Record<string, ProtoState>;
hasLoaded(settingsType: number): boolean;
getGuildFolders(): GuildFolder[];
getGuildRecentsDismissedAt(guildId: string): number;
getDismissedGuildContent(guildId: string): Record<string, number> | null;
// TODO: finish typing
getGuildDismissedContentState(guildId: string): any;
getGuildsProto(): Record<string, GuildProto>;
}

View file

@ -1,10 +1,34 @@
import { FluxStore, User } from "..";
/** returned by takeSnapshot for persistence */
export interface UserStoreSnapshot {
/** snapshot format version, currently 1 */
version: number;
data: {
/** contains only the current user */
users: User[];
};
}
export class UserStore extends FluxStore {
filter(filter: (user: User) => boolean, sort?: boolean): Record<string, User>;
findByTag(username: string, discriminator: string): User;
forEach(action: (user: User) => void): void;
/**
* filters users and optionally sorts results.
* @param sort if true (default false), sorts alphabetically by username
*/
filter(filter: (user: User) => boolean, sort?: boolean): User[];
/**
* finds user by username and discriminator.
* for new username system (unique usernames), pass null/undefined as discriminator.
*/
findByTag(username: string, discriminator?: string | null): User | undefined;
/** @param action return false to break iteration early */
forEach(action: (user: User) => boolean | void): void;
getCurrentUser(): User;
getUser(userId: string): User;
/** keyed by user ID */
getUsers(): Record<string, User>;
/** increments when users are added/updated/removed */
getUserStoreVersion(): number;
/** only includes current user, used for persistence */
takeSnapshot(): UserStoreSnapshot;
}

View file

@ -24,19 +24,28 @@ export interface VoiceState extends DiscordRecord {
export class VoiceStateStore extends FluxStore {
getAllVoiceStates(): VoiceStates;
getVoiceStateVersion(): number;
getVoiceStates(guildId?: string | null): UserVoiceStateRecords;
getVoiceStatesForChannel(channelId: string): UserVoiceStateRecords;
getVideoVoiceStatesForChannel(channelId: string): UserVoiceStateRecords;
getVoiceState(guildId: string | null, userId: string): VoiceState | undefined;
getUserVoiceChannelId(guildId: string | null, userId: string): string | undefined;
getDiscoverableVoiceState(guildId: string | null, userId: string): VoiceState | null;
getVoiceStateForChannel(channelId: string, userId?: string): VoiceState | undefined;
getVoiceStateForUser(userId: string): VoiceState | undefined;
getDiscoverableVoiceStateForUser(userId: string): VoiceState | undefined;
getVoiceStateForSession(userId: string, sessionId?: string | null): VoiceState | null | undefined;
getUserVoiceChannelId(guildId: string | null, userId: string): string | undefined;
getCurrentClientVoiceChannelId(guildId: string | null): string | undefined;
isCurrentClientInVoiceChannel(): boolean;
getUsersWithVideo(channelId: string): Set<string>;
getVoicePlatformForChannel(channelId: string, guildId: string): string | undefined;
isCurrentClientInVoiceChannel(): boolean;
isInChannel(channelId: string, userId?: string): boolean;
hasVideo(channelId: string): boolean;
get userHasBeenMovedVersion(): number;
}

View file

@ -1,7 +1,22 @@
import { FluxStore } from "..";
export class WindowStore extends FluxStore {
isElementFullScreen(): boolean;
isFocused(): boolean;
windowSize(): Record<"width" | "height", number>;
export interface WindowSize {
width: number;
height: number;
}
export class WindowStore extends FluxStore {
/** returns focused window ID, or null if no window is focused */
getFocusedWindowId(): string | null;
getLastFocusedWindowId(): string;
/** true if any window is focused (getFocusedWindowId() !== null) */
isAppFocused(): boolean;
/** @param windowId defaults to current window */
isElementFullScreen(windowId?: string): boolean;
/** @param windowId defaults to current window */
isFocused(windowId?: string): boolean;
/** @param windowId defaults to current window */
isVisible(windowId?: string): boolean;
/** @param windowId defaults to current window, returns {width: 0, height: 0} for invalid ID */
windowSize(windowId?: string): WindowSize;
}

View file

@ -1,22 +1,47 @@
// please keep in alphabetical order
export * from "./AccessibilityStore";
export * from "./ActiveJoinedThreadsStore";
export * from "./ApplicationStore";
export * from "./AuthenticationStore";
export * from "./CallStore";
export * from "./ChannelRTCStore";
export * from "./ChannelStore";
export * from "./DraftStore";
export * from "./EmojiStore";
export * from "./FluxStore";
export * from "./FriendsStore";
export * from "./GuildChannelStore";
export * from "./GuildMemberCountStore";
export * from "./GuildMemberStore";
export * from "./GuildRoleStore";
export * from "./GuildScheduledEventStore";
export * from "./GuildStore";
export * from "./InstantInviteStore";
export * from "./InviteStore";
export * from "./LocaleStore";
export * from "./MediaEngineStore";
export * from "./MessageStore";
export * from "./NotificationSettingsStore";
export * from "./OverridePremiumTypeStore";
export * from "./PermissionStore";
export * from "./PresenceStore";
export * from "./ReadStateStore";
export * from "./RelationshipStore";
export * from "./RTCConnectionStore";
export * from "./RunningGameStore";
export * from "./SelectedChannelStore";
export * from "./SelectedGuildStore";
export * from "./SoundboardStore";
export * from "./SpellCheckStore";
export * from "./SpotifyStore";
export * from "./StickersStore";
export * from "./StreamerModeStore";
export * from "./ThemeStore";
export * from "./TypingStore";
export * from "./UploadAttachmentStore";
export * from "./UserGuildSettingsStore";
export * from "./UserProfileStore";
export * from "./UserSettingsProtoStore";
export * from "./UserStore";
export * from "./VoiceStateStore";
export * from "./WindowStore";

View file

@ -17,13 +17,12 @@
*/
import ErrorBoundary from "@components/ErrorBoundary";
import { findComponentByCodeLazy, findStoreLazy } from "@webpack";
import { Animations, useStateFromStores } from "@webpack/common";
import { findComponentByCodeLazy } from "@webpack";
import { Animations, ChannelRTCStore, useStateFromStores } from "@webpack/common";
import type { CSSProperties } from "react";
import { ExpandedGuildFolderStore, settings, SortedGuildStore } from ".";
const ChannelRTCStore = findStoreLazy("ChannelRTCStore");
const GuildsBar = findComponentByCodeLazy('("guildsnav")');
function getExpandedFolderIds() {

View file

@ -25,13 +25,11 @@ import { Logger } from "@utils/Logger";
import definePlugin, { OptionType, Patch } from "@utils/types";
import type { Emoji, Message, Sticker } from "@vencord/discord-types";
import { StickerFormatType } from "@vencord/discord-types/enums";
import { findByCodeLazy, findByPropsLazy, findStoreLazy, proxyLazyWebpack } from "@webpack";
import { Alerts, ChannelStore, DraftType, EmojiStore, FluxDispatcher, Forms, GuildMemberStore, lodash, Parser, PermissionsBits, PermissionStore, StickersStore, UploadHandler, UserSettingsActionCreators, UserStore } from "@webpack/common";
import { findByCodeLazy, findByPropsLazy, proxyLazyWebpack } from "@webpack";
import { Alerts, ChannelStore, DraftType, EmojiStore, FluxDispatcher, Forms, GuildMemberStore, lodash, Parser, PermissionsBits, PermissionStore, StickersStore, UploadHandler, UserSettingsActionCreators, UserSettingsProtoStore, UserStore } from "@webpack/common";
import { applyPalette, GIFEncoder, quantize } from "gifenc";
import type { ReactElement, ReactNode } from "react";
const UserSettingsProtoStore = findStoreLazy("UserSettingsProtoStore");
const BINARY_READ_OPTIONS = findByPropsLazy("readerFactory");
function searchProtoClassField(localName: string, protoClass: any) {

View file

@ -11,8 +11,7 @@ import { Flex } from "@components/Flex";
import { Devs } from "@utils/constants";
import { Margins } from "@utils/margins";
import definePlugin, { OptionType } from "@utils/types";
import { findStoreLazy } from "@webpack";
import { Button, Forms, showToast, TextInput, Toasts, Tooltip, useEffect, useState } from "@webpack/common";
import { Button, Forms, RunningGameStore, showToast, TextInput, Toasts, Tooltip, useEffect, useState } from "@webpack/common";
const enum ActivitiesTypes {
Game,
@ -30,8 +29,6 @@ const enum FilterMode {
Blacklist
}
const RunningGameStore = findStoreLazy("RunningGameStore");
const ShowCurrentGame = getUserSettingLazy("status", "showCurrentGame")!;
function ToggleIcon(activity: IgnoredActivity, tooltipText: string, path: string, fill: string) {

View file

@ -6,9 +6,9 @@
import { getCurrentChannel } from "@utils/discord";
import { isObjectEmpty } from "@utils/misc";
import { ChannelStore, PermissionsBits, PermissionStore, SelectedChannelStore, Tooltip, useEffect, useStateFromStores, VoiceStateStore } from "@webpack/common";
import { ChannelStore, GuildMemberCountStore, PermissionsBits, PermissionStore, SelectedChannelStore, Tooltip, useEffect, useStateFromStores, VoiceStateStore } from "@webpack/common";
import { ChannelMemberStore, cl, GuildMemberCountStore, numberFormat, settings, ThreadMemberListStore } from ".";
import { ChannelMemberStore, cl, numberFormat, settings, ThreadMemberListStore } from ".";
import { CircleIcon } from "./CircleIcon";
import { OnlineMemberCountStore } from "./OnlineMemberCountStore";
import { VoiceIcon } from "./VoiceIcon";
@ -40,7 +40,7 @@ export function MemberCount({ isTooltip, tooltipGuildId }: { isTooltip?: true; t
const totalCount = useStateFromStores(
[GuildMemberCountStore],
() => GuildMemberCountStore.getMemberCount(guildId)
() => GuildMemberCountStore.getMemberCount(guildId!)
);
let onlineCount = useStateFromStores(

View file

@ -22,7 +22,7 @@ export const OnlineMemberCountStore = proxyLazy(() => {
async _ensureCount(guildId: string) {
if (onlineMemberMap.has(guildId)) return;
await ChannelActionCreators.preload(guildId, GuildChannelStore.getDefaultChannel(guildId).id);
await ChannelActionCreators.preload(guildId, GuildChannelStore.getDefaultChannel(guildId)!.id);
}
ensureCount(guildId?: string) {

View file

@ -28,7 +28,6 @@ import { findStoreLazy } from "@webpack";
import { MemberCount } from "./MemberCount";
export const GuildMemberCountStore = findStoreLazy("GuildMemberCountStore") as FluxStore & { getMemberCount(guildId?: string): number | null; };
export const ChannelMemberStore = findStoreLazy("ChannelMemberStore") as FluxStore & {
getProps(guildId?: string, channelId?: string): { groups: { count: number; id: string; }[]; };
};

View file

@ -21,8 +21,7 @@ import { Devs } from "@utils/constants";
import { makeLazy } from "@utils/lazy";
import definePlugin from "@utils/types";
import { CommandArgument, CommandContext } from "@vencord/discord-types";
import { findByPropsLazy } from "@webpack";
import { DraftType, UploadHandler, UploadManager, UserUtils } from "@webpack/common";
import { DraftType, UploadAttachmentStore, UploadHandler, UploadManager, UserUtils } from "@webpack/common";
import { applyPalette, GIFEncoder, quantize } from "gifenc";
const DEFAULT_DELAY = 20;
@ -36,8 +35,6 @@ const getFrames = makeLazy(() => Promise.all(
))
);
const UploadStore = findByPropsLazy("getUploads");
function loadImage(source: File | string) {
const isFile = source instanceof File;
const url = isFile ? URL.createObjectURL(source) : source;
@ -59,7 +56,7 @@ async function resolveImage(options: CommandArgument[], ctx: CommandContext, noS
for (const opt of options) {
switch (opt.name) {
case "image":
const upload = UploadStore.getUpload(ctx.channel.id, opt.name, DraftType.SlashCommand);
const upload = UploadAttachmentStore.getUpload(ctx.channel.id, opt.name, DraftType.SlashCommand);
if (upload) {
if (!upload.isImage) {
UploadManager.clearAll(ctx.channel.id, DraftType.SlashCommand);

View file

@ -21,10 +21,7 @@ import { generateId, sendBotMessage } from "@api/Commands";
import { Devs } from "@utils/constants";
import definePlugin, { IconComponent, StartAt } from "@utils/types";
import { CloudUpload, MessageAttachment } from "@vencord/discord-types";
import { findByPropsLazy } from "@webpack";
import { DraftStore, DraftType, UserStore, useStateFromStores } from "@webpack/common";
const UploadStore = findByPropsLazy("getUploads");
import { DraftStore, DraftType, UploadAttachmentStore, UserStore, useStateFromStores } from "@webpack/common";
const getDraft = (channelId: string) => DraftStore.getDraft(channelId, DraftType.ChannelMessage);
@ -44,7 +41,7 @@ const getImageBox = (url: string): Promise<{ width: number, height: number; } |
const getAttachments = async (channelId: string) =>
await Promise.all(
UploadStore.getUploads(channelId, DraftType.ChannelMessage)
UploadAttachmentStore.getUploads(channelId, DraftType.ChannelMessage)
.map(async (upload: CloudUpload) => {
const { isImage, filename, spoiler, item: { file } } = upload;
const url = URL.createObjectURL(file);
@ -94,7 +91,7 @@ const PreviewButton: ChatBarButtonFactory = ({ isAnyChat, isEmpty, type: { attac
if (!isAnyChat) return null;
const hasAttachments = attachments && UploadStore.getUploads(channelId, DraftType.ChannelMessage).length > 0;
const hasAttachments = attachments && UploadAttachmentStore.getUploads(channelId, DraftType.ChannelMessage).length > 0;
const hasContent = !isEmpty && draft?.length > 0;
if (!hasContent && !hasAttachments) return null;

View file

@ -23,30 +23,14 @@ import { TextButton } from "@components/Button";
import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants";
import definePlugin from "@utils/types";
import { Channel } from "@vencord/discord-types";
import { findStoreLazy } from "@webpack";
import { FluxDispatcher, GuildChannelStore, GuildStore, React, ReadStateStore } from "@webpack/common";
interface ThreadJoined {
channel: Channel;
joinTimestamp: number;
}
type ThreadsJoined = Record<string, ThreadJoined>;
type ThreadsJoinedByParent = Record<string, ThreadsJoined>;
interface ActiveJoinedThreadsStore {
getActiveJoinedThreadsForGuild(guildId: string): ThreadsJoinedByParent;
}
const ActiveJoinedThreadsStore: ActiveJoinedThreadsStore = findStoreLazy("ActiveJoinedThreadsStore");
import { ActiveJoinedThreadsStore, FluxDispatcher, GuildChannelStore, GuildStore, React, ReadStateStore } from "@webpack/common";
function onClick() {
const channels: Array<any> = [];
Object.values(GuildStore.getGuilds()).forEach(guild => {
GuildChannelStore.getChannels(guild.id).SELECTABLE // Array<{ channel, comparator }>
.concat(GuildChannelStore.getChannels(guild.id).VOCAL) // Array<{ channel, comparator }>
GuildChannelStore.getChannels(guild.id).SELECTABLE
.concat(GuildChannelStore.getChannels(guild.id).VOCAL)
.concat(
Object.values(ActiveJoinedThreadsStore.getActiveJoinedThreadsForGuild(guild.id))
.flatMap(threadChannels => Object.values(threadChannels))

View file

@ -17,11 +17,11 @@
*/
import { getUniqueUsername, openUserProfile } from "@utils/discord";
import { ChannelType } from "@vencord/discord-types/enums";
import { ChannelType, RelationshipType } from "@vencord/discord-types/enums";
import { UserUtils } from "@webpack/common";
import settings from "./settings";
import { ChannelDelete, GuildDelete, RelationshipRemove, RelationshipType } from "./types";
import { ChannelDelete, GuildDelete, RelationshipRemove } from "./types";
import { deleteGroup, deleteGuild, getGroup, getGuild, GuildAvailabilityStore, notify } from "./utils";
let manuallyRemovedFriend: string | undefined;

View file

@ -51,10 +51,3 @@ export interface SimpleGuild {
name: string;
iconURL?: string;
}
export const enum RelationshipType {
FRIEND = 1,
BLOCKED = 2,
INCOMING_REQUEST = 3,
OUTGOING_REQUEST = 4,
}

View file

@ -21,12 +21,12 @@ import { popNotice, showNotice } from "@api/Notices";
import { showNotification } from "@api/Notifications";
import { getUniqueUsername, openUserProfile } from "@utils/discord";
import { FluxStore } from "@vencord/discord-types";
import { ChannelType } from "@vencord/discord-types/enums";
import { ChannelType, RelationshipType } from "@vencord/discord-types/enums";
import { findStoreLazy } from "@webpack";
import { ChannelStore, GuildMemberStore, GuildStore, RelationshipStore, UserStore, UserUtils } from "@webpack/common";
import settings from "./settings";
import { RelationshipType, SimpleGroupChannel, SimpleGuild } from "./types";
import { SimpleGroupChannel, SimpleGuild } from "./types";
export const GuildAvailabilityStore = findStoreLazy("GuildAvailabilityStore") as FluxStore & {
totalGuilds: number;

View file

@ -24,13 +24,11 @@ import { buildSeveralUsers } from "@plugins/typingTweaks";
import { Devs } from "@utils/constants";
import { getIntlMessage } from "@utils/discord";
import definePlugin, { OptionType } from "@utils/types";
import { findComponentByCodeLazy, findStoreLazy } from "@webpack";
import { GuildMemberStore, RelationshipStore, SelectedChannelStore, Tooltip, TypingStore, UserStore, UserSummaryItem, useStateFromStores } from "@webpack/common";
import { findComponentByCodeLazy } from "@webpack";
import { GuildMemberStore, RelationshipStore, SelectedChannelStore, Tooltip, TypingStore, UserGuildSettingsStore, UserStore, UserSummaryItem, useStateFromStores } from "@webpack/common";
const ThreeDots = findComponentByCodeLazy(".dots,", "dotRadius:");
const UserGuildSettingsStore = findStoreLazy("UserGuildSettingsStore");
const enum IndicatorMode {
Dots = 1 << 0,
Avatars = 1 << 1

View file

@ -17,11 +17,10 @@
*/
import { PluginNative } from "@utils/types";
import { Button, showToast, Toasts, useState } from "@webpack/common";
import { Button, MediaEngineStore, showToast, Toasts, useState } from "@webpack/common";
import type { VoiceRecorder } from ".";
import { settings } from "./settings";
import { MediaEngineStore } from "./utils";
const Native = VencordNative.pluginHelpers.VoiceMessages as PluginNative<typeof import("./native")>;

View file

@ -19,7 +19,7 @@
import { useTimer } from "@utils/react";
import { findComponentByCodeLazy } from "@webpack";
import { cl } from "./utils";
import { cl } from ".";
interface VoiceMessageProps {
src: string;

View file

@ -16,11 +16,10 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Button, useState } from "@webpack/common";
import { Button, MediaEngineStore, useState } from "@webpack/common";
import type { VoiceRecorder } from ".";
import { settings } from "./settings";
import { MediaEngineStore } from "./utils";
export const VoiceRecorderWeb: VoiceRecorder = ({ setAudioBlob, onRecordingChange }) => {
const [recording, setRecording] = useState(false);

View file

@ -24,6 +24,7 @@ import { Microphone } from "@components/Icons";
import { Link } from "@components/Link";
import { Paragraph } from "@components/Paragraph";
import { Devs } from "@utils/constants";
import { classNameFactory } from "@utils/css";
import { Margins } from "@utils/margins";
import { ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, openModal } from "@utils/modal";
import { useAwaiter } from "@utils/react";
@ -37,7 +38,6 @@ import { ComponentType } from "react";
import { VoiceRecorderDesktop } from "./DesktopRecorder";
import { settings } from "./settings";
import { cl } from "./utils";
import { VoicePreview } from "./VoicePreview";
import { VoiceRecorderWeb } from "./WebRecorder";
@ -45,6 +45,7 @@ const CloudUpload: typeof TCloudUpload = findLazy(m => m.prototype?.trackUploadF
const PendingReplyStore = findStoreLazy("PendingReplyStore");
const OptionClasses = findByPropsLazy("optionName", "optionIcon", "optionLabel");
export const cl = classNameFactory("vc-vmsg-");
export type VoiceRecorder = ComponentType<{
setAudioBlob(blob: Blob): void;
onRecordingChange?(recording: boolean): void;

View file

@ -1,23 +0,0 @@
/*
* 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 { classNameFactory } from "@utils/css";
import { findStoreLazy } from "@webpack";
export const MediaEngineStore = findStoreLazy("MediaEngineStore");
export const cl = classNameFactory("vc-vmsg-");

View file

@ -31,16 +31,20 @@ export let MessageStore: Omit<t.MessageStore, "getMessages"> & GenericStore & {
getMessages(chanId: string): any;
};
export let PermissionStore: GenericStore;
export let GuildChannelStore: GenericStore;
export let ReadStateStore: GenericStore;
export let PermissionStore: t.PermissionStore;
export let GuildChannelStore: t.GuildChannelStore;
export let ReadStateStore: t.ReadStateStore;
export let PresenceStore: t.PresenceStore;
export let AccessibilityStore: t.AccessibilityStore;
export let GuildStore: t.GuildStore;
export let GuildRoleStore: t.GuildRoleStore;
export let GuildScheduledEventStore: t.GuildScheduledEventStore;
export let GuildMemberCountStore: t.GuildMemberCountStore;
export let GuildMemberStore: t.GuildMemberStore;
export let UserStore: t.UserStore;
export let AuthenticationStore: t.AuthenticationStore;
export let ApplicationStore: t.ApplicationStore;
export let UserProfileStore: t.UserProfileStore;
export let SelectedChannelStore: t.SelectedChannelStore;
export let SelectedGuildStore: t.SelectedGuildStore;
@ -55,12 +59,33 @@ export let ThemeStore: t.ThemeStore;
export let WindowStore: t.WindowStore;
export let DraftStore: t.DraftStore;
export let StreamerModeStore: t.StreamerModeStore;
export let SpotifyStore: t.SpotifyStore;
export let MediaEngineStore: t.MediaEngineStore;
export let NotificationSettingsStore: t.NotificationSettingsStore;
export let SpellCheckStore: t.SpellCheckStore;
export let UploadAttachmentStore: t.UploadAttachmentStore;
export let OverridePremiumTypeStore: t.OverridePremiumTypeStore;
export let RunningGameStore: t.RunningGameStore;
export let ActiveJoinedThreadsStore: t.ActiveJoinedThreadsStore;
export let UserGuildSettingsStore: t.UserGuildSettingsStore;
export let UserSettingsProtoStore: t.UserSettingsProtoStore;
export let CallStore: t.CallStore;
export let ChannelRTCStore: t.ChannelRTCStore;
export let FriendsStore: t.FriendsStore;
export let InstantInviteStore: t.InstantInviteStore;
export let InviteStore: t.InviteStore;
export let LocaleStore: t.LocaleStore;
export let RTCConnectionStore: t.RTCConnectionStore;
export let SoundboardStore: t.SoundboardStore;
/**
* @see jsdoc of {@link t.useStateFromStores}
*/
export const useStateFromStores: t.useStateFromStores = findByCodeLazy("useStateFromStores");
waitForStore("AccessibilityStore", s => AccessibilityStore = s);
waitForStore("ApplicationStore", s => ApplicationStore = s);
waitForStore("AuthenticationStore", s => AuthenticationStore = s);
waitForStore("DraftStore", s => DraftStore = s);
waitForStore("UserStore", s => UserStore = s);
@ -71,11 +96,16 @@ waitForStore("SelectedGuildStore", m => SelectedGuildStore = m);
waitForStore("GuildStore", m => GuildStore = m);
waitForStore("GuildMemberStore", m => GuildMemberStore = m);
waitForStore("RelationshipStore", m => RelationshipStore = m);
waitForStore("MediaEngineStore", m => MediaEngineStore = m);
waitForStore("NotificationSettingsStore", m => NotificationSettingsStore = m);
waitForStore("SpellcheckStore", m => SpellCheckStore = m);
waitForStore("PermissionStore", m => PermissionStore = m);
waitForStore("PresenceStore", m => PresenceStore = m);
waitForStore("ReadStateStore", m => ReadStateStore = m);
waitForStore("GuildChannelStore", m => GuildChannelStore = m);
waitForStore("GuildRoleStore", m => GuildRoleStore = m);
waitForStore("GuildScheduledEventStore", m => GuildScheduledEventStore = m);
waitForStore("GuildMemberCountStore", m => GuildMemberCountStore = m);
waitForStore("MessageStore", m => MessageStore = m);
waitForStore("WindowStore", m => WindowStore = m);
waitForStore("EmojiStore", m => EmojiStore = m);
@ -83,6 +113,21 @@ waitForStore("StickersStore", m => StickersStore = m);
waitForStore("TypingStore", m => TypingStore = m);
waitForStore("VoiceStateStore", m => VoiceStateStore = m);
waitForStore("StreamerModeStore", m => StreamerModeStore = m);
waitForStore("SpotifyStore", m => SpotifyStore = m);
waitForStore("OverridePremiumTypeStore", m => OverridePremiumTypeStore = m);
waitForStore("UploadAttachmentStore", m => UploadAttachmentStore = m);
waitForStore("RunningGameStore", m => RunningGameStore = m);
waitForStore("ActiveJoinedThreadsStore", m => ActiveJoinedThreadsStore = m);
waitForStore("UserGuildSettingsStore", m => UserGuildSettingsStore = m);
waitForStore("UserSettingsProtoStore", m => UserSettingsProtoStore = m);
waitForStore("CallStore", m => CallStore = m);
waitForStore("ChannelRTCStore", m => ChannelRTCStore = m);
waitForStore("FriendsStore", m => FriendsStore = m);
waitForStore("InstantInviteStore", m => InstantInviteStore = m);
waitForStore("InviteStore", m => InviteStore = m);
waitForStore("LocaleStore", m => LocaleStore = m);
waitForStore("RTCConnectionStore", m => RTCConnectionStore = m);
waitForStore("SoundboardStore", m => SoundboardStore = m);
waitForStore("ThemeStore", m => {
ThemeStore = m;
// Importing this directly causes all webpack commons to be imported, which can easily cause circular dependencies.