Merge branch 'dev'

This commit is contained in:
thororen1234 2024-10-22 23:01:57 -04:00
commit a8ae13ec08
26 changed files with 269 additions and 129 deletions

View file

@ -1,7 +1,7 @@
{ {
"name": "equicord", "name": "equicord",
"private": "true", "private": "true",
"version": "1.10.4", "version": "1.10.5",
"description": "The other cutest Discord client mod", "description": "The other cutest Discord client mod",
"homepage": "https://github.com/Equicord/Equicord#readme", "homepage": "https://github.com/Equicord/Equicord#readme",
"bugs": { "bugs": {

View file

@ -5,3 +5,73 @@
.vc-plugin-modal-description { .vc-plugin-modal-description {
flex-grow: 1; flex-grow: 1;
} }
/* warning modal */
.vc-text-selectable {
user-select: none;
}
.vc-warning-info {
gap: 15px;
flex-direction: column;
user-select: none;
}
.vc-warning-info img {
width: 70%;
height: auto;
display: block;
margin: auto auto 10px;
}
.vc-warning-info strong {
font-weight: bold;
}
.vc-warning-info button.disable-warning {
font-size: 0.8rem;
background-color: transparent;
color: red;
cursor: pointer;
margin: 0 auto;
width: fit-content;
text-decoration: underline;
}
.vc-warning-info button.disable-warning:hover {
background-color: transparent;
color: var(--text-danger);
}
.vc-warning-info button.disable-warning:active {
background-color: transparent;
}
.text-danger {
color: var(--text-danger);
font-size: 1.4rem;
font-weight: bold;
}
.text-normal {
color: var(--text-normal);
font-size: 1.2rem;
}
.text-normal.margin-bottom {
margin-bottom: 10px;
}
.button-danger-background {
background-color: var(--button-danger-background);
}
.button-danger-background-no-margin {
background-color: var(--button-danger-background) !important;
color: var(--header-primary);
}
.vc-modal-close-button {
margin-left: auto !important;
}

View file

@ -407,36 +407,27 @@ export function openWarningModal(plugin: Plugin, pluginModalProps: ModalProps, o
transitionState={warningModalProps.transitionState} transitionState={warningModalProps.transitionState}
> >
<ModalHeader separator={false}> <ModalHeader separator={false}>
<Text style={{ flexGrow: 1, color: "var(--text-danger)", fontSize: "1.4rem", fontWeight: "bold" }}>Warning: Dangerous Action</Text> <Text className="text-danger">Warning: Dangerous Action</Text>
<ModalCloseButton onClick={warningModalProps.onClose} /> <ModalCloseButton onClick={warningModalProps.onClose} className="vc-modal-close-button" />
</ModalHeader> </ModalHeader>
<ModalContent> <ModalContent>
<Forms.FormSection> <Forms.FormSection>
<Flex className="vc-warning-info" style={{ gap: "15px", flexDirection: "column", userSelect: "none" }}> <Flex className="vc-warning-info">
<img <img
src="https://media.tenor.com/Y6DXKZiBCs8AAAAi/stavario-josefbenes.gif" src="https://media.tenor.com/Y6DXKZiBCs8AAAAi/stavario-josefbenes.gif"
alt="Warning" alt="Warning"
style={{ width: "60%", height: "auto", marginBottom: "10px", display: "block", margin: "auto" }}
/> />
<Text style={{ fontSize: "1.2rem", color: "var(--text-normal)" }}> <Text className="text-normal">
You are about to reset all settings for <strong>{plugin.name}</strong> to their default values. You are about to reset all settings for <strong>{plugin.name}</strong> to their default values.
</Text> </Text>
<Text style={{ fontSize: "1.2rem", color: "var(--text-danger)", fontWeight: "bold" }}> <Text className="text-danger">
This action is irreversible. This action is irreversible.
</Text> </Text>
<Text style={{ fontSize: "1.2rem", color: "var(--text-normal)", marginBottom: "10px" }}> <Text className="text-normal margin-bottom">
If you are certain you want to proceed, click <strong>Confirm Reset</strong>. Otherwise, click <strong>Cancel</strong>. If you are certain you want to proceed, click <strong>Confirm Reset</strong>. Otherwise, click <strong>Cancel</strong>.
</Text> </Text>
{!Settings.ignoreResetWarning && ( {!Settings.ignoreResetWarning && (
<Button style={{ <Button className="disable-warning" onClick={() => {
fontSize: "0.8rem",
backgroundColor: "transparent",
color: "red",
cursor: "pointer",
margin: "0 auto",
width: "fit-content",
textDecoration: "underline"
}} onClick={() => {
Settings.ignoreResetWarning = true; Settings.ignoreResetWarning = true;
}}> }}>
Disable this warning forever Disable this warning forever
@ -466,7 +457,7 @@ export function openWarningModal(plugin: Plugin, pluginModalProps: ModalProps, o
}} }}
onMouseEnter={onMouseEnter} onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave} onMouseLeave={onMouseLeave}
style={{ marginLeft: "10px", backgroundColor: "var(--button-danger-background)" }} className="button-danger-background-no-margin"
> >
Confirm Reset Confirm Reset
</Button> </Button>

View file

@ -429,35 +429,26 @@ export default function PluginSettings() {
{enabledPlugins.length > 0 && ( {enabledPlugins.length > 0 && (
<Button <Button
size={Button.Sizes.SMALL} size={Button.Sizes.SMALL}
style={{ backgroundColor: "var(--button-danger-background)", margin: "20px 0" }} className="button-danger-background"
onClick={() => { onClick={() => {
if (Settings.ignoreResetWarning) return resetCheckAndDo(); if (Settings.ignoreResetWarning) return resetCheckAndDo();
return Alerts.show({ return Alerts.show({
title: "Disable All Plugins", title: "Disable All Plugins",
body: ( body: (
<div style={{ textAlign: "center" }}> <div className="alert-body">
<img <img
src="https://media.tenor.com/Y6DXKZiBCs8AAAAi/stavario-josefbenes.gif" src="https://media.tenor.com/Y6DXKZiBCs8AAAAi/stavario-josefbenes.gif"
alt="Warning" alt="Warning"
style={{ width: "60%", height: "auto", marginBottom: "10px", display: "block", margin: "auto" }}
/> />
<p style={{ fontSize: "1.2rem", color: "var(--text-danger)", fontWeight: "bold" }}> <p className="warning-text">
WARNING: You are about to disable <span style={{ textDecoration: "underline" }}>{enabledPlugins.length}</span> plugins! WARNING: You are about to disable <span>{enabledPlugins.length}</span> plugins!
</p> </p>
<p style={{ fontSize: "1rem" }}> <p>
Are you absolutely sure you want to proceed? You can always enable them back later. Are you absolutely sure you want to proceed? You can always enable them back later.
</p> </p>
{!Settings.ignoreResetWarning && ( {!Settings.ignoreResetWarning && (
<Button style={{ <Button className="disable-warning" onClick={() => {
fontSize: "0.8rem",
backgroundColor: "transparent",
color: "red",
cursor: "pointer",
margin: "0 auto",
width: "fit-content",
textDecoration: "underline"
}} onClick={() => {
Settings.ignoreResetWarning = true; Settings.ignoreResetWarning = true;
}}> }}>
Disable this warning forever Disable this warning forever
@ -466,6 +457,7 @@ export default function PluginSettings() {
</div> </div>
), ),
confirmText: "Disable All", confirmText: "Disable All",
confirmColor: "button-danger-background-no-margin",
cancelText: "Cancel", cancelText: "Cancel",
onConfirm: () => { onConfirm: () => {
resetCheckAndDo(); resetCheckAndDo();

View file

@ -124,3 +124,66 @@
height: 100%; height: 100%;
margin: 0 10px; margin: 0 10px;
} }
/* disable all modal */
.button-danger-background {
background-color: var(--button-danger-background) !important;
margin: 20px 0;
}
.button-danger-background-no-margin {
background-color: var(--button-danger-background) !important;
color: var(--header-primary);
}
.button-danger-background:hover {
background-color: var(--button-danger-background-hover);
}
.alert-body {
text-align: center;
}
.alert-body img {
width: 60%;
height: auto;
display: block;
margin: auto auto 10px;
border-radius: 8px;
}
.alert-body p.warning-text {
font-size: 1.2rem;
color: var(--text-danger);
font-weight: bold;
}
.alert-body p {
font-size: 1rem;
}
.alert-body span {
text-decoration: underline;
}
.disable-warning {
font-size: 0.8rem;
background-color: transparent !important;
color: var(--text-danger) !important;
cursor: pointer;
margin: 0 auto;
width: fit-content;
text-decoration: underline;
transition: color 0.2s ease;
}
.disable-warning:hover {
color: var(--text-danger);
background-color: transparent;
opacity: 0.8;
}
.disable-warning:active {
background-color: transparent;
}

View file

@ -134,8 +134,9 @@ export async function sendSticker({
file = await toGIF(sticker.image, ffmpegState.ffmpeg); file = await toGIF(sticker.image, ffmpegState.ffmpeg);
} }
else { else {
const response = await fetch(sticker.image, { cache: "force-cache" }); const url = new URL(sticker.image);
// const blob = await response.blob(); url.searchParams.set("t", Date.now().toString()); // To prevent caching, in order to avoid CORS bug in Chrome
const response = await fetch(sticker.image);
const orgImageUrl = URL.createObjectURL(await response.blob()); const orgImageUrl = URL.createObjectURL(await response.blob());
const processedImage = await resizeImage(orgImageUrl); const processedImage = await resizeImage(orgImageUrl);

View file

@ -9,9 +9,9 @@ import definePlugin from "@utils/types";
export default definePlugin({ export default definePlugin({
name: "ImageModalAPI", name: "DynamicImageModalAPI",
authors: [Devs.sadan, Devs.Nuckyz], authors: [Devs.sadan, Devs.Nuckyz],
description: "Allows you to open Image Modals", description: "Allows you to omit either width or height when opening an image modal",
patches: [ patches: [
{ {
find: "SCALE_DOWN:", find: "SCALE_DOWN:",

View file

@ -209,7 +209,7 @@ export default definePlugin({
}, },
get electronVersion() { get electronVersion() {
return VencordNative.native.getVersions().electron || window.armcord?.electron || null; return VencordNative.native.getVersions().electron || window.legcord?.electron || null;
}, },
get chromiumVersion() { get chromiumVersion() {

View file

@ -75,7 +75,7 @@ async function generateDebugInfoMessage() {
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 (IS_EQUIBOP) return `Equibop v${VesktopNative.app.getVersion()}`; if (IS_EQUIBOP) return `Equibop v${VesktopNative.app.getVersion()}`;
if ("armcord" in window) return `ArmCord v${window.armcord.version}`; if ("legcord" in window) return `LegCord v${window.armcord.version}`;
// @ts-expect-error // @ts-expect-error
const name = typeof unsafeWindow !== "undefined" ? "UserScript" : "Web"; const name = typeof unsafeWindow !== "undefined" ? "UserScript" : "Web";

View file

@ -73,8 +73,8 @@ export default definePlugin({
}, },
async start() { async start() {
// ArmCord comes with its own arRPC implementation, so this plugin just confuses users // Legcord comes with its own arRPC implementation, so this plugin just confuses users
if ("armcord" in window) return; if ("legcord" 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

View file

@ -65,7 +65,7 @@ export default definePlugin({
name: "BetterRoleContext", name: "BetterRoleContext",
description: "Adds options to copy role color / edit role / view role icon when right clicking roles in the user profile", description: "Adds options to copy role color / edit role / view role icon when right clicking roles in the user profile",
authors: [Devs.Ven, Devs.goodbee], authors: [Devs.Ven, Devs.goodbee],
dependencies: ["UserSettingsAPI", "ImageModalAPI"], dependencies: ["UserSettingsAPI"],
settings, settings,
@ -99,7 +99,11 @@ export default definePlugin({
id="vc-view-role-icon" id="vc-view-role-icon"
label="View Role Icon" label="View Role Icon"
action={() => { action={() => {
openImageModal(`${location.protocol}//${window.GLOBAL_ENV.CDN_HOST}/role-icons/${role.id}/${role.icon}.${settings.store.roleIconFileFormat}`); openImageModal({
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}
/> />

View file

@ -57,7 +57,11 @@ 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(previewUrl); openImageModal({
url: previewUrl,
height: 720,
width: 1280
});
}; };
export const addViewStreamContext: NavContextMenuPatchCallback = (children, { userId }: { userId: string | bigint; }) => { export const addViewStreamContext: NavContextMenuPatchCallback = (children, { userId }: { userId: string | bigint; }) => {
@ -89,7 +93,6 @@ export default definePlugin({
name: "BiggerStreamPreview", name: "BiggerStreamPreview",
description: "This plugin allows you to enlarge stream previews", description: "This plugin allows you to enlarge stream previews",
authors: [Devs.phil], authors: [Devs.phil],
dependencies: ["ImageModalAPI"],
contextMenus: { contextMenus: {
"user-context": userContextPatch, "user-context": userContextPatch,
"stream-context": streamContextPatch "stream-context": streamContextPatch

View file

@ -181,13 +181,6 @@ export default definePlugin({
replace: "$&$self.unMountMagnifier();" replace: "$&$self.unMountMagnifier();"
} }
] ]
},
{
find: ".carouselModal",
replacement: {
match: /(?<=\.carouselModal.{0,100}onClick:)\i,/,
replace: "()=>{},"
}
} }
], ],

View file

@ -21,12 +21,3 @@
/* 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;
}

View file

@ -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(".mutualFriendItem]"); const ExpandableList = findComponentByCodeLazy('"PRESS_SECTION"');
const GuildLabelClasses = findByPropsLazy("guildNick", "guildAvatarWithoutIcon"); const GuildLabelClasses = findByPropsLazy("guildNick", "guildAvatarWithoutIcon");
function getGroupDMName(channel: Channel) { function getGroupDMName(channel: Channel) {
@ -142,16 +142,15 @@ 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
className={listStyle} listClassName={listStyle}
header={header} header={"Mutual Groups"}
isLoadingHeader={false} isLoading={false}
children={renderClickableGDMs(mutualGDms, () => { })} items={renderClickableGDMs(mutualGDms, () => { })}
/> />
</> </>
); );

View file

@ -91,15 +91,6 @@ 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,

View file

@ -80,7 +80,10 @@ function GuildInfoModal({ guild }: GuildProps) {
className={cl("banner")} className={cl("banner")}
src={bannerUrl} src={bannerUrl}
alt="" alt=""
onClick={() => openImageModal(bannerUrl)} onClick={() => openImageModal({
url: bannerUrl,
width: 1024
})}
/> />
)} )}
@ -89,8 +92,10 @@ function GuildInfoModal({ guild }: GuildProps) {
? <img ? <img
src={iconUrl} src={iconUrl}
alt="" alt=""
onClick={() => openImageModal(iconUrl, { onClick={() => openImageModal({
width: 256 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>
@ -153,7 +158,15 @@ function Owner(guildId: string, owner: User) {
return ( return (
<div className={cl("owner")}> <div className={cl("owner")}>
<img src={ownerAvatarUrl} alt="" onClick={() => openImageModal(ownerAvatarUrl)} /> <img
src={ownerAvatarUrl}
alt=""
onClick={() => openImageModal({
url: ownerAvatarUrl,
height: 512,
width: 512
})}
/>
{Parser.parse(`<@${owner.id}>`)} {Parser.parse(`<@${owner.id}>`)}
</div> </div>
); );

View file

@ -30,8 +30,8 @@ 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"],
dependencies: ["ImageModalAPI"],
contextMenus: { contextMenus: {
"guild-context": Patch, "guild-context": Patch,
"guild-header-popout": Patch "guild-header-popout": Patch

View file

@ -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.url)} action={() => openImageModal(track.album.image)}
icon={ImageIcon} icon={ImageIcon}
/> />
<Menu.MenuControlItem <Menu.MenuControlItem

View file

@ -33,7 +33,6 @@ export default definePlugin({
name: "SpotifyControls", name: "SpotifyControls",
description: "Adds a Spotify player above the account panel", description: "Adds a Spotify player above the account panel",
authors: [Devs.Ven, Devs.afn, Devs.KraXen72, Devs.Av32000], authors: [Devs.Ven, Devs.afn, Devs.KraXen72, Devs.Av32000],
dependencies: ["ImageModalAPI"],
options: { options: {
hoverControls: { hoverControls: {
description: "Show controls on hover", description: "Show controls on hover",

View file

@ -67,7 +67,10 @@ const settings = definePluginSettings({
} }
}); });
function openImage(url: string) { const openAvatar = (url: string) => openImage(url, 512, 512);
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);
@ -76,11 +79,13 @@ function openImage(url: string) {
url = u.toString(); url = u.toString();
u.searchParams.set("size", "4096"); u.searchParams.set("size", "4096");
const originalUrl = u.toString(); const original = u.toString();
openImageModal(url, { openImageModal({
original: originalUrl, url,
height: 256 original,
width,
height
}); });
} }
@ -93,14 +98,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={() => openImage(IconUtils.getUserAvatarURL(user, true))} action={() => openAvatar(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={() => openImage(IconUtils.getGuildMemberAvatarURLSimple({ action={() => openAvatar(IconUtils.getGuildMemberAvatarURLSimple({
userId: user.id, userId: user.id,
avatar: memberAvatar, avatar: memberAvatar,
guildId: guildId!, guildId: guildId!,
@ -126,7 +131,7 @@ const GuildContext: NavContextMenuPatchCallback = (children, { guild }: GuildCon
id="view-icon" id="view-icon"
label="View Icon" label="View Icon"
action={() => action={() =>
openImage(IconUtils.getGuildIconURL({ openAvatar(IconUtils.getGuildIconURL({
id, id,
icon, icon,
canAnimate: true canAnimate: true
@ -140,7 +145,7 @@ const GuildContext: NavContextMenuPatchCallback = (children, { guild }: GuildCon
id="view-banner" id="view-banner"
label="View Banner" label="View Banner"
action={() => action={() =>
openImage(IconUtils.getGuildBannerURL(guild, true)!) openBanner(IconUtils.getGuildBannerURL(guild, true)!)
} }
icon={ImageIcon} icon={ImageIcon}
/> />
@ -158,7 +163,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={() =>
openImage(IconUtils.getChannelIconURL(channel)!) openAvatar(IconUtils.getChannelIconURL(channel)!)
} }
icon={ImageIcon} icon={ImageIcon}
/> />
@ -170,12 +175,13 @@ export default definePlugin({
name: "ViewIcons", name: "ViewIcons",
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.",
dependencies: ["ImageModalAPI"],
tags: ["ImageUtilities"], tags: ["ImageUtilities"],
dependencies: ["DynamicImageModalAPI"],
settings, settings,
openImage, openAvatar,
openBanner,
contextMenus: { contextMenus: {
"user-context": UserContext, "user-context": UserContext,
@ -189,7 +195,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.openImage($1)}," replace: "$&style:{cursor:\"pointer\"},onClick:()=>{$self.openAvatar($1)},"
}, },
all: true all: true
}, },
@ -198,7 +204,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.openImage(${bannerSrc}),style:{cursor:${bannerSrc}!=null?"pointer":void 0,` replace: (_, rest, bannerSrc) => `${rest},onClick:()=>${bannerSrc}!=null&&$self.openBanner(${bannerSrc}),style:{cursor:${bannerSrc}!=null?"pointer":void 0,`
} }
}, },
// Group DMs top small & large icon // Group DMs top small & large icon
@ -206,7 +212,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.openImage(${iconUrl})` replace: (m, iconUrl) => `${m},onClick:()=>$self.openAvatar(${iconUrl})`
} }
}, },
// User DMs top small icon // User DMs top small icon
@ -214,7 +220,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.openImage(${avatarUrl})` replace: (m, avatarUrl) => `${m},onClick:()=>$self.openAvatar(${avatarUrl})`
} }
}, },
// User Dms top large icon // User Dms top large icon
@ -222,7 +228,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.openImage(${avatarUrl})` replace: (m, avatarUrl) => `${m},onClick:()=>$self.openAvatar(${avatarUrl})`
} }
} }
] ]

View file

@ -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/ArmCord, 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/Legcord, not inside your browser",
authors: [Devs.Ven], authors: [Devs.Ven],
enabledByDefault: true, enabledByDefault: true,

View file

@ -928,6 +928,10 @@ export const EquicordDevs = Object.freeze({
name: "Leko", name: "Leko",
id: 108153734541942784n id: 108153734541942784n
}, },
SomeAspy: {
name: "SomeAspy",
id: 516750892372852754n,
},
} satisfies Record<string, Dev>); } satisfies Record<string, Dev>);
// iife so #__PURE__ works correctly // iife so #__PURE__ works correctly

View file

@ -1,3 +1,25 @@
.vc-imagemodal-fix {
.vc-position-inherit {
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
}
}

View file

@ -19,10 +19,10 @@
import "./discord.css"; import "./discord.css";
import { MessageObject } from "@api/MessageEvents"; import { MessageObject } from "@api/MessageEvents";
import { ChannelStore, ComponentDispatch, Constants, FluxDispatcher, GuildStore, InviteActions, MaskedLink, MessageActions, PrivateChannelsStore, RestAPI, SelectedChannelStore, SelectedGuildStore, UserProfileActions, UserProfileStore, UserSettingsActionCreators, UserUtils } from "@webpack/common"; import { ChannelStore, ComponentDispatch, Constants, FluxDispatcher, GuildStore, InviteActions, MessageActions, 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, openModal } from "./modal"; import { ImageModal, ImageModalItem, openModal } from "./modal";
/** /**
* Open the invite modal * Open the invite modal
@ -110,23 +110,23 @@ export function sendMessage(
return MessageActions.sendMessage(channelId, messageData, waitForChannelReady, extra); return MessageActions.sendMessage(channelId, messageData, waitForChannelReady, extra);
} }
const FIX_CLASS_NAME = "vc-imagemodal-fix"; /**
* You must specify either height or width
export function openImageModal(url: string, props?: Partial<React.ComponentProps<ImageModal>>): string { */
export function openImageModal(props: Omit<ImageModalItem, "type">): string {
return openModal(modalProps => ( return openModal(modalProps => (
<ImageModal <ImageModal
{...modalProps} {...modalProps}
renderLinkComponent={props => <MaskedLink {...props} />} className="vc-image-modal"
// Don't render forward message button scaleDown_f97a12 contain_f97a12 fit="vc-position-inherit"
renderForwardComponent={() => null}
shouldHideMediaOptions={false}
shouldAnimate={true}
fit={FIX_CLASS_NAME}
items={[{ items={[{
...props,
type: "IMAGE", type: "IMAGE",
url, original: props.url,
...props,
}]} }]}
onClose={modalProps.onClose}
shouldHideMediaOptions={false}
shouldAnimate
/> />
)); ));
} }

View file

@ -101,23 +101,21 @@ export const Modals = findByPropsLazy("ModalRoot", "ModalCloseButton") as {
}>; }>;
}; };
// FIXME: type this export interface ImageModalItem {
export type ImageModal = any & ComponentType<{ type: "IMAGE" | "VIDEO";
className?: string; url: string;
src: string;
placeholder: string;
original: string;
width?: number; width?: number;
height?: number; height?: number;
animated?: boolean; original?: string;
responsive?: boolean; }
renderLinkComponent(props: any): ReactNode;
renderForwardComponent(props: any): ReactNode; export type ImageModal = ComponentType<{
maxWidth?: number; className?: string;
maxHeight?: number; fit?: string;
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") as ImageModal;