Merge remote-tracking branch 'upstream/dev' into dev
This commit is contained in:
commit
ebc5c975dc
12 changed files with 311 additions and 65 deletions
246
packages/discord-types/src/stores/PopoutWindowStore.d.ts
vendored
Normal file
246
packages/discord-types/src/stores/PopoutWindowStore.d.ts
vendored
Normal file
|
|
@ -0,0 +1,246 @@
|
|||
import { FluxStore } from "..";
|
||||
|
||||
/**
|
||||
* Known popout window key constants.
|
||||
* Used as the key parameter for PopoutWindowStore and PopoutActions methods.
|
||||
*/
|
||||
export type PopoutWindowKey =
|
||||
| "DISCORD_CHANNEL_CALL_POPOUT"
|
||||
| "DISCORD_CALL_TILE_POPOUT"
|
||||
| "DISCORD_SOUNDBOARD"
|
||||
| "DISCORD_RTC_DEBUG_POPOUT"
|
||||
| "DISCORD_CHANNEL_POPOUT"
|
||||
| "DISCORD_ACTIVITY_POPOUT"
|
||||
| "DISCORD_OVERLAY_POPOUT"
|
||||
| "DISCORD_DEVTOOLS_POPOUT";
|
||||
|
||||
/**
|
||||
* Popout window lifecycle event types.
|
||||
* Sent via postMessage from popout to parent window.
|
||||
*/
|
||||
export type PopoutWindowEventType = "loaded" | "unloaded";
|
||||
|
||||
/**
|
||||
* Persisted window position and size state.
|
||||
* Saved to localStorage and restored when reopening popouts.
|
||||
*/
|
||||
export interface PopoutWindowState {
|
||||
/** window x position on screen in pixels. */
|
||||
x: number;
|
||||
/** window y position on screen in pixels. */
|
||||
y: number;
|
||||
/** window inner width in pixels. */
|
||||
width: number;
|
||||
/** window inner height in pixels. */
|
||||
height: number;
|
||||
/** whether window stays above other windows, only on desktop app. */
|
||||
alwaysOnTop?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Features passed to window.open() for popout configuration.
|
||||
* Merged with default features (menubar, toolbar, location, directories = false).
|
||||
*/
|
||||
export interface BrowserWindowFeatures {
|
||||
/** whether to show browser toolbar. */
|
||||
toolbar?: boolean;
|
||||
/** whether to show menu bar. */
|
||||
menubar?: boolean;
|
||||
/** whether to show location/address bar. */
|
||||
location?: boolean;
|
||||
/** whether to show directory buttons. */
|
||||
directories?: boolean;
|
||||
/** window width in pixels. */
|
||||
width?: number;
|
||||
/** window height in pixels. */
|
||||
height?: number;
|
||||
/** default width if no persisted state exists. */
|
||||
defaultWidth?: number;
|
||||
/** default height if no persisted state exists. */
|
||||
defaultHeight?: number;
|
||||
/** window left position in pixels. */
|
||||
left?: number;
|
||||
/** window top position in pixels. */
|
||||
top?: number;
|
||||
/** default always-on-top state, defaults to false. */
|
||||
defaultAlwaysOnTop?: boolean;
|
||||
/** whether window can be moved by user. */
|
||||
movable?: boolean;
|
||||
/** whether window can be resized by user. */
|
||||
resizable?: boolean;
|
||||
/** whether window has a frame/border. */
|
||||
frame?: boolean;
|
||||
/** whether window stays above other windows. */
|
||||
alwaysOnTop?: boolean;
|
||||
/** whether window has a shadow (macOS). */
|
||||
hasShadow?: boolean;
|
||||
/** whether window background is transparent. */
|
||||
transparent?: boolean;
|
||||
/** whether to hide window from taskbar. */
|
||||
skipTaskbar?: boolean;
|
||||
/** title bar style, null for default. */
|
||||
titleBarStyle?: string | null;
|
||||
/** window background color as hex string. */
|
||||
backgroundColor?: string;
|
||||
/** whether this is an out-of-process overlay window. */
|
||||
outOfProcessOverlay?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Manages Discord's popout windows (voice calls, activities, etc.).
|
||||
* Extends PersistedStore to save window positions across sessions.
|
||||
*
|
||||
* Handles Flux actions:
|
||||
* - POPOUT_WINDOW_OPEN: opens a new popout window
|
||||
* - POPOUT_WINDOW_CLOSE: closes a popout window
|
||||
* - POPOUT_WINDOW_SET_ALWAYS_ON_TOP: toggles always-on-top (desktop only)
|
||||
* - POPOUT_WINDOW_ADD_STYLESHEET: injects stylesheet into all open popouts
|
||||
* - LOGOUT: closes all popout windows
|
||||
*/
|
||||
export class PopoutWindowStore extends FluxStore {
|
||||
/**
|
||||
* Gets the Window object for a popout.
|
||||
* @param key unique identifier for the popout window
|
||||
* @returns Window reference or undefined if not open
|
||||
*/
|
||||
getWindow(key: string): Window | undefined;
|
||||
|
||||
/**
|
||||
* Gets persisted position/size state for a window.
|
||||
* State is saved when window closes and restored when reopened.
|
||||
* @param key unique identifier for the popout window
|
||||
* @returns saved state or undefined if never opened
|
||||
*/
|
||||
getWindowState(key: string): PopoutWindowState | undefined;
|
||||
|
||||
/**
|
||||
* Gets all currently open popout window keys.
|
||||
* @returns array of window key identifiers
|
||||
*/
|
||||
getWindowKeys(): string[];
|
||||
|
||||
/**
|
||||
* Checks if a popout window is currently open.
|
||||
* @param key unique identifier for the popout window
|
||||
* @returns true if window exists and is not closed
|
||||
*/
|
||||
getWindowOpen(key: string): boolean;
|
||||
|
||||
/**
|
||||
* Checks if a popout window has always-on-top enabled.
|
||||
* Only functional on desktop app (isPlatformEmbedded).
|
||||
* @param key unique identifier for the popout window
|
||||
* @returns true if always-on-top is enabled
|
||||
*/
|
||||
getIsAlwaysOnTop(key: string): boolean;
|
||||
|
||||
/**
|
||||
* Checks if a popout window's document has focus.
|
||||
* @param key unique identifier for the popout window
|
||||
* @returns true if window document has focus
|
||||
*/
|
||||
getWindowFocused(key: string): boolean;
|
||||
|
||||
/**
|
||||
* Checks if a popout window is visible (not minimized/hidden).
|
||||
* Uses document.visibilityState === "visible".
|
||||
* @param key unique identifier for the popout window
|
||||
* @returns true if window is visible
|
||||
*/
|
||||
getWindowVisible(key: string): boolean;
|
||||
|
||||
/**
|
||||
* Gets all persisted window states.
|
||||
* Keyed by window identifier, contains position/size data.
|
||||
* @returns record of window key to persisted state
|
||||
*/
|
||||
getState(): Record<string, PopoutWindowState>;
|
||||
|
||||
/**
|
||||
* Checks if a window is fully initialized and ready for rendering.
|
||||
* A window is fully initialized when it has:
|
||||
* - Window object created
|
||||
* - React root mounted
|
||||
* - Render function stored
|
||||
* @param key unique identifier for the popout window
|
||||
* @returns true if window is fully initialized
|
||||
*/
|
||||
isWindowFullyInitialized(key: string): boolean;
|
||||
|
||||
/**
|
||||
* Checks if a popout window is in fullscreen mode.
|
||||
* Checks if document.fullscreenElement.id === "app-mount".
|
||||
* @param key unique identifier for the popout window
|
||||
* @returns true if window is fullscreen
|
||||
*/
|
||||
isWindowFullScreen(key: string): boolean;
|
||||
|
||||
/**
|
||||
* Unmounts and closes a popout window.
|
||||
* Saves current position/size before closing.
|
||||
* Logs warning if window was not fully initialized.
|
||||
* @param key unique identifier for the popout window
|
||||
*/
|
||||
unmountWindow(key: string): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Actions for managing popout windows.
|
||||
* Dispatches Flux actions to PopoutWindowStore.
|
||||
*/
|
||||
export interface PopoutActions {
|
||||
/**
|
||||
* Opens a new popout window.
|
||||
* If window with key already exists and is not out-of-process:
|
||||
* - On desktop: focuses the existing window via native module
|
||||
* - On web: calls window.focus()
|
||||
* @param key unique identifier for the popout window
|
||||
* @param render function that returns React element to render, receives key as arg
|
||||
* @param features window features (size, position, etc.)
|
||||
*/
|
||||
open(key: string, render: (key: string) => React.ReactNode, features?: BrowserWindowFeatures): void;
|
||||
|
||||
/**
|
||||
* Closes a popout window.
|
||||
* Saves position/size state before closing unless preventPopoutClose setting is true.
|
||||
* @param key unique identifier for the popout window
|
||||
*/
|
||||
close(key: string): void;
|
||||
|
||||
/**
|
||||
* Sets always-on-top state for a popout window.
|
||||
* Only functional on desktop app (isPlatformEmbedded).
|
||||
* @param key unique identifier for the popout window
|
||||
* @param alwaysOnTop whether window should stay above others
|
||||
*/
|
||||
setAlwaysOnTop(key: string, alwaysOnTop: boolean): void;
|
||||
|
||||
/**
|
||||
* Note: Not actually in the Webpack Common. You have to add it yourself if you want to use it
|
||||
*
|
||||
* Injects a stylesheet into all open popout windows.
|
||||
* Validates origin matches current host or webpack public path.
|
||||
* @param url stylesheet URL to inject
|
||||
* @param integrity optional SRI integrity hash
|
||||
*/
|
||||
addStylesheet?(url: string, integrity?: string): void;
|
||||
|
||||
/**
|
||||
* Note: Not actually in the Webpack Common. You have to add it yourself if you want to use it
|
||||
*
|
||||
* Opens a channel call popout for voice/video calls.
|
||||
* Dispatches CHANNEL_CALL_POPOUT_WINDOW_OPEN action.
|
||||
* @param channel channel object to open call popout for
|
||||
*/
|
||||
openChannelCallPopout?(channel: { id: string; }): void;
|
||||
|
||||
/**
|
||||
* Note: Not actually in the Webpack Common. You have to add it yourself if you want to use it
|
||||
*
|
||||
* Opens a call tile popout for a specific participant.
|
||||
* Dispatches CALL_TILE_POPOUT_WINDOW_OPEN action.
|
||||
* @param channelId channel ID of the call
|
||||
* @param participantId user ID of the participant
|
||||
*/
|
||||
openCallTilePopout?(channelId: string, participantId: string): void;
|
||||
}
|
||||
1
packages/discord-types/src/stores/index.d.ts
vendored
1
packages/discord-types/src/stores/index.d.ts
vendored
|
|
@ -24,6 +24,7 @@ export * from "./MessageStore";
|
|||
export * from "./NotificationSettingsStore";
|
||||
export * from "./OverridePremiumTypeStore";
|
||||
export * from "./PermissionStore";
|
||||
export * from "./PopoutWindowStore";
|
||||
export * from "./PresenceStore";
|
||||
export * from "./ReadStateStore";
|
||||
export * from "./RelationshipStore";
|
||||
|
|
|
|||
29
packages/discord-types/src/utils.d.ts
vendored
29
packages/discord-types/src/utils.d.ts
vendored
|
|
@ -232,34 +232,7 @@ export interface ExpressionPickerStore {
|
|||
useExpressionPickerStore<T>(selector: (state: ExpressionPickerStoreState) => T): T;
|
||||
}
|
||||
|
||||
export interface BrowserWindowFeatures {
|
||||
toolbar?: boolean;
|
||||
menubar?: boolean;
|
||||
location?: boolean;
|
||||
directories?: boolean;
|
||||
width?: number;
|
||||
height?: number;
|
||||
defaultWidth?: number;
|
||||
defaultHeight?: number;
|
||||
left?: number;
|
||||
top?: number;
|
||||
defaultAlwaysOnTop?: boolean;
|
||||
movable?: boolean;
|
||||
resizable?: boolean;
|
||||
frame?: boolean;
|
||||
alwaysOnTop?: boolean;
|
||||
hasShadow?: boolean;
|
||||
transparent?: boolean;
|
||||
skipTaskbar?: boolean;
|
||||
titleBarStyle?: string | null;
|
||||
backgroundColor?: string;
|
||||
}
|
||||
|
||||
export interface PopoutActions {
|
||||
open(key: string, render: (windowKey: string) => ReactNode, features?: BrowserWindowFeatures);
|
||||
close(key: string): void;
|
||||
setAlwaysOnTop(key: string, alwaysOnTop: boolean): void;
|
||||
}
|
||||
export { BrowserWindowFeatures, PopoutActions } from "./stores/PopoutWindowStore";
|
||||
|
||||
export type UserNameUtilsTagInclude = LiteralUnion<"auto" | "always" | "never", string>;
|
||||
export interface UserNameUtilsTagOptions {
|
||||
|
|
|
|||
|
|
@ -19,8 +19,9 @@
|
|||
import { Settings, SettingsStore } from "@api/Settings";
|
||||
import { createAndAppendStyle } from "@utils/css";
|
||||
import { ThemeStore } from "@vencord/discord-types";
|
||||
import { PopoutWindowStore } from "@webpack/common";
|
||||
|
||||
import { userStyleRootNode } from "./Styles";
|
||||
import { userStyleRootNode, vencordRootNode } from "./Styles";
|
||||
|
||||
let style: HTMLStyleElement;
|
||||
let themesStyle: HTMLStyleElement;
|
||||
|
|
@ -33,6 +34,7 @@ async function toggle(isEnabled: boolean) {
|
|||
style.textContent = css;
|
||||
// At the time of writing this, changing textContent resets the disabled state
|
||||
style.disabled = !Settings.useQuickCss;
|
||||
updatePopoutWindows();
|
||||
});
|
||||
style.textContent = await VencordNative.quickCss.get();
|
||||
}
|
||||
|
|
@ -76,6 +78,25 @@ async function initThemes() {
|
|||
}
|
||||
|
||||
themesStyle.textContent = links.map(link => `@import url("${link.trim()}");`).join("\n");
|
||||
updatePopoutWindows();
|
||||
}
|
||||
|
||||
function applyToPopout(popoutWindow: Window | undefined) {
|
||||
if (!popoutWindow?.document) return;
|
||||
|
||||
const doc = popoutWindow.document;
|
||||
|
||||
doc.querySelector("vencord-root")?.remove();
|
||||
|
||||
doc.documentElement.appendChild(vencordRootNode.cloneNode(true));
|
||||
}
|
||||
|
||||
function updatePopoutWindows() {
|
||||
const windowKeys = PopoutWindowStore.getWindowKeys();
|
||||
for (const key of windowKeys) {
|
||||
const popoutWindow = PopoutWindowStore.getWindow(key);
|
||||
applyToPopout(popoutWindow);
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
|
|
@ -89,6 +110,14 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||
SettingsStore.addChangeListener("enabledThemeLinks", initThemes);
|
||||
SettingsStore.addChangeListener("enabledThemes", initThemes);
|
||||
|
||||
window.addEventListener("message", event => {
|
||||
const { discordPopoutEvent } = event.data || {};
|
||||
if (discordPopoutEvent?.type !== "loaded") return;
|
||||
|
||||
const popoutWindow = PopoutWindowStore.getWindow(discordPopoutEvent.key);
|
||||
applyToPopout(popoutWindow);
|
||||
});
|
||||
|
||||
if (!IS_WEB) {
|
||||
VencordNative.quickCss.addThemeChangeListener(initThemes);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,7 +41,11 @@ export function BooleanSetting({ option, pluginSettings, definedSettings, id, on
|
|||
|
||||
return (
|
||||
<SettingsSection tag="label" name={id} description={option.description} error={error} inlineSetting>
|
||||
<Switch checked={state} onChange={handleChange} />
|
||||
<Switch
|
||||
checked={state}
|
||||
onChange={handleChange}
|
||||
disabled={option.disabled?.call(definedSettings) ?? false}
|
||||
/>
|
||||
</SettingsSection>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,9 @@
|
|||
*/
|
||||
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { managedStyleRootNode } from "@api/Styles";
|
||||
import { Devs } from "@utils/constants";
|
||||
import { createAndAppendStyle } from "@utils/css";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
|
||||
let style: HTMLStyleElement;
|
||||
|
|
@ -56,9 +58,7 @@ export default definePlugin({
|
|||
],
|
||||
|
||||
start() {
|
||||
style = document.createElement("style");
|
||||
style.id = "VcBlurNsfw";
|
||||
document.head.appendChild(style);
|
||||
style = createAndAppendStyle("VcBlurNsfw", managedStyleRootNode);
|
||||
|
||||
setCss();
|
||||
},
|
||||
|
|
|
|||
|
|
@ -4,10 +4,15 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { hexToHSL } from "./colorUtils";
|
||||
import { managedStyleRootNode } from "@api/Styles";
|
||||
import { createAndAppendStyle } from "@utils/css";
|
||||
|
||||
import { hexToHSL } from "./colorUtils";
|
||||
const VARS_STYLE_ID = "vc-clientTheme-vars";
|
||||
const OVERRIDES_STYLE_ID = "vc-clientTheme-overrides";
|
||||
type StyleId = typeof VARS_STYLE_ID | typeof OVERRIDES_STYLE_ID;
|
||||
|
||||
const styleCache = {} as Record<StyleId, HTMLStyleElement | null>;
|
||||
|
||||
export function createOrUpdateThemeColorVars(color: string) {
|
||||
const { hue, saturation, lightness } = hexToHSL(color);
|
||||
|
|
@ -25,23 +30,20 @@ export async function startClientTheme(color: string) {
|
|||
}
|
||||
|
||||
export function disableClientTheme() {
|
||||
document.getElementById(VARS_STYLE_ID)?.remove();
|
||||
document.getElementById(OVERRIDES_STYLE_ID)?.remove();
|
||||
styleCache[VARS_STYLE_ID]?.remove();
|
||||
styleCache[OVERRIDES_STYLE_ID]?.remove();
|
||||
styleCache[VARS_STYLE_ID] = null;
|
||||
styleCache[OVERRIDES_STYLE_ID] = null;
|
||||
}
|
||||
|
||||
function getOrCreateStyle(styleId: string) {
|
||||
const existingStyle = document.getElementById(styleId);
|
||||
if (existingStyle) {
|
||||
return existingStyle as HTMLStyleElement;
|
||||
function getOrCreateStyle(styleId: StyleId) {
|
||||
if (!styleCache[styleId]) {
|
||||
styleCache[styleId] = createAndAppendStyle(styleId, managedStyleRootNode);
|
||||
}
|
||||
|
||||
const newStyle = document.createElement("style");
|
||||
newStyle.id = styleId;
|
||||
|
||||
return document.head.appendChild(newStyle);
|
||||
return styleCache[styleId];
|
||||
}
|
||||
|
||||
function createOrUpdateStyle(styleId: string, css: string) {
|
||||
function createOrUpdateStyle(styleId: StyleId, css: string) {
|
||||
const style = getOrCreateStyle(styleId);
|
||||
style.textContent = css;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ function getGuildMaxStickerSlots(guild: Guild) {
|
|||
|
||||
function getUrl(data: Data, size: number) {
|
||||
if (data.t === "Emoji")
|
||||
return `${location.protocol}//${window.GLOBAL_ENV.CDN_HOST}/emojis/${data.id}.${data.isAnimated ? "gif" : "png"}?size=${size}&lossless=true`;
|
||||
return `${location.protocol}//${window.GLOBAL_ENV.CDN_HOST}/emojis/${data.id}.webp?size=${size}&lossless=true`;
|
||||
|
||||
return `${window.GLOBAL_ENV.MEDIA_PROXY_ENDPOINT}/stickers/${data.id}.${StickerExtMap[data.format_type]}?size=${size}&lossless=true`;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,13 +21,13 @@ import { definePluginSettings } from "@api/Settings";
|
|||
import { Paragraph } from "@components/Paragraph";
|
||||
import { ApngBlendOp, ApngDisposeOp, parseAPNG } from "@utils/apng";
|
||||
import { Devs } from "@utils/constants";
|
||||
import { getCurrentGuild, getEmojiURL } from "@utils/discord";
|
||||
import { getCurrentGuild } from "@utils/discord";
|
||||
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, proxyLazyWebpack } from "@webpack";
|
||||
import { Alerts, ChannelStore, DraftType, EmojiStore, FluxDispatcher, GuildMemberStore, lodash, OverridePremiumTypeStore, Parser, PermissionsBits, PermissionStore, StickersStore, UploadHandler, UserSettingsActionCreators, UserSettingsProtoStore } from "@webpack/common";
|
||||
import { Alerts, ChannelStore, DraftType, EmojiStore, FluxDispatcher, Forms, GuildMemberStore, IconUtils, lodash, OverridePremiumTypeStore, Parser, PermissionsBits, PermissionStore, StickersStore, UploadHandler, UserSettingsActionCreators, UserSettingsProtoStore, UserStore } from "@webpack/common";
|
||||
import { applyPalette, GIFEncoder, quantize } from "gifenc";
|
||||
import type { ReactElement, ReactNode } from "react";
|
||||
|
||||
|
|
@ -883,7 +883,7 @@ export default definePlugin({
|
|||
|
||||
const emojiString = `<${emoji.animated ? "a" : ""}:${emoji.originalName || emoji.name}:${emoji.id}>`;
|
||||
|
||||
const url = new URL(getEmojiURL(emoji.id, emoji.animated, s.emojiSize));
|
||||
const url = new URL(IconUtils.getEmojiURL({ id: emoji.id, animated: emoji.animated, size: s.emojiSize }));
|
||||
url.searchParams.set("size", s.emojiSize.toString());
|
||||
url.searchParams.set("name", emoji.name);
|
||||
if (emoji.animated) {
|
||||
|
|
@ -919,7 +919,7 @@ export default definePlugin({
|
|||
|
||||
hasBypass = true;
|
||||
|
||||
const url = new URL(getEmojiURL(emoji.id, emoji.animated, s.emojiSize));
|
||||
const url = new URL(IconUtils.getEmojiURL({ id: emoji.id, animated: emoji.animated, size: s.emojiSize }));
|
||||
url.searchParams.set("size", s.emojiSize.toString());
|
||||
url.searchParams.set("name", emoji.name);
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ import { useForceUpdater } from "@utils/react";
|
|||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { CustomEmoji, Message, ReactionEmoji, User } from "@vencord/discord-types";
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
import { ChannelStore, Constants, FluxDispatcher, React, RestAPI, useEffect, useLayoutEffect, UserSummaryItem } from "@webpack/common";
|
||||
import { ChannelStore, Constants, FluxDispatcher, React, RestAPI, useEffect, useLayoutEffect, UserStore, UserSummaryItem } from "@webpack/common";
|
||||
|
||||
const AvatarStyles = findByPropsLazy("moreUsers", "emptyUser", "avatarContainer", "clickableAvatar");
|
||||
let Scroll: any = null;
|
||||
|
|
@ -150,7 +150,7 @@ export default definePlugin({
|
|||
}, [message.id, forceUpdate]);
|
||||
|
||||
const reactions = getReactionsWithQueue(message, emoji, type);
|
||||
const users = [...reactions.values()].filter(Boolean);
|
||||
const users = Array.from(reactions, ([id]) => UserStore.getUser(id)).filter(Boolean);
|
||||
|
||||
return (
|
||||
<div
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
import type { MessageObject } from "@api/MessageEvents";
|
||||
import type { Channel, CloudUpload, Guild, GuildFeatures, Message, User } from "@vencord/discord-types";
|
||||
import { ChannelActionCreators, ChannelStore, ComponentDispatch, Constants, FluxDispatcher, GuildStore, i18n, IconUtils, InviteActions, MessageActions, RestAPI, SelectedChannelStore, SelectedGuildStore, Toasts, UserProfileActions, UserProfileStore, UserSettingsActionCreators, UserUtils } from "@webpack/common";
|
||||
import { ChannelActionCreators, ChannelStore, ComponentDispatch, Constants, FluxDispatcher, GuildStore, i18n, InviteActions, MessageActions, RestAPI, SelectedChannelStore, SelectedGuildStore, Toasts, UserProfileActions, UserProfileStore, UserSettingsActionCreators, UserUtils } from "@webpack/common";
|
||||
import { Except } from "type-fest";
|
||||
|
||||
import { copyToClipboard } from "./clipboard";
|
||||
|
|
@ -236,17 +236,6 @@ export function getUniqueUsername(user: User) {
|
|||
return user.discriminator === "0" ? user.username : user.tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the URL for an emoji. This function always returns a gif URL for animated emojis, instead of webp
|
||||
* @param id The emoji id
|
||||
* @param animated Whether the emoji is animated
|
||||
* @param size The size for the emoji
|
||||
*/
|
||||
export function getEmojiURL(id: string, animated: boolean, size: number) {
|
||||
const url = IconUtils.getEmojiURL({ id, animated, size });
|
||||
return animated ? url.replace(".webp", ".gif") : url;
|
||||
}
|
||||
|
||||
// Discord has a similar function in their code
|
||||
export function getGuildAcronym(guild: Guild): string {
|
||||
return guild.name
|
||||
|
|
|
|||
|
|
@ -78,6 +78,7 @@ export let InviteStore: t.InviteStore;
|
|||
export let LocaleStore: t.LocaleStore;
|
||||
export let RTCConnectionStore: t.RTCConnectionStore;
|
||||
export let SoundboardStore: t.SoundboardStore;
|
||||
export let PopoutWindowStore: t.PopoutWindowStore;
|
||||
export let ApplicationCommandIndexStore: GenericStore;
|
||||
|
||||
/**
|
||||
|
|
@ -129,6 +130,7 @@ waitForStore("InviteStore", m => InviteStore = m);
|
|||
waitForStore("LocaleStore", m => LocaleStore = m);
|
||||
waitForStore("RTCConnectionStore", m => RTCConnectionStore = m);
|
||||
waitForStore("SoundboardStore", m => SoundboardStore = m);
|
||||
waitForStore("PopoutWindowStore", m => PopoutWindowStore = m);
|
||||
waitForStore("ApplicationCommandIndexStore", m => ApplicationCommandIndexStore = m);
|
||||
waitForStore("ThemeStore", m => {
|
||||
ThemeStore = m;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue