Compare commits
No commits in common. "32182c484db451fb0a534e86295ba94f591d39e5" and "6cfd2801c54f9a88a98ea2c2e1a225e46f63f057" have entirely different histories.
32182c484d
...
6cfd2801c5
37 changed files with 524 additions and 331 deletions
|
@ -5,7 +5,6 @@
|
||||||
// @author Vendicated (https://github.com/Vendicated)
|
// @author Vendicated (https://github.com/Vendicated)
|
||||||
// @namespace https://github.com/Vendicated/Vencord
|
// @namespace https://github.com/Vendicated/Vencord
|
||||||
// @supportURL https://github.com/Vendicated/Vencord
|
// @supportURL https://github.com/Vendicated/Vencord
|
||||||
// @icon https://raw.githubusercontent.com/Vendicated/Vencord/refs/heads/main/browser/icon.png
|
|
||||||
// @license GPL-3.0
|
// @license GPL-3.0
|
||||||
// @match *://*.discord.com/*
|
// @match *://*.discord.com/*
|
||||||
// @grant GM_xmlhttpRequest
|
// @grant GM_xmlhttpRequest
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "vencord",
|
"name": "vencord",
|
||||||
"private": "true",
|
"private": "true",
|
||||||
"version": "1.10.5",
|
"version": "1.10.4",
|
||||||
"description": "The cutest Discord client mod",
|
"description": "The cutest Discord client mod",
|
||||||
"homepage": "https://github.com/Vendicated/Vencord#readme",
|
"homepage": "https://github.com/Vendicated/Vencord#readme",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { onceDefined } from "@shared/onceDefined";
|
import { onceDefined } from "@shared/onceDefined";
|
||||||
import electron, { app, BrowserWindowConstructorOptions, Menu, nativeTheme } from "electron";
|
import electron, { app, BrowserWindowConstructorOptions, Menu } from "electron";
|
||||||
import { dirname, join } from "path";
|
import { dirname, join } from "path";
|
||||||
|
|
||||||
import { initIpc } from "./ipcMain";
|
import { initIpc } from "./ipcMain";
|
||||||
|
@ -100,19 +100,6 @@ if (!IS_VANILLA) {
|
||||||
|
|
||||||
super(options);
|
super(options);
|
||||||
initIpc(this);
|
initIpc(this);
|
||||||
|
|
||||||
// Workaround for https://github.com/electron/electron/issues/43367. Vesktop also has its own workaround
|
|
||||||
// @TODO: Remove this when the issue is fixed
|
|
||||||
if (IS_DISCORD_DESKTOP) {
|
|
||||||
this.webContents.on("devtools-opened", () => {
|
|
||||||
if (!nativeTheme.shouldUseDarkColors) return;
|
|
||||||
|
|
||||||
nativeTheme.themeSource = "light";
|
|
||||||
setTimeout(() => {
|
|
||||||
nativeTheme.themeSource = "dark";
|
|
||||||
}, 100);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else super(options);
|
} else super(options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
/*
|
|
||||||
* Vencord, a Discord client mod
|
|
||||||
* Copyright (c) 2024 Vendicated and contributors
|
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { Devs } from "@utils/constants";
|
|
||||||
import definePlugin from "@utils/types";
|
|
||||||
|
|
||||||
|
|
||||||
export default definePlugin({
|
|
||||||
name: "DynamicImageModalAPI",
|
|
||||||
authors: [Devs.sadan, Devs.Nuckyz],
|
|
||||||
description: "Allows you to omit either width or height when opening an image modal",
|
|
||||||
patches: [
|
|
||||||
{
|
|
||||||
find: "SCALE_DOWN:",
|
|
||||||
replacement: {
|
|
||||||
match: /!\(null==(\i)\|\|0===\i\|\|null==(\i)\|\|0===\i\)/,
|
|
||||||
replace: (_, width, height) => `!((null == ${width} || 0 === ${width}) && (null == ${height} || 0 === ${height}))`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
|
@ -197,7 +197,7 @@ export default definePlugin({
|
||||||
},
|
},
|
||||||
|
|
||||||
get electronVersion() {
|
get electronVersion() {
|
||||||
return VencordNative.native.getVersions().electron || window.legcord?.electron || null;
|
return VencordNative.native.getVersions().electron || window.armcord?.electron || null;
|
||||||
},
|
},
|
||||||
|
|
||||||
get chromiumVersion() {
|
get chromiumVersion() {
|
||||||
|
|
|
@ -77,7 +77,7 @@ async function generateDebugInfoMessage() {
|
||||||
const client = (() => {
|
const client = (() => {
|
||||||
if (IS_DISCORD_DESKTOP) return `Discord Desktop v${DiscordNative.app.getVersion()}`;
|
if (IS_DISCORD_DESKTOP) return `Discord Desktop v${DiscordNative.app.getVersion()}`;
|
||||||
if (IS_VESKTOP) return `Vesktop v${VesktopNative.app.getVersion()}`;
|
if (IS_VESKTOP) return `Vesktop v${VesktopNative.app.getVersion()}`;
|
||||||
if ("legcord" in window) return `Legcord v${window.legcord.version}`;
|
if ("armcord" in window) return `ArmCord v${window.armcord.version}`;
|
||||||
|
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
const name = typeof unsafeWindow !== "undefined" ? "UserScript" : "Web";
|
const name = typeof unsafeWindow !== "undefined" ? "UserScript" : "Web";
|
||||||
|
@ -149,8 +149,8 @@ export default definePlugin({
|
||||||
patches: [{
|
patches: [{
|
||||||
find: ".BEGINNING_DM.format",
|
find: ".BEGINNING_DM.format",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /BEGINNING_DM\.format\(\{.+?\}\),(?=.{0,300}(\i)\.isMultiUserDM)/,
|
match: /BEGINNING_DM\.format\(\{.+?\}\),(?=.{0,100}userId:(\i\.getRecipientId\(\)))/,
|
||||||
replace: "$& $self.renderContributorDmWarningCard({ channel: $1 }),"
|
replace: "$& $self.ContributorDmWarningCard({ userId: $1 }),"
|
||||||
}
|
}
|
||||||
}],
|
}],
|
||||||
|
|
||||||
|
@ -235,8 +235,7 @@ export default definePlugin({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
renderContributorDmWarningCard: ErrorBoundary.wrap(({ channel }) => {
|
ContributorDmWarningCard: ErrorBoundary.wrap(({ userId }) => {
|
||||||
const userId = channel.getRecipientId();
|
|
||||||
if (!isPluginDev(userId)) return null;
|
if (!isPluginDev(userId)) return null;
|
||||||
if (RelationshipStore.isFriend(userId) || isPluginDev(UserStore.getCurrentUser()?.id)) return null;
|
if (RelationshipStore.isFriend(userId) || isPluginDev(UserStore.getCurrentUser()?.id)) return null;
|
||||||
|
|
||||||
|
|
|
@ -73,8 +73,8 @@ export default definePlugin({
|
||||||
},
|
},
|
||||||
|
|
||||||
async start() {
|
async start() {
|
||||||
// Legcord comes with its own arRPC implementation, so this plugin just confuses users
|
// ArmCord comes with its own arRPC implementation, so this plugin just confuses users
|
||||||
if ("legcord" in window) return;
|
if ("armcord" in window) return;
|
||||||
|
|
||||||
if (ws) ws.close();
|
if (ws) ws.close();
|
||||||
ws = new WebSocket("ws://127.0.0.1:1337"); // try to open WebSocket
|
ws = new WebSocket("ws://127.0.0.1:1337"); // try to open WebSocket
|
||||||
|
|
|
@ -99,11 +99,7 @@ export default definePlugin({
|
||||||
id="vc-view-role-icon"
|
id="vc-view-role-icon"
|
||||||
label="View Role Icon"
|
label="View Role Icon"
|
||||||
action={() => {
|
action={() => {
|
||||||
openImageModal({
|
openImageModal(`${location.protocol}//${window.GLOBAL_ENV.CDN_HOST}/role-icons/${role.id}/${role.icon}.${settings.store.roleIconFileFormat}`);
|
||||||
url: `${location.protocol}//${window.GLOBAL_ENV.CDN_HOST}/role-icons/${role.id}/${role.icon}.${settings.store.roleIconFileFormat}`,
|
|
||||||
height: 128,
|
|
||||||
width: 128
|
|
||||||
});
|
|
||||||
}}
|
}}
|
||||||
icon={ImageIcon}
|
icon={ImageIcon}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -57,11 +57,7 @@ export const handleViewPreview = async ({ guildId, channelId, ownerId }: Applica
|
||||||
const previewUrl = await ApplicationStreamPreviewStore.getPreviewURL(guildId, channelId, ownerId);
|
const previewUrl = await ApplicationStreamPreviewStore.getPreviewURL(guildId, channelId, ownerId);
|
||||||
if (!previewUrl) return;
|
if (!previewUrl) return;
|
||||||
|
|
||||||
openImageModal({
|
openImageModal(previewUrl);
|
||||||
url: previewUrl,
|
|
||||||
height: 720,
|
|
||||||
width: 1280
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const addViewStreamContext: NavContextMenuPatchCallback = (children, { userId }: { userId: string | bigint; }) => {
|
export const addViewStreamContext: NavContextMenuPatchCallback = (children, { userId }: { userId: string | bigint; }) => {
|
||||||
|
|
|
@ -350,7 +350,7 @@ export default definePlugin({
|
||||||
{
|
{
|
||||||
// Filter attachments to remove fake nitro stickers or emojis
|
// Filter attachments to remove fake nitro stickers or emojis
|
||||||
predicate: () => settings.store.transformStickers,
|
predicate: () => settings.store.transformStickers,
|
||||||
match: /renderAttachments\(\i\){.+?{attachments:(\i).+?;/,
|
match: /renderAttachments\(\i\){let{attachments:(\i).+?;/,
|
||||||
replace: (m, attachments) => `${m}${attachments}=$self.filterAttachments(${attachments});`
|
replace: (m, attachments) => `${m}${attachments}=$self.filterAttachments(${attachments});`
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
# Fix Images Quality
|
|
||||||
|
|
||||||
Prevents images from being loaded as webp, which can cause quality loss
|
|
|
@ -1,23 +0,0 @@
|
||||||
/*
|
|
||||||
* Vencord, a Discord client mod
|
|
||||||
* Copyright (c) 2024 Vendicated and contributors
|
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { Devs } from "@utils/constants";
|
|
||||||
import definePlugin from "@utils/types";
|
|
||||||
|
|
||||||
export default definePlugin({
|
|
||||||
name: "FixImagesQuality",
|
|
||||||
description: "Prevents images from being loaded as webp, which can cause quality loss",
|
|
||||||
authors: [Devs.Nuckyz],
|
|
||||||
patches: [
|
|
||||||
{
|
|
||||||
find: "getFormatQuality(){",
|
|
||||||
replacement: {
|
|
||||||
match: /(?<=null;return )\i\.\i&&\(\i\|\|!\i\.isAnimated.+?:(?=\i&&\(\i="png"\))/,
|
|
||||||
replace: ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
|
@ -156,10 +156,14 @@ export default definePlugin({
|
||||||
|
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
find: ".contain,SCALE_DOWN:",
|
find: "Messages.OPEN_IN_BROWSER",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /\.slide,\i\),/g,
|
// there are 2 image thingies. one for carosuel and one for the single image.
|
||||||
replace: `$&id:"${ELEMENT_ID}",`
|
// so thats why i added global flag.
|
||||||
|
// also idk if this patch is good, should it be more specific?
|
||||||
|
// https://regex101.com/r/xfvNvV/1
|
||||||
|
match: /return.{1,200}\.wrapper.{1,200}src:\i,/g,
|
||||||
|
replace: `$&id: '${ELEMENT_ID}',`
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -181,6 +185,13 @@ export default definePlugin({
|
||||||
replace: "$&$self.unMountMagnifier();"
|
replace: "$&$self.unMountMagnifier();"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
find: ".carouselModal",
|
||||||
|
replacement: {
|
||||||
|
match: /(?<=\.carouselModal.{0,100}onClick:)\i,/,
|
||||||
|
replace: "()=>{},"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|
|
@ -23,3 +23,12 @@
|
||||||
|
|
||||||
/* https://googlechrome.github.io/samples/image-rendering-pixelated/index.html */
|
/* https://googlechrome.github.io/samples/image-rendering-pixelated/index.html */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* make the carousel take up less space so we can click the backdrop and exit out of it */
|
||||||
|
[class*="modalCarouselWrapper_"] {
|
||||||
|
top: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
[class*="carouselModal_"] {
|
||||||
|
height: 0 !important;
|
||||||
|
}
|
||||||
|
|
|
@ -74,7 +74,7 @@ export default definePlugin({
|
||||||
if (msg.deleted === true) return;
|
if (msg.deleted === true) return;
|
||||||
|
|
||||||
if (isMe) {
|
if (isMe) {
|
||||||
if (!settings.store.enableDoubleClickToEdit || EditStore.isEditing(channel.id, msg.id) || msg.state !== "SENT") return;
|
if (!settings.store.enableDoubleClickToEdit || EditStore.isEditing(channel.id, msg.id)) return;
|
||||||
|
|
||||||
MessageActions.startEditMessage(channel.id, msg.id, msg.content);
|
MessageActions.startEditMessage(channel.id, msg.id, msg.content);
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
|
@ -28,7 +28,7 @@ const SelectedChannelActionCreators = findByPropsLazy("selectPrivateChannel");
|
||||||
const UserUtils = findByPropsLazy("getGlobalName");
|
const UserUtils = findByPropsLazy("getGlobalName");
|
||||||
|
|
||||||
const ProfileListClasses = findByPropsLazy("emptyIconFriends", "emptyIconGuilds");
|
const ProfileListClasses = findByPropsLazy("emptyIconFriends", "emptyIconGuilds");
|
||||||
const ExpandableList = findComponentByCodeLazy('"PRESS_SECTION"');
|
const ExpandableList = findComponentByCodeLazy(".mutualFriendItem]");
|
||||||
const GuildLabelClasses = findByPropsLazy("guildNick", "guildAvatarWithoutIcon");
|
const GuildLabelClasses = findByPropsLazy("guildNick", "guildAvatarWithoutIcon");
|
||||||
|
|
||||||
function getGroupDMName(channel: Channel) {
|
function getGroupDMName(channel: Channel) {
|
||||||
|
@ -142,15 +142,16 @@ export default definePlugin({
|
||||||
const mutualGDms = getMutualGroupDms(user.id);
|
const mutualGDms = getMutualGroupDms(user.id);
|
||||||
if (mutualGDms.length === 0) return null;
|
if (mutualGDms.length === 0) return null;
|
||||||
|
|
||||||
|
const header = getMutualGDMCountText(user);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{Divider}
|
{Divider}
|
||||||
<ExpandableList
|
<ExpandableList
|
||||||
listClassName={listStyle}
|
className={listStyle}
|
||||||
header={"Mutual Groups"}
|
header={header}
|
||||||
isLoading={false}
|
isLoadingHeader={false}
|
||||||
items={renderClickableGDMs(mutualGDms, () => { })}
|
children={renderClickableGDMs(mutualGDms, () => { })}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
@ -91,6 +91,15 @@ export default definePlugin({
|
||||||
replace: "async function $1 if(await $self.handleLink(...arguments)) return;"
|
replace: "async function $1 if(await $self.handleLink(...arguments)) return;"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
// Make Spotify profile activity links open in app on web
|
||||||
|
{
|
||||||
|
find: "WEB_OPEN(",
|
||||||
|
predicate: () => !IS_DISCORD_DESKTOP && pluginSettings.store.spotify,
|
||||||
|
replacement: {
|
||||||
|
match: /\i\.\i\.isProtocolRegistered\(\)(.{0,100})window.open/g,
|
||||||
|
replace: "true$1VencordNative.native.openExternal"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
find: "no artist ids in metadata",
|
find: "no artist ids in metadata",
|
||||||
predicate: () => !IS_DISCORD_DESKTOP && pluginSettings.store.spotify,
|
predicate: () => !IS_DISCORD_DESKTOP && pluginSettings.store.spotify,
|
||||||
|
|
172
src/plugins/pronoundb/api.ts
Normal file
172
src/plugins/pronoundb/api.ts
Normal file
|
@ -0,0 +1,172 @@
|
||||||
|
/*
|
||||||
|
* Vencord, a modification for Discord's desktop app
|
||||||
|
* Copyright (c) 2022 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 { getCurrentChannel } from "@utils/discord";
|
||||||
|
import { useAwaiter } from "@utils/react";
|
||||||
|
import { findStoreLazy } from "@webpack";
|
||||||
|
import { UserProfileStore, UserStore } from "@webpack/common";
|
||||||
|
|
||||||
|
import { settings } from "./settings";
|
||||||
|
import { PronounMapping, Pronouns, PronounsCache, PronounSets, PronounsFormat, PronounSource, PronounsResponse } from "./types";
|
||||||
|
|
||||||
|
const UserSettingsAccountStore = findStoreLazy("UserSettingsAccountStore");
|
||||||
|
|
||||||
|
const EmptyPronouns = { pronouns: undefined, source: "", hasPendingPronouns: false } as const satisfies Pronouns;
|
||||||
|
|
||||||
|
type RequestCallback = (pronounSets?: PronounSets) => void;
|
||||||
|
|
||||||
|
const pronounCache: Record<string, PronounsCache> = {};
|
||||||
|
const requestQueue: Record<string, RequestCallback[]> = {};
|
||||||
|
let isProcessing = false;
|
||||||
|
|
||||||
|
async function processQueue() {
|
||||||
|
if (isProcessing) return;
|
||||||
|
isProcessing = true;
|
||||||
|
|
||||||
|
let ids = Object.keys(requestQueue);
|
||||||
|
while (ids.length > 0) {
|
||||||
|
const idsChunk = ids.splice(0, 50);
|
||||||
|
const pronouns = await bulkFetchPronouns(idsChunk);
|
||||||
|
|
||||||
|
for (const id of idsChunk) {
|
||||||
|
const callbacks = requestQueue[id];
|
||||||
|
for (const callback of callbacks) {
|
||||||
|
callback(pronouns[id]?.sets);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete requestQueue[id];
|
||||||
|
}
|
||||||
|
|
||||||
|
ids = Object.keys(requestQueue);
|
||||||
|
await new Promise(r => setTimeout(r, 2000));
|
||||||
|
}
|
||||||
|
|
||||||
|
isProcessing = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function fetchPronouns(id: string): Promise<string | undefined> {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
if (pronounCache[id] != null) {
|
||||||
|
resolve(extractPronouns(pronounCache[id].sets));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handlePronouns(pronounSets?: PronounSets) {
|
||||||
|
const pronouns = extractPronouns(pronounSets);
|
||||||
|
resolve(pronouns);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (requestQueue[id] != null) {
|
||||||
|
requestQueue[id].push(handlePronouns);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
requestQueue[id] = [handlePronouns];
|
||||||
|
processQueue();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function bulkFetchPronouns(ids: string[]): Promise<PronounsResponse> {
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
params.append("platform", "discord");
|
||||||
|
params.append("ids", ids.join(","));
|
||||||
|
|
||||||
|
try {
|
||||||
|
const req = await fetch("https://pronoundb.org/api/v2/lookup?" + String(params), {
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
"Accept": "application/json",
|
||||||
|
"X-PronounDB-Source": "WebExtension/0.14.5"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!req.ok) throw new Error(`Status ${req.status}`);
|
||||||
|
const res: PronounsResponse = await req.json();
|
||||||
|
|
||||||
|
Object.assign(pronounCache, res);
|
||||||
|
return res;
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
console.error("PronounDB request failed:", e);
|
||||||
|
const dummyPronouns: PronounsResponse = Object.fromEntries(ids.map(id => [id, { sets: {} }]));
|
||||||
|
|
||||||
|
Object.assign(pronounCache, dummyPronouns);
|
||||||
|
return dummyPronouns;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function extractPronouns(pronounSets?: PronounSets): string | undefined {
|
||||||
|
if (pronounSets == null) return undefined;
|
||||||
|
if (pronounSets.en == null) return PronounMapping.unspecified;
|
||||||
|
|
||||||
|
const pronouns = pronounSets.en;
|
||||||
|
if (pronouns.length === 0) return PronounMapping.unspecified;
|
||||||
|
|
||||||
|
const { pronounsFormat } = settings.store;
|
||||||
|
|
||||||
|
if (pronouns.length > 1) {
|
||||||
|
const pronounString = pronouns.map(p => p[0].toUpperCase() + p.slice(1)).join("/");
|
||||||
|
return pronounsFormat === PronounsFormat.Capitalized ? pronounString : pronounString.toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
const pronoun = pronouns[0];
|
||||||
|
// For capitalized pronouns or special codes (any, ask, avoid), we always return the normal (capitalized) string
|
||||||
|
if (pronounsFormat === PronounsFormat.Capitalized || ["any", "ask", "avoid", "other", "unspecified"].includes(pronoun)) {
|
||||||
|
return PronounMapping[pronoun];
|
||||||
|
} else {
|
||||||
|
return PronounMapping[pronoun].toLowerCase();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDiscordPronouns(id: string, useGlobalProfile: boolean = false): string | undefined {
|
||||||
|
const globalPronouns = UserProfileStore.getUserProfile(id)?.pronouns;
|
||||||
|
if (useGlobalProfile) return globalPronouns;
|
||||||
|
|
||||||
|
return UserProfileStore.getGuildMemberProfile(id, getCurrentChannel()?.guild_id)?.pronouns || globalPronouns;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useFormattedPronouns(id: string, useGlobalProfile: boolean = false): Pronouns {
|
||||||
|
const discordPronouns = getDiscordPronouns(id, useGlobalProfile)?.trim().replace(/\n+/g, "");
|
||||||
|
const hasPendingPronouns = UserSettingsAccountStore.getPendingPronouns() != null;
|
||||||
|
|
||||||
|
const [pronouns] = useAwaiter(() => fetchPronouns(id));
|
||||||
|
|
||||||
|
if (settings.store.pronounSource === PronounSource.PreferDiscord && discordPronouns) {
|
||||||
|
return { pronouns: discordPronouns, source: "Discord", hasPendingPronouns };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pronouns != null && pronouns !== PronounMapping.unspecified) {
|
||||||
|
return { pronouns, source: "PronounDB", hasPendingPronouns };
|
||||||
|
}
|
||||||
|
|
||||||
|
return { pronouns: discordPronouns, source: "Discord", hasPendingPronouns };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useProfilePronouns(id: string, useGlobalProfile: boolean = false): Pronouns {
|
||||||
|
try {
|
||||||
|
const pronouns = useFormattedPronouns(id, useGlobalProfile);
|
||||||
|
|
||||||
|
if (!settings.store.showInProfile) return EmptyPronouns;
|
||||||
|
if (!settings.store.showSelf && id === UserStore.getCurrentUser()?.id) return EmptyPronouns;
|
||||||
|
|
||||||
|
return pronouns;
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
return EmptyPronouns;
|
||||||
|
}
|
||||||
|
}
|
41
src/plugins/pronoundb/components/PronounsAboutComponent.tsx
Normal file
41
src/plugins/pronoundb/components/PronounsAboutComponent.tsx
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* Vencord, a modification for Discord's desktop app
|
||||||
|
* Copyright (c) 2022 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 { Link } from "@components/Link";
|
||||||
|
import { Forms, React } from "@webpack/common";
|
||||||
|
|
||||||
|
export default function PronounsAboutComponent() {
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<Forms.FormTitle tag="h3">More Information</Forms.FormTitle>
|
||||||
|
<Forms.FormText>To add your own pronouns, visit{" "}
|
||||||
|
<Link href="https://pronoundb.org">pronoundb.org</Link>
|
||||||
|
</Forms.FormText>
|
||||||
|
<Forms.FormDivider />
|
||||||
|
<Forms.FormText>
|
||||||
|
The two pronoun formats are lowercase and capitalized. Example:
|
||||||
|
<ul>
|
||||||
|
<li>Lowercase: they/them</li>
|
||||||
|
<li>Capitalized: They/Them</li>
|
||||||
|
</ul>
|
||||||
|
Text like "Ask me my pronouns" or "Any pronouns" will always be capitalized. <br /><br />
|
||||||
|
You can also configure whether or not to display pronouns for the current user (since you probably already know them)
|
||||||
|
</Forms.FormText>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
|
@ -16,22 +16,22 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { getUserSettingLazy } from "@api/UserSettings";
|
|
||||||
import ErrorBoundary from "@components/ErrorBoundary";
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { classes } from "@utils/misc";
|
import { classes } from "@utils/misc";
|
||||||
import { findByPropsLazy } from "@webpack";
|
import { findByPropsLazy } from "@webpack";
|
||||||
import { i18n, Tooltip, UserStore } from "@webpack/common";
|
import { UserStore } from "@webpack/common";
|
||||||
import { Message } from "discord-types/general";
|
import { Message } from "discord-types/general";
|
||||||
|
|
||||||
import { settings } from "./settings";
|
import { useFormattedPronouns } from "../api";
|
||||||
import { useFormattedPronouns } from "./utils";
|
import { settings } from "../settings";
|
||||||
|
|
||||||
const styles: Record<string, string> = findByPropsLazy("timestampInline");
|
const styles: Record<string, string> = findByPropsLazy("timestampInline");
|
||||||
const MessageDisplayCompact = getUserSettingLazy("textAndImages", "messageDisplayCompact")!;
|
|
||||||
|
|
||||||
const AUTO_MODERATION_ACTION = 24;
|
const AUTO_MODERATION_ACTION = 24;
|
||||||
|
|
||||||
function shouldShow(message: Message): boolean {
|
function shouldShow(message: Message): boolean {
|
||||||
|
if (!settings.store.showInMessages)
|
||||||
|
return false;
|
||||||
if (message.author.bot || message.author.system || message.type === AUTO_MODERATION_ACTION)
|
if (message.author.bot || message.author.system || message.type === AUTO_MODERATION_ACTION)
|
||||||
return false;
|
return false;
|
||||||
if (!settings.store.showSelf && message.author.id === UserStore.getCurrentUser().id)
|
if (!settings.store.showSelf && message.author.id === UserStore.getCurrentUser().id)
|
||||||
|
@ -40,21 +40,6 @@ function shouldShow(message: Message): boolean {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function PronounsChatComponent({ message }: { message: Message; }) {
|
|
||||||
const pronouns = useFormattedPronouns(message.author.id);
|
|
||||||
|
|
||||||
return pronouns && (
|
|
||||||
<Tooltip text={i18n.Messages.USER_PROFILE_PRONOUNS}>
|
|
||||||
{tooltipProps => (
|
|
||||||
<span
|
|
||||||
{...tooltipProps}
|
|
||||||
className={classes(styles.timestampInline, styles.timestamp)}
|
|
||||||
>• {pronouns}</span>
|
|
||||||
)}
|
|
||||||
</Tooltip>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export const PronounsChatComponentWrapper = ErrorBoundary.wrap(({ message }: { message: Message; }) => {
|
export const PronounsChatComponentWrapper = ErrorBoundary.wrap(({ message }: { message: Message; }) => {
|
||||||
return shouldShow(message)
|
return shouldShow(message)
|
||||||
? <PronounsChatComponent message={message} />
|
? <PronounsChatComponent message={message} />
|
||||||
|
@ -62,11 +47,27 @@ export const PronounsChatComponentWrapper = ErrorBoundary.wrap(({ message }: { m
|
||||||
}, { noop: true });
|
}, { noop: true });
|
||||||
|
|
||||||
export const CompactPronounsChatComponentWrapper = ErrorBoundary.wrap(({ message }: { message: Message; }) => {
|
export const CompactPronounsChatComponentWrapper = ErrorBoundary.wrap(({ message }: { message: Message; }) => {
|
||||||
const compact = MessageDisplayCompact.useSetting();
|
return shouldShow(message)
|
||||||
|
? <CompactPronounsChatComponent message={message} />
|
||||||
|
: null;
|
||||||
|
}, { noop: true });
|
||||||
|
|
||||||
if (!compact || !shouldShow(message)) {
|
function PronounsChatComponent({ message }: { message: Message; }) {
|
||||||
return null;
|
const { pronouns } = useFormattedPronouns(message.author.id);
|
||||||
|
|
||||||
|
return pronouns && (
|
||||||
|
<span
|
||||||
|
className={classes(styles.timestampInline, styles.timestamp)}
|
||||||
|
>• {pronouns}</span>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return <PronounsChatComponent message={message} />;
|
export const CompactPronounsChatComponent = ErrorBoundary.wrap(({ message }: { message: Message; }) => {
|
||||||
|
const { pronouns } = useFormattedPronouns(message.author.id);
|
||||||
|
|
||||||
|
return pronouns && (
|
||||||
|
<span
|
||||||
|
className={classes(styles.timestampInline, styles.timestamp, "vc-pronoundb-compact")}
|
||||||
|
>• {pronouns}</span>
|
||||||
|
);
|
||||||
}, { noop: true });
|
}, { noop: true });
|
78
src/plugins/pronoundb/index.ts
Normal file
78
src/plugins/pronoundb/index.ts
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
/*
|
||||||
|
* Vencord, a modification for Discord's desktop app
|
||||||
|
* Copyright (c) 2022 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 "./styles.css";
|
||||||
|
|
||||||
|
import { Devs } from "@utils/constants";
|
||||||
|
import definePlugin from "@utils/types";
|
||||||
|
|
||||||
|
import { useProfilePronouns } from "./api";
|
||||||
|
import PronounsAboutComponent from "./components/PronounsAboutComponent";
|
||||||
|
import { CompactPronounsChatComponentWrapper, PronounsChatComponentWrapper } from "./components/PronounsChatComponent";
|
||||||
|
import { settings } from "./settings";
|
||||||
|
|
||||||
|
export default definePlugin({
|
||||||
|
name: "PronounDB",
|
||||||
|
authors: [Devs.Tyman, Devs.TheKodeToad, Devs.Ven, Devs.Elvyra],
|
||||||
|
description: "Adds pronouns to user messages using pronoundb",
|
||||||
|
patches: [
|
||||||
|
{
|
||||||
|
find: "showCommunicationDisabledStyles",
|
||||||
|
replacement: [
|
||||||
|
// Add next to username (compact mode)
|
||||||
|
{
|
||||||
|
match: /("span",{id:\i,className:\i,children:\i}\))/,
|
||||||
|
replace: "$1, $self.CompactPronounsChatComponentWrapper(arguments[0])"
|
||||||
|
},
|
||||||
|
// Patch the chat timestamp element (normal mode)
|
||||||
|
{
|
||||||
|
match: /(?<=return\s*\(0,\i\.jsxs?\)\(.+!\i&&)(\(0,\i.jsxs?\)\(.+?\{.+?\}\))/,
|
||||||
|
replace: "[$1, $self.PronounsChatComponentWrapper(arguments[0])]"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
find: ".Messages.USER_PROFILE_PRONOUNS",
|
||||||
|
group: true,
|
||||||
|
replacement: [
|
||||||
|
{
|
||||||
|
match: /\.PANEL},/,
|
||||||
|
replace: "$&{pronouns:vcPronoun,source:vcPronounSource,hasPendingPronouns:vcHasPendingPronouns}=$self.useProfilePronouns(arguments[0].user?.id),"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
match: /text:\i\.\i.Messages.USER_PROFILE_PRONOUNS/,
|
||||||
|
replace: '$&+(vcPronoun==null||vcHasPendingPronouns?"":` (${vcPronounSource})`)'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
match: /(\.pronounsText.+?children:)(\i)/,
|
||||||
|
replace: "$1(vcPronoun==null||vcHasPendingPronouns)?$2:vcPronoun"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
settings,
|
||||||
|
|
||||||
|
settingsAboutComponent: PronounsAboutComponent,
|
||||||
|
|
||||||
|
// Re-export the components on the plugin object so it is easily accessible in patches
|
||||||
|
PronounsChatComponentWrapper,
|
||||||
|
CompactPronounsChatComponentWrapper,
|
||||||
|
useProfilePronouns
|
||||||
|
});
|
|
@ -19,10 +19,7 @@
|
||||||
import { definePluginSettings } from "@api/Settings";
|
import { definePluginSettings } from "@api/Settings";
|
||||||
import { OptionType } from "@utils/types";
|
import { OptionType } from "@utils/types";
|
||||||
|
|
||||||
export const enum PronounsFormat {
|
import { PronounsFormat, PronounSource } from "./types";
|
||||||
Lowercase = "LOWERCASE",
|
|
||||||
Capitalized = "CAPITALIZED"
|
|
||||||
}
|
|
||||||
|
|
||||||
export const settings = definePluginSettings({
|
export const settings = definePluginSettings({
|
||||||
pronounsFormat: {
|
pronounsFormat: {
|
||||||
|
@ -40,9 +37,34 @@ export const settings = definePluginSettings({
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
pronounSource: {
|
||||||
|
type: OptionType.SELECT,
|
||||||
|
description: "Where to source pronouns from",
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: "Prefer PronounDB, fall back to Discord",
|
||||||
|
value: PronounSource.PreferPDB,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Prefer Discord, fall back to PronounDB (might lead to inconsistency between pronouns in chat and profile)",
|
||||||
|
value: PronounSource.PreferDiscord
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
showSelf: {
|
showSelf: {
|
||||||
type: OptionType.BOOLEAN,
|
type: OptionType.BOOLEAN,
|
||||||
description: "Enable or disable showing pronouns for yourself",
|
description: "Enable or disable showing pronouns for the current user",
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
showInMessages: {
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
description: "Show in messages",
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
showInProfile: {
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
description: "Show in profile",
|
||||||
default: true
|
default: true
|
||||||
}
|
}
|
||||||
});
|
});
|
9
src/plugins/pronoundb/styles.css
Normal file
9
src/plugins/pronoundb/styles.css
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
.vc-pronoundb-compact {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
[class*="compact"] .vc-pronoundb-compact {
|
||||||
|
display: inline-block;
|
||||||
|
margin-left: -2px;
|
||||||
|
margin-right: 0.25rem;
|
||||||
|
}
|
63
src/plugins/pronoundb/types.ts
Normal file
63
src/plugins/pronoundb/types.ts
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
* Vencord, a modification for Discord's desktop app
|
||||||
|
* Copyright (c) 2022 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export interface UserProfileProps {
|
||||||
|
userId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UserProfilePronounsProps {
|
||||||
|
currentPronouns: string | null;
|
||||||
|
hidePersonalInformation: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type PronounSets = Record<string, PronounCode[]>;
|
||||||
|
export type PronounsResponse = Record<string, { sets?: PronounSets; }>;
|
||||||
|
|
||||||
|
export interface PronounsCache {
|
||||||
|
sets?: PronounSets;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PronounMapping = {
|
||||||
|
he: "He/Him",
|
||||||
|
it: "It/Its",
|
||||||
|
she: "She/Her",
|
||||||
|
they: "They/Them",
|
||||||
|
any: "Any pronouns",
|
||||||
|
other: "Other pronouns",
|
||||||
|
ask: "Ask me my pronouns",
|
||||||
|
avoid: "Avoid pronouns, use my name",
|
||||||
|
unspecified: "No pronouns specified.",
|
||||||
|
} as const satisfies Record<string, string>;
|
||||||
|
|
||||||
|
export type PronounCode = keyof typeof PronounMapping;
|
||||||
|
|
||||||
|
export interface Pronouns {
|
||||||
|
pronouns?: string;
|
||||||
|
source: string;
|
||||||
|
hasPendingPronouns: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const enum PronounsFormat {
|
||||||
|
Lowercase = "LOWERCASE",
|
||||||
|
Capitalized = "CAPITALIZED"
|
||||||
|
}
|
||||||
|
|
||||||
|
export const enum PronounSource {
|
||||||
|
PreferPDB,
|
||||||
|
PreferDiscord
|
||||||
|
}
|
|
@ -80,10 +80,7 @@ function GuildInfoModal({ guild }: GuildProps) {
|
||||||
className={cl("banner")}
|
className={cl("banner")}
|
||||||
src={bannerUrl}
|
src={bannerUrl}
|
||||||
alt=""
|
alt=""
|
||||||
onClick={() => openImageModal({
|
onClick={() => openImageModal(bannerUrl)}
|
||||||
url: bannerUrl,
|
|
||||||
width: 1024
|
|
||||||
})}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
@ -92,11 +89,7 @@ function GuildInfoModal({ guild }: GuildProps) {
|
||||||
? <img
|
? <img
|
||||||
src={iconUrl}
|
src={iconUrl}
|
||||||
alt=""
|
alt=""
|
||||||
onClick={() => openImageModal({
|
onClick={() => openImageModal(iconUrl)}
|
||||||
url: iconUrl,
|
|
||||||
height: 512,
|
|
||||||
width: 512,
|
|
||||||
})}
|
|
||||||
/>
|
/>
|
||||||
: <div aria-hidden className={classes(IconClasses.childWrapper, IconClasses.acronym)}>{guild.acronym}</div>
|
: <div aria-hidden className={classes(IconClasses.childWrapper, IconClasses.acronym)}>{guild.acronym}</div>
|
||||||
}
|
}
|
||||||
|
@ -158,15 +151,7 @@ function Owner(guildId: string, owner: User) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cl("owner")}>
|
<div className={cl("owner")}>
|
||||||
<img
|
<img src={ownerAvatarUrl} alt="" onClick={() => openImageModal(ownerAvatarUrl)} />
|
||||||
src={ownerAvatarUrl}
|
|
||||||
alt=""
|
|
||||||
onClick={() => openImageModal({
|
|
||||||
url: ownerAvatarUrl,
|
|
||||||
height: 512,
|
|
||||||
width: 512
|
|
||||||
})}
|
|
||||||
/>
|
|
||||||
{Parser.parse(`<@${owner.id}>`)}
|
{Parser.parse(`<@${owner.id}>`)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -30,9 +30,7 @@ export default definePlugin({
|
||||||
name: "ServerInfo",
|
name: "ServerInfo",
|
||||||
description: "Allows you to view info about a server",
|
description: "Allows you to view info about a server",
|
||||||
authors: [Devs.Ven, Devs.Nuckyz],
|
authors: [Devs.Ven, Devs.Nuckyz],
|
||||||
dependencies: ["DynamicImageModalAPI"],
|
|
||||||
tags: ["guild", "info", "ServerProfile"],
|
tags: ["guild", "info", "ServerProfile"],
|
||||||
|
|
||||||
contextMenus: {
|
contextMenus: {
|
||||||
"guild-context": Patch,
|
"guild-context": Patch,
|
||||||
"guild-header-popout": Patch
|
"guild-header-popout": Patch
|
||||||
|
|
|
@ -61,10 +61,6 @@ export const settings = definePluginSettings({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function isUncategorized(objChannel: { channel: Channel; comparator: number; }) {
|
|
||||||
return objChannel.channel.id === "null" && objChannel.channel.name === "Uncategorized" && objChannel.comparator === -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "ShowHiddenChannels",
|
name: "ShowHiddenChannels",
|
||||||
description: "Show channels that you do not have access to view.",
|
description: "Show channels that you do not have access to view.",
|
||||||
|
@ -103,7 +99,7 @@ export default definePlugin({
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
// Do not show confirmation to join a voice channel when already connected to another if clicking on a hidden voice channel
|
// Do not show confirmation to join a voice channel when already connected to another if clicking on a hidden voice channel
|
||||||
match: /(?<=getBlockedUsersForVoiceChannel\((\i)\.id\);return)/,
|
match: /(?<=getCurrentClientVoiceChannelId\((\i)\.guild_id\);return)/,
|
||||||
replace: (_, channel) => `!$self.isHiddenChannel(${channel})&&`
|
replace: (_, channel) => `!$self.isHiddenChannel(${channel})&&`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -507,7 +503,7 @@ export default definePlugin({
|
||||||
res[key] ??= [];
|
res[key] ??= [];
|
||||||
|
|
||||||
for (const objChannel of maybeObjChannels) {
|
for (const objChannel of maybeObjChannels) {
|
||||||
if (isUncategorized(objChannel) || objChannel.channel.id === null || !this.isHiddenChannel(objChannel.channel)) res[key].push(objChannel);
|
if (objChannel.channel.id === null || !this.isHiddenChannel(objChannel.channel)) res[key].push(objChannel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -229,7 +229,7 @@ function AlbumContextMenu({ track }: { track: Track; }) {
|
||||||
id="view-cover"
|
id="view-cover"
|
||||||
label="View Album Cover"
|
label="View Album Cover"
|
||||||
// trolley
|
// trolley
|
||||||
action={() => openImageModal(track.album.image)}
|
action={() => openImageModal(track.album.image.url)}
|
||||||
icon={ImageIcon}
|
icon={ImageIcon}
|
||||||
/>
|
/>
|
||||||
<Menu.MenuControlItem
|
<Menu.MenuControlItem
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
User Messages Pronouns
|
|
||||||
|
|
||||||
Adds pronouns to chat user messages
|
|
||||||
|
|
||||||
![](https://github.com/user-attachments/assets/34dc373d-faf4-4420-b49b-08b2647baa3b)
|
|
|
@ -1,54 +0,0 @@
|
||||||
/*
|
|
||||||
* Vencord, a modification for Discord's desktop app
|
|
||||||
* Copyright (c) 2022 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 { migratePluginSettings } from "@api/Settings";
|
|
||||||
import { Devs } from "@utils/constants";
|
|
||||||
import definePlugin from "@utils/types";
|
|
||||||
|
|
||||||
import { CompactPronounsChatComponentWrapper, PronounsChatComponentWrapper } from "./PronounsChatComponent";
|
|
||||||
import { settings } from "./settings";
|
|
||||||
|
|
||||||
migratePluginSettings("UserMessagesPronouns", "PronounDB");
|
|
||||||
export default definePlugin({
|
|
||||||
name: "UserMessagesPronouns",
|
|
||||||
authors: [Devs.Tyman, Devs.TheKodeToad, Devs.Ven, Devs.Elvyra],
|
|
||||||
description: "Adds pronouns to chat user messages",
|
|
||||||
settings,
|
|
||||||
|
|
||||||
patches: [
|
|
||||||
{
|
|
||||||
find: "showCommunicationDisabledStyles",
|
|
||||||
replacement: {
|
|
||||||
// Add next to timestamp (normal mode)
|
|
||||||
match: /(?<=return\s*\(0,\i\.jsxs?\)\(.+!\i&&)(\(0,\i.jsxs?\)\(.+?\{.+?\}\))/,
|
|
||||||
replace: "[$1, $self.PronounsChatComponentWrapper(arguments[0])]"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
find: '="SYSTEM_TAG"',
|
|
||||||
replacement: {
|
|
||||||
// Add next to username (compact mode)
|
|
||||||
match: /className:\i\(\)\(\i\.className(?:,\i\.clickable)?,\i\)}\),(?=\i)/g,
|
|
||||||
replace: "$&$self.CompactPronounsChatComponentWrapper(arguments[0]),"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
|
|
||||||
PronounsChatComponentWrapper,
|
|
||||||
CompactPronounsChatComponentWrapper,
|
|
||||||
});
|
|
|
@ -1,35 +0,0 @@
|
||||||
/*
|
|
||||||
* Vencord, a modification for Discord's desktop app
|
|
||||||
* Copyright (c) 2022 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 { getCurrentChannel } from "@utils/discord";
|
|
||||||
import { UserProfileStore, useStateFromStores } from "@webpack/common";
|
|
||||||
|
|
||||||
import { PronounsFormat, settings } from "./settings";
|
|
||||||
|
|
||||||
function useDiscordPronouns(id: string, useGlobalProfile: boolean = false): string | undefined {
|
|
||||||
const globalPronouns: string | undefined = useStateFromStores([UserProfileStore], () => UserProfileStore.getUserProfile(id)?.pronouns);
|
|
||||||
const guildPronouns: string | undefined = useStateFromStores([UserProfileStore], () => UserProfileStore.getGuildMemberProfile(id, getCurrentChannel()?.getGuildId())?.pronouns);
|
|
||||||
|
|
||||||
if (useGlobalProfile) return globalPronouns;
|
|
||||||
return guildPronouns || globalPronouns;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function useFormattedPronouns(id: string, useGlobalProfile: boolean = false) {
|
|
||||||
const pronouns = useDiscordPronouns(id, useGlobalProfile)?.trim().replace(/\n+/g, "");
|
|
||||||
return settings.store.pronounsFormat === PronounsFormat.Lowercase ? pronouns?.toLowerCase() : pronouns;
|
|
||||||
}
|
|
|
@ -67,10 +67,7 @@ const settings = definePluginSettings({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const openAvatar = (url: string) => openImage(url, 512, 512);
|
function openImage(url: string) {
|
||||||
const openBanner = (url: string) => openImage(url, 1024);
|
|
||||||
|
|
||||||
function openImage(url: string, width: number, height?: number) {
|
|
||||||
const format = url.startsWith("/") ? "png" : settings.store.format;
|
const format = url.startsWith("/") ? "png" : settings.store.format;
|
||||||
|
|
||||||
const u = new URL(url, window.location.href);
|
const u = new URL(url, window.location.href);
|
||||||
|
@ -79,13 +76,11 @@ function openImage(url: string, width: number, height?: number) {
|
||||||
url = u.toString();
|
url = u.toString();
|
||||||
|
|
||||||
u.searchParams.set("size", "4096");
|
u.searchParams.set("size", "4096");
|
||||||
const original = u.toString();
|
const originalUrl = u.toString();
|
||||||
|
|
||||||
openImageModal({
|
openImageModal(url, {
|
||||||
url,
|
original: originalUrl,
|
||||||
original,
|
height: 256
|
||||||
width,
|
|
||||||
height
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,14 +93,14 @@ const UserContext: NavContextMenuPatchCallback = (children, { user, guildId }: U
|
||||||
<Menu.MenuItem
|
<Menu.MenuItem
|
||||||
id="view-avatar"
|
id="view-avatar"
|
||||||
label="View Avatar"
|
label="View Avatar"
|
||||||
action={() => openAvatar(IconUtils.getUserAvatarURL(user, true))}
|
action={() => openImage(IconUtils.getUserAvatarURL(user, true))}
|
||||||
icon={ImageIcon}
|
icon={ImageIcon}
|
||||||
/>
|
/>
|
||||||
{memberAvatar && (
|
{memberAvatar && (
|
||||||
<Menu.MenuItem
|
<Menu.MenuItem
|
||||||
id="view-server-avatar"
|
id="view-server-avatar"
|
||||||
label="View Server Avatar"
|
label="View Server Avatar"
|
||||||
action={() => openAvatar(IconUtils.getGuildMemberAvatarURLSimple({
|
action={() => openImage(IconUtils.getGuildMemberAvatarURLSimple({
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
avatar: memberAvatar,
|
avatar: memberAvatar,
|
||||||
guildId: guildId!,
|
guildId: guildId!,
|
||||||
|
@ -131,7 +126,7 @@ const GuildContext: NavContextMenuPatchCallback = (children, { guild }: GuildCon
|
||||||
id="view-icon"
|
id="view-icon"
|
||||||
label="View Icon"
|
label="View Icon"
|
||||||
action={() =>
|
action={() =>
|
||||||
openAvatar(IconUtils.getGuildIconURL({
|
openImage(IconUtils.getGuildIconURL({
|
||||||
id,
|
id,
|
||||||
icon,
|
icon,
|
||||||
canAnimate: true
|
canAnimate: true
|
||||||
|
@ -145,7 +140,7 @@ const GuildContext: NavContextMenuPatchCallback = (children, { guild }: GuildCon
|
||||||
id="view-banner"
|
id="view-banner"
|
||||||
label="View Banner"
|
label="View Banner"
|
||||||
action={() =>
|
action={() =>
|
||||||
openBanner(IconUtils.getGuildBannerURL(guild, true)!)
|
openImage(IconUtils.getGuildBannerURL(guild, true)!)
|
||||||
}
|
}
|
||||||
icon={ImageIcon}
|
icon={ImageIcon}
|
||||||
/>
|
/>
|
||||||
|
@ -163,7 +158,7 @@ const GroupDMContext: NavContextMenuPatchCallback = (children, { channel }: Grou
|
||||||
id="view-group-channel-icon"
|
id="view-group-channel-icon"
|
||||||
label="View Icon"
|
label="View Icon"
|
||||||
action={() =>
|
action={() =>
|
||||||
openAvatar(IconUtils.getChannelIconURL(channel)!)
|
openImage(IconUtils.getChannelIconURL(channel)!)
|
||||||
}
|
}
|
||||||
icon={ImageIcon}
|
icon={ImageIcon}
|
||||||
/>
|
/>
|
||||||
|
@ -176,12 +171,10 @@ export default definePlugin({
|
||||||
authors: [Devs.Ven, Devs.TheKodeToad, Devs.Nuckyz, Devs.nyx],
|
authors: [Devs.Ven, Devs.TheKodeToad, Devs.Nuckyz, Devs.nyx],
|
||||||
description: "Makes avatars and banners in user profiles clickable, adds View Icon/Banner entries in the user, server and group channel context menu.",
|
description: "Makes avatars and banners in user profiles clickable, adds View Icon/Banner entries in the user, server and group channel context menu.",
|
||||||
tags: ["ImageUtilities"],
|
tags: ["ImageUtilities"],
|
||||||
dependencies: ["DynamicImageModalAPI"],
|
|
||||||
|
|
||||||
settings,
|
settings,
|
||||||
|
|
||||||
openAvatar,
|
openImage,
|
||||||
openBanner,
|
|
||||||
|
|
||||||
contextMenus: {
|
contextMenus: {
|
||||||
"user-context": UserContext,
|
"user-context": UserContext,
|
||||||
|
@ -195,7 +188,7 @@ export default definePlugin({
|
||||||
find: ".overlay:void 0,status:",
|
find: ".overlay:void 0,status:",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /avatarSrc:(\i),eventHandlers:(\i).+?"div",{...\2,/,
|
match: /avatarSrc:(\i),eventHandlers:(\i).+?"div",{...\2,/,
|
||||||
replace: "$&style:{cursor:\"pointer\"},onClick:()=>{$self.openAvatar($1)},"
|
replace: "$&style:{cursor:\"pointer\"},onClick:()=>{$self.openImage($1)},"
|
||||||
},
|
},
|
||||||
all: true
|
all: true
|
||||||
},
|
},
|
||||||
|
@ -204,7 +197,7 @@ export default definePlugin({
|
||||||
find: 'backgroundColor:"COMPLETE"',
|
find: 'backgroundColor:"COMPLETE"',
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(\.banner,.+?),style:{(?=.+?backgroundImage:null!=(\i)\?"url\("\.concat\(\2,)/,
|
match: /(\.banner,.+?),style:{(?=.+?backgroundImage:null!=(\i)\?"url\("\.concat\(\2,)/,
|
||||||
replace: (_, rest, bannerSrc) => `${rest},onClick:()=>${bannerSrc}!=null&&$self.openBanner(${bannerSrc}),style:{cursor:${bannerSrc}!=null?"pointer":void 0,`
|
replace: (_, rest, bannerSrc) => `${rest},onClick:()=>${bannerSrc}!=null&&$self.openImage(${bannerSrc}),style:{cursor:${bannerSrc}!=null?"pointer":void 0,`
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// Group DMs top small & large icon
|
// Group DMs top small & large icon
|
||||||
|
@ -212,7 +205,7 @@ export default definePlugin({
|
||||||
find: /\.recipients\.length>=2(?!<isMultiUserDM.{0,50})/,
|
find: /\.recipients\.length>=2(?!<isMultiUserDM.{0,50})/,
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /null==\i\.icon\?.+?src:(\(0,\i\.\i\).+?\))(?=[,}])/,
|
match: /null==\i\.icon\?.+?src:(\(0,\i\.\i\).+?\))(?=[,}])/,
|
||||||
replace: (m, iconUrl) => `${m},onClick:()=>$self.openAvatar(${iconUrl})`
|
replace: (m, iconUrl) => `${m},onClick:()=>$self.openImage(${iconUrl})`
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// User DMs top small icon
|
// User DMs top small icon
|
||||||
|
@ -220,7 +213,7 @@ export default definePlugin({
|
||||||
find: ".cursorPointer:null,children",
|
find: ".cursorPointer:null,children",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /.Avatar,.+?src:(.+?\))(?=[,}])/,
|
match: /.Avatar,.+?src:(.+?\))(?=[,}])/,
|
||||||
replace: (m, avatarUrl) => `${m},onClick:()=>$self.openAvatar(${avatarUrl})`
|
replace: (m, avatarUrl) => `${m},onClick:()=>$self.openImage(${avatarUrl})`
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// User Dms top large icon
|
// User Dms top large icon
|
||||||
|
@ -228,7 +221,7 @@ export default definePlugin({
|
||||||
find: 'experimentLocation:"empty_messages"',
|
find: 'experimentLocation:"empty_messages"',
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /.Avatar,.+?src:(.+?\))(?=[,}])/,
|
match: /.Avatar,.+?src:(.+?\))(?=[,}])/,
|
||||||
replace: (m, avatarUrl) => `${m},onClick:()=>$self.openAvatar(${avatarUrl})`
|
replace: (m, avatarUrl) => `${m},onClick:()=>$self.openImage(${avatarUrl})`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -25,7 +25,7 @@ const KeyBinds = findByPropsLazy("JUMP_TO_GUILD", "SERVER_NEXT");
|
||||||
|
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "WebKeybinds",
|
name: "WebKeybinds",
|
||||||
description: "Re-adds keybinds missing in the web version of Discord: ctrl+t, ctrl+shift+t, ctrl+tab, ctrl+shift+tab, ctrl+1-9, ctrl+,. Only works fully on Vesktop/Legcord, not inside your browser",
|
description: "Re-adds keybinds missing in the web version of Discord: ctrl+t, ctrl+shift+t, ctrl+tab, ctrl+shift+tab, ctrl+1-9, ctrl+,. Only works fully on Vesktop/ArmCord, not inside your browser",
|
||||||
authors: [Devs.Ven],
|
authors: [Devs.Ven],
|
||||||
enabledByDefault: true,
|
enabledByDefault: true,
|
||||||
|
|
||||||
|
|
|
@ -575,10 +575,6 @@ export const Devs = /* #__PURE__*/ Object.freeze({
|
||||||
name: "RamziAH",
|
name: "RamziAH",
|
||||||
id: 1279957227612147747n,
|
id: 1279957227612147747n,
|
||||||
},
|
},
|
||||||
SomeAspy: {
|
|
||||||
name: "SomeAspy",
|
|
||||||
id: 516750892372852754n,
|
|
||||||
},
|
|
||||||
} satisfies Record<string, Dev>);
|
} satisfies Record<string, Dev>);
|
||||||
|
|
||||||
// iife so #__PURE__ works correctly
|
// iife so #__PURE__ works correctly
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
.vc-position-inherit {
|
|
||||||
position: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* copy pasted from discord css. not really webpack-findable since it's the only class in the module
|
|
||||||
**/
|
|
||||||
|
|
||||||
.vc-image-modal {
|
|
||||||
background: transparent!important;
|
|
||||||
box-shadow: none!important;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
border-radius: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
@media(width <= 485px) {
|
|
||||||
.vc-image-modal {
|
|
||||||
display:relative;
|
|
||||||
overflow: visible;
|
|
||||||
overflow: initial
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -16,13 +16,11 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import "./discord.css";
|
|
||||||
|
|
||||||
import { MessageObject } from "@api/MessageEvents";
|
import { MessageObject } from "@api/MessageEvents";
|
||||||
import { ChannelStore, ComponentDispatch, Constants, FluxDispatcher, GuildStore, InviteActions, MessageActions, PrivateChannelsStore, RestAPI, SelectedChannelStore, SelectedGuildStore, UserProfileActions, UserProfileStore, UserSettingsActionCreators, UserUtils } from "@webpack/common";
|
import { ChannelStore, ComponentDispatch, Constants, FluxDispatcher, GuildStore, InviteActions, MaskedLink, MessageActions, ModalImageClasses, PrivateChannelsStore, RestAPI, SelectedChannelStore, SelectedGuildStore, UserProfileActions, UserProfileStore, UserSettingsActionCreators, UserUtils } from "@webpack/common";
|
||||||
import { Channel, Guild, Message, User } from "discord-types/general";
|
import { Channel, Guild, Message, User } from "discord-types/general";
|
||||||
|
|
||||||
import { ImageModal, ImageModalItem, openModal } from "./modal";
|
import { ImageModal, ModalRoot, ModalSize, openModal } from "./modal";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Open the invite modal
|
* Open the invite modal
|
||||||
|
@ -110,24 +108,25 @@ export function sendMessage(
|
||||||
return MessageActions.sendMessage(channelId, messageData, waitForChannelReady, extra);
|
return MessageActions.sendMessage(channelId, messageData, waitForChannelReady, extra);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export function openImageModal(url: string, props?: Partial<React.ComponentProps<ImageModal>>): string {
|
||||||
* You must specify either height or width
|
|
||||||
*/
|
|
||||||
export function openImageModal(props: Omit<ImageModalItem, "type">): string {
|
|
||||||
return openModal(modalProps => (
|
return openModal(modalProps => (
|
||||||
<ImageModal
|
<ModalRoot
|
||||||
{...modalProps}
|
{...modalProps}
|
||||||
className="vc-image-modal"
|
className={ModalImageClasses.modal}
|
||||||
fit="vc-position-inherit"
|
size={ModalSize.DYNAMIC}>
|
||||||
items={[{
|
<ImageModal
|
||||||
type: "IMAGE",
|
className={ModalImageClasses.image}
|
||||||
original: props.url,
|
original={url}
|
||||||
...props,
|
placeholder={url}
|
||||||
}]}
|
src={url}
|
||||||
onClose={modalProps.onClose}
|
renderLinkComponent={props => <MaskedLink {...props} />}
|
||||||
|
// Don't render forward message button
|
||||||
|
renderForwardComponent={() => null}
|
||||||
shouldHideMediaOptions={false}
|
shouldHideMediaOptions={false}
|
||||||
shouldAnimate
|
shouldAnimate
|
||||||
|
{...props}
|
||||||
/>
|
/>
|
||||||
|
</ModalRoot>
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -101,24 +101,25 @@ export const Modals = findByPropsLazy("ModalRoot", "ModalCloseButton") as {
|
||||||
}>;
|
}>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface ImageModalItem {
|
|
||||||
type: "IMAGE" | "VIDEO";
|
|
||||||
url: string;
|
|
||||||
width?: number;
|
|
||||||
height?: number;
|
|
||||||
original?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ImageModal = ComponentType<{
|
export type ImageModal = ComponentType<{
|
||||||
className?: string;
|
className?: string;
|
||||||
fit?: string;
|
src: string;
|
||||||
|
placeholder: string;
|
||||||
|
original: string;
|
||||||
|
width?: number;
|
||||||
|
height?: number;
|
||||||
|
animated?: boolean;
|
||||||
|
responsive?: boolean;
|
||||||
|
renderLinkComponent(props: any): ReactNode;
|
||||||
|
renderForwardComponent(props: any): ReactNode;
|
||||||
|
maxWidth?: number;
|
||||||
|
maxHeight?: number;
|
||||||
|
shouldAnimate?: boolean;
|
||||||
onClose?(): void;
|
onClose?(): void;
|
||||||
shouldHideMediaOptions?: boolean;
|
shouldHideMediaOptions?: boolean;
|
||||||
shouldAnimate?: boolean;
|
|
||||||
items: ImageModalItem[];
|
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
export const ImageModal = findComponentByCodeLazy(".MEDIA_MODAL_CLOSE") as ImageModal;
|
export const ImageModal = findComponentByCodeLazy(".MEDIA_MODAL_CLOSE", "responsive") as ImageModal;
|
||||||
|
|
||||||
export const ModalRoot = LazyComponent(() => Modals.ModalRoot);
|
export const ModalRoot = LazyComponent(() => Modals.ModalRoot);
|
||||||
export const ModalHeader = LazyComponent(() => Modals.ModalHeader);
|
export const ModalHeader = LazyComponent(() => Modals.ModalHeader);
|
||||||
|
|
Loading…
Reference in a new issue