mirror of
https://github.com/Vendicated/Vencord.git
synced 2025-01-10 09:56:24 +00:00
Merge branch 'dev' into feat/vcnarrator-ignoreself
This commit is contained in:
commit
0cf09f5595
22 changed files with 232 additions and 136 deletions
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "vencord",
|
"name": "vencord",
|
||||||
"private": "true",
|
"private": "true",
|
||||||
"version": "1.6.8",
|
"version": "1.6.9",
|
||||||
"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": {
|
||||||
|
|
|
@ -21,7 +21,7 @@ import { Settings } from "@api/Settings";
|
||||||
import { classNameFactory } from "@api/Styles";
|
import { classNameFactory } from "@api/Styles";
|
||||||
import { closeModal, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
import { closeModal, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
||||||
import { useAwaiter } from "@utils/react";
|
import { useAwaiter } from "@utils/react";
|
||||||
import { Alerts, Button, Forms, moment, React, Text, Timestamp, useEffect, useReducer, useState } from "@webpack/common";
|
import { Alerts, Button, Forms, React, Text, Timestamp, useEffect, useReducer, useState } from "@webpack/common";
|
||||||
import { nanoid } from "nanoid";
|
import { nanoid } from "nanoid";
|
||||||
import type { DispatchWithoutAction } from "react";
|
import type { DispatchWithoutAction } from "react";
|
||||||
|
|
||||||
|
@ -129,7 +129,7 @@ function NotificationEntry({ data }: { data: PersistentNotificationData; }) {
|
||||||
richBody={
|
richBody={
|
||||||
<div className={cl("body")}>
|
<div className={cl("body")}>
|
||||||
{data.body}
|
{data.body}
|
||||||
<Timestamp timestamp={moment(data.timestamp)} className={cl("timestamp")} />
|
<Timestamp timestamp={new Date(data.timestamp)} className={cl("timestamp")} />
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -57,6 +57,7 @@ const settings = definePluginSettings({
|
||||||
});
|
});
|
||||||
|
|
||||||
let hasCrashedOnce = false;
|
let hasCrashedOnce = false;
|
||||||
|
let isRecovering = false;
|
||||||
let shouldAttemptRecover = true;
|
let shouldAttemptRecover = true;
|
||||||
|
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
|
@ -71,22 +72,30 @@ export default definePlugin({
|
||||||
{
|
{
|
||||||
find: ".Messages.ERRORS_UNEXPECTED_CRASH",
|
find: ".Messages.ERRORS_UNEXPECTED_CRASH",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(?=this\.setState\()/,
|
match: /this\.setState\((.+?)\)/,
|
||||||
replace: "$self.handleCrash(this);"
|
replace: "$self.handleCrash(this,$1);"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
handleCrash(_this: any) {
|
handleCrash(_this: any, errorState: any) {
|
||||||
|
_this.setState(errorState);
|
||||||
|
|
||||||
|
// Already recovering, prevent error which happens more than once too fast to trigger another recover
|
||||||
|
if (isRecovering) return;
|
||||||
|
isRecovering = true;
|
||||||
|
|
||||||
// 1 ms timeout to avoid react breaking when re-rendering
|
// 1 ms timeout to avoid react breaking when re-rendering
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
try {
|
||||||
|
// Prevent a crash loop with an error that could not be handled
|
||||||
if (!shouldAttemptRecover) {
|
if (!shouldAttemptRecover) {
|
||||||
try {
|
try {
|
||||||
showNotification({
|
showNotification({
|
||||||
color: "#eed202",
|
color: "#eed202",
|
||||||
title: "Discord has crashed!",
|
title: "Discord has crashed!",
|
||||||
body: "Awn :( Discord has crashed two times rapidly, not attempting to recover.",
|
body: "Awn :( Discord has crashed two times rapidly, not attempting to recover.",
|
||||||
noPersist: true,
|
noPersist: true
|
||||||
});
|
});
|
||||||
} catch { }
|
} catch { }
|
||||||
|
|
||||||
|
@ -96,13 +105,16 @@ export default definePlugin({
|
||||||
shouldAttemptRecover = false;
|
shouldAttemptRecover = false;
|
||||||
// This is enough to avoid a crash loop
|
// This is enough to avoid a crash loop
|
||||||
setTimeout(() => shouldAttemptRecover = true, 500);
|
setTimeout(() => shouldAttemptRecover = true, 500);
|
||||||
|
} catch { }
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!hasCrashedOnce) {
|
if (!hasCrashedOnce) {
|
||||||
hasCrashedOnce = true;
|
hasCrashedOnce = true;
|
||||||
maybePromptToUpdate("Uh oh, Discord has just crashed... but good news, there is a Vencord update available that might fix this issue! Would you like to update now?", true);
|
maybePromptToUpdate("Uh oh, Discord has just crashed... but good news, there is a Vencord update available that might fix this issue! Would you like to update now?", true);
|
||||||
}
|
}
|
||||||
|
} catch { }
|
||||||
|
|
||||||
|
try {
|
||||||
if (settings.store.attemptToPreventCrashes) {
|
if (settings.store.attemptToPreventCrashes) {
|
||||||
this.handlePreventCrash(_this);
|
this.handlePreventCrash(_this);
|
||||||
}
|
}
|
||||||
|
@ -118,7 +130,7 @@ export default definePlugin({
|
||||||
color: "#eed202",
|
color: "#eed202",
|
||||||
title: "Discord has crashed!",
|
title: "Discord has crashed!",
|
||||||
body: "Attempting to recover...",
|
body: "Attempting to recover...",
|
||||||
noPersist: true,
|
noPersist: true
|
||||||
});
|
});
|
||||||
} catch { }
|
} catch { }
|
||||||
|
|
||||||
|
@ -169,6 +181,10 @@ export default definePlugin({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Set isRecovering to false before setting the state to allow us to handle the next crash error correcty, in case it happens
|
||||||
|
setImmediate(() => isRecovering = false);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
_this.setState({ error: null, info: null });
|
_this.setState({ error: null, info: null });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|
|
@ -838,7 +838,7 @@ export default definePlugin({
|
||||||
url.searchParams.set("name", emoji.name);
|
url.searchParams.set("name", emoji.name);
|
||||||
|
|
||||||
messageObj.content = messageObj.content.replace(emojiString, (match, offset, origStr) => {
|
messageObj.content = messageObj.content.replace(emojiString, (match, offset, origStr) => {
|
||||||
return `${getWordBoundary(origStr, offset - 1)}${s.useHyperLinks ? `[:${emoji.name}:](${url})` : url}${getWordBoundary(origStr, offset + match.length)}`;
|
return `${getWordBoundary(origStr, offset - 1)}${s.useHyperLinks ? `[${emoji.name}](${url})` : url}${getWordBoundary(origStr, offset + match.length)}`;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -864,7 +864,7 @@ export default definePlugin({
|
||||||
url.searchParams.set("size", s.emojiSize.toString());
|
url.searchParams.set("size", s.emojiSize.toString());
|
||||||
url.searchParams.set("name", emoji.name);
|
url.searchParams.set("name", emoji.name);
|
||||||
|
|
||||||
return `${getWordBoundary(origStr, offset - 1)}${s.useHyperLinks ? `[:${emoji.name}:](${url})` : url}${getWordBoundary(origStr, offset + emojiStr.length)}`;
|
return `${getWordBoundary(origStr, offset - 1)}${s.useHyperLinks ? `[${emoji.name}](${url})` : url}${getWordBoundary(origStr, offset + emojiStr.length)}`;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
|
@ -123,14 +123,13 @@ export const Magnifier: React.FC<MagnifierProps> = ({ instance, size: initialSiz
|
||||||
waitFor(() => instance.state.readyState === "READY", () => {
|
waitFor(() => instance.state.readyState === "READY", () => {
|
||||||
const elem = document.getElementById(ELEMENT_ID) as HTMLDivElement;
|
const elem = document.getElementById(ELEMENT_ID) as HTMLDivElement;
|
||||||
element.current = elem;
|
element.current = elem;
|
||||||
elem.firstElementChild!.setAttribute("draggable", "false");
|
elem.querySelector("img,video")?.setAttribute("draggable", "false");
|
||||||
if (instance.props.animated) {
|
if (instance.props.animated) {
|
||||||
originalVideoElementRef.current = elem!.querySelector("video")!;
|
originalVideoElementRef.current = elem!.querySelector("video")!;
|
||||||
originalVideoElementRef.current.addEventListener("timeupdate", syncVideos);
|
originalVideoElementRef.current.addEventListener("timeupdate", syncVideos);
|
||||||
setReady(true);
|
|
||||||
} else {
|
|
||||||
setReady(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setReady(true);
|
||||||
});
|
});
|
||||||
document.addEventListener("keydown", onKeyDown);
|
document.addEventListener("keydown", onKeyDown);
|
||||||
document.addEventListener("keyup", onKeyUp);
|
document.addEventListener("keyup", onKeyUp);
|
||||||
|
@ -155,7 +154,9 @@ export const Magnifier: React.FC<MagnifierProps> = ({ instance, size: initialSiz
|
||||||
|
|
||||||
if (!ready) return null;
|
if (!ready) return null;
|
||||||
|
|
||||||
const box = element.current!.getBoundingClientRect();
|
const box = element.current?.getBoundingClientRect();
|
||||||
|
|
||||||
|
if (!box) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
|
|
@ -171,7 +171,7 @@ export default definePlugin({
|
||||||
find: "handleImageLoad=",
|
find: "handleImageLoad=",
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
match: /showThumbhashPlaceholder:\i,/,
|
match: /placeholderVersion:\i,/,
|
||||||
replace: "...$self.makeProps(this),$&"
|
replace: "...$self.makeProps(this),$&"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -29,15 +29,17 @@ import {
|
||||||
ChannelStore,
|
ChannelStore,
|
||||||
FluxDispatcher,
|
FluxDispatcher,
|
||||||
GuildStore,
|
GuildStore,
|
||||||
|
IconUtils,
|
||||||
MessageStore,
|
MessageStore,
|
||||||
Parser,
|
Parser,
|
||||||
|
PermissionsBits,
|
||||||
PermissionStore,
|
PermissionStore,
|
||||||
RestAPI,
|
RestAPI,
|
||||||
Text,
|
Text,
|
||||||
TextAndImagesSettingsStores,
|
TextAndImagesSettingsStores,
|
||||||
UserStore
|
UserStore
|
||||||
} from "@webpack/common";
|
} from "@webpack/common";
|
||||||
import { Channel, Guild, Message } from "discord-types/general";
|
import { Channel, Message } from "discord-types/general";
|
||||||
|
|
||||||
const messageCache = new Map<string, {
|
const messageCache = new Map<string, {
|
||||||
message?: Message;
|
message?: Message;
|
||||||
|
@ -49,8 +51,9 @@ const AutoModEmbed = findComponentByCodeLazy(".withFooter]:", "childrenMessageCo
|
||||||
const ChannelMessage = findComponentByCodeLazy("renderSimpleAccessories)");
|
const ChannelMessage = findComponentByCodeLazy("renderSimpleAccessories)");
|
||||||
|
|
||||||
const SearchResultClasses = findByPropsLazy("message", "searchResult");
|
const SearchResultClasses = findByPropsLazy("message", "searchResult");
|
||||||
|
const EmbedClasses = findByPropsLazy("embedAuthorIcon", "embedAuthor", "embedAuthor");
|
||||||
|
|
||||||
const messageLinkRegex = /(?<!<)https?:\/\/(?:\w+\.)?discord(?:app)?\.com\/channels\/(\d{17,20}|@me)\/(\d{17,20})\/(\d{17,20})/g;
|
const messageLinkRegex = /(?<!<)https?:\/\/(?:\w+\.)?discord(?:app)?\.com\/channels\/(?:\d{17,20}|@me)\/(\d{17,20})\/(\d{17,20})/g;
|
||||||
const tenorRegex = /^https:\/\/(?:www\.)?tenor\.com\//;
|
const tenorRegex = /^https:\/\/(?:www\.)?tenor\.com\//;
|
||||||
|
|
||||||
interface Attachment {
|
interface Attachment {
|
||||||
|
@ -63,7 +66,6 @@ interface Attachment {
|
||||||
interface MessageEmbedProps {
|
interface MessageEmbedProps {
|
||||||
message: Message;
|
message: Message;
|
||||||
channel: Channel;
|
channel: Channel;
|
||||||
guildID: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const messageFetchQueue = new Queue();
|
const messageFetchQueue = new Queue();
|
||||||
|
@ -226,19 +228,19 @@ function MessageEmbedAccessory({ message }: { message: Message; }) {
|
||||||
|
|
||||||
let match = null as RegExpMatchArray | null;
|
let match = null as RegExpMatchArray | null;
|
||||||
while ((match = messageLinkRegex.exec(message.content!)) !== null) {
|
while ((match = messageLinkRegex.exec(message.content!)) !== null) {
|
||||||
const [_, guildID, channelID, messageID] = match;
|
const [_, channelID, messageID] = match;
|
||||||
if (embeddedBy.includes(messageID)) {
|
if (embeddedBy.includes(messageID)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const linkedChannel = ChannelStore.getChannel(channelID);
|
const linkedChannel = ChannelStore.getChannel(channelID);
|
||||||
if (!linkedChannel || (guildID !== "@me" && !PermissionStore.can(1024n /* view channel */, linkedChannel))) {
|
if (!linkedChannel || (!linkedChannel.isPrivate() && !PermissionStore.can(PermissionsBits.VIEW_CHANNEL, linkedChannel))) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { listMode, idList } = settings.store;
|
const { listMode, idList } = settings.store;
|
||||||
|
|
||||||
const isListed = [guildID, channelID, message.author.id].some(id => id && idList.includes(id));
|
const isListed = [linkedChannel.guild_id, channelID, message.author.id].some(id => id && idList.includes(id));
|
||||||
|
|
||||||
if (listMode === "blacklist" && isListed) continue;
|
if (listMode === "blacklist" && isListed) continue;
|
||||||
if (listMode === "whitelist" && !isListed) continue;
|
if (listMode === "whitelist" && !isListed) continue;
|
||||||
|
@ -265,8 +267,7 @@ function MessageEmbedAccessory({ message }: { message: Message; }) {
|
||||||
|
|
||||||
const messageProps: MessageEmbedProps = {
|
const messageProps: MessageEmbedProps = {
|
||||||
message: withEmbeddedBy(linkedMessage, [...embeddedBy, message.id]),
|
message: withEmbeddedBy(linkedMessage, [...embeddedBy, message.id]),
|
||||||
channel: linkedChannel,
|
channel: linkedChannel
|
||||||
guildID
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const type = settings.store.automodEmbeds;
|
const type = settings.store.automodEmbeds;
|
||||||
|
@ -280,28 +281,28 @@ function MessageEmbedAccessory({ message }: { message: Message; }) {
|
||||||
return accessories.length ? <>{accessories}</> : null;
|
return accessories.length ? <>{accessories}</> : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function ChannelMessageEmbedAccessory({ message, channel, guildID }: MessageEmbedProps): JSX.Element | null {
|
function getChannelLabelAndIconUrl(channel: Channel) {
|
||||||
const isDM = guildID === "@me";
|
if (channel.isDM()) return ["Direct Message", IconUtils.getUserAvatarURL(UserStore.getUser(channel.recipients[0]))];
|
||||||
|
if (channel.isGroupDM()) return ["Group DM", IconUtils.getChannelIconURL(channel)];
|
||||||
|
return ["Server", IconUtils.getGuildIconURL(GuildStore.getGuild(channel.guild_id))];
|
||||||
|
}
|
||||||
|
|
||||||
const guild = !isDM && GuildStore.getGuild(channel.guild_id);
|
function ChannelMessageEmbedAccessory({ message, channel }: MessageEmbedProps): JSX.Element | null {
|
||||||
const dmReceiver = UserStore.getUser(ChannelStore.getChannel(channel.id).recipients?.[0]);
|
const dmReceiver = UserStore.getUser(ChannelStore.getChannel(channel.id).recipients?.[0]);
|
||||||
|
|
||||||
|
const [channelLabel, iconUrl] = getChannelLabelAndIconUrl(channel);
|
||||||
|
|
||||||
return <Embed
|
return (
|
||||||
|
<Embed
|
||||||
embed={{
|
embed={{
|
||||||
rawDescription: "",
|
rawDescription: "",
|
||||||
color: "var(--background-secondary)",
|
color: "var(--background-secondary)",
|
||||||
author: {
|
author: {
|
||||||
name: <Text variant="text-xs/medium" tag="span">
|
name: <Text variant="text-xs/medium" tag="span">
|
||||||
<span>{isDM ? "Direct Message - " : (guild as Guild).name + " - "}</span>
|
<span>{channelLabel} - </span>
|
||||||
{isDM
|
{Parser.parse(channel.isDM() ? `<@${dmReceiver.id}>` : `<#${channel.id}>`)}
|
||||||
? Parser.parse(`<@${dmReceiver.id}>`)
|
|
||||||
: Parser.parse(`<#${channel.id}>`)
|
|
||||||
}
|
|
||||||
</Text>,
|
</Text>,
|
||||||
iconProxyURL: guild
|
iconProxyURL: iconUrl
|
||||||
? `https://${window.GLOBAL_ENV.CDN_HOST}/icons/${guild.id}/${guild.icon}.png`
|
|
||||||
: `https://${window.GLOBAL_ENV.CDN_HOST}/avatars/${dmReceiver.id}/${dmReceiver.avatar}`
|
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
renderDescription={() => (
|
renderDescription={() => (
|
||||||
|
@ -314,25 +315,30 @@ function ChannelMessageEmbedAccessory({ message, channel, guildID }: MessageEmbe
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
/>;
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function AutomodEmbedAccessory(props: MessageEmbedProps): JSX.Element | null {
|
function AutomodEmbedAccessory(props: MessageEmbedProps): JSX.Element | null {
|
||||||
const { message, channel, guildID } = props;
|
const { message, channel } = props;
|
||||||
const compact = TextAndImagesSettingsStores.MessageDisplayCompact.useSetting();
|
const compact = TextAndImagesSettingsStores.MessageDisplayCompact.useSetting();
|
||||||
const isDM = guildID === "@me";
|
|
||||||
const images = getImages(message);
|
const images = getImages(message);
|
||||||
const { parse } = Parser;
|
const { parse } = Parser;
|
||||||
|
|
||||||
|
const [channelLabel, iconUrl] = getChannelLabelAndIconUrl(channel);
|
||||||
|
|
||||||
return <AutoModEmbed
|
return <AutoModEmbed
|
||||||
channel={channel}
|
channel={channel}
|
||||||
childrenAccessories={
|
childrenAccessories={
|
||||||
<Text color="text-muted" variant="text-xs/medium" tag="span">
|
<Text color="text-muted" variant="text-xs/medium" tag="span" className={`${EmbedClasses.embedAuthor} ${EmbedClasses.embedMargin}`}>
|
||||||
{isDM
|
{iconUrl && <img src={iconUrl} className={EmbedClasses.embedAuthorIcon} alt="" />}
|
||||||
? parse(`<@${ChannelStore.getChannel(channel.id).recipients[0]}>`)
|
<span>
|
||||||
: parse(`<#${channel.id}>`)
|
<span>{channelLabel} - </span>
|
||||||
|
{channel.isDM()
|
||||||
|
? Parser.parse(`<@${ChannelStore.getChannel(channel.id).recipients[0]}>`)
|
||||||
|
: Parser.parse(`<#${channel.id}>`)
|
||||||
}
|
}
|
||||||
<span>{isDM ? " - Direct Message" : " - " + GuildStore.getGuild(channel.guild_id)?.name}</span>
|
</span>
|
||||||
</Text>
|
</Text>
|
||||||
}
|
}
|
||||||
compact={compact}
|
compact={compact}
|
||||||
|
|
|
@ -26,7 +26,7 @@ import { Devs } from "@utils/constants";
|
||||||
import { Logger } from "@utils/Logger";
|
import { Logger } from "@utils/Logger";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { findByPropsLazy } from "@webpack";
|
import { findByPropsLazy } from "@webpack";
|
||||||
import { ChannelStore, FluxDispatcher, i18n, Menu, moment, Parser, Timestamp, UserStore } from "@webpack/common";
|
import { ChannelStore, FluxDispatcher, i18n, Menu, Parser, Timestamp, UserStore } from "@webpack/common";
|
||||||
|
|
||||||
import overlayStyle from "./deleteStyleOverlay.css?managed";
|
import overlayStyle from "./deleteStyleOverlay.css?managed";
|
||||||
import textStyle from "./deleteStyleText.css?managed";
|
import textStyle from "./deleteStyleText.css?managed";
|
||||||
|
@ -122,7 +122,7 @@ export default definePlugin({
|
||||||
|
|
||||||
makeEdit(newMessage: any, oldMessage: any): any {
|
makeEdit(newMessage: any, oldMessage: any): any {
|
||||||
return {
|
return {
|
||||||
timestamp: moment?.call(newMessage.edited_timestamp),
|
timestamp: new Date(newMessage.edited_timestamp),
|
||||||
content: oldMessage.content
|
content: oldMessage.content
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
|
@ -20,11 +20,10 @@ import { Devs } from "@utils/constants";
|
||||||
import { isNonNullish } from "@utils/guards";
|
import { isNonNullish } from "@utils/guards";
|
||||||
import definePlugin from "@utils/types";
|
import definePlugin from "@utils/types";
|
||||||
import { findByPropsLazy } from "@webpack";
|
import { findByPropsLazy } from "@webpack";
|
||||||
import { Avatar, ChannelStore, Clickable, RelationshipStore, ScrollerThin, UserStore } from "@webpack/common";
|
import { Avatar, ChannelStore, Clickable, IconUtils, RelationshipStore, ScrollerThin, UserStore } from "@webpack/common";
|
||||||
import { Channel, User } from "discord-types/general";
|
import { Channel, User } from "discord-types/general";
|
||||||
|
|
||||||
const SelectedChannelActionCreators = findByPropsLazy("selectPrivateChannel");
|
const SelectedChannelActionCreators = findByPropsLazy("selectPrivateChannel");
|
||||||
const AvatarUtils = findByPropsLazy("getChannelIconURL");
|
|
||||||
const UserUtils = findByPropsLazy("getGlobalName");
|
const UserUtils = findByPropsLazy("getGlobalName");
|
||||||
|
|
||||||
const ProfileListClasses = findByPropsLazy("emptyIconFriends", "emptyIconGuilds");
|
const ProfileListClasses = findByPropsLazy("emptyIconFriends", "emptyIconGuilds");
|
||||||
|
@ -71,7 +70,7 @@ export default definePlugin({
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Avatar
|
<Avatar
|
||||||
src={AvatarUtils.getChannelIconURL({ id: c.id, icon: c.icon, size: 32 })}
|
src={IconUtils.getChannelIconURL({ id: c.id, icon: c.icon, size: 32 })}
|
||||||
size="SIZE_40"
|
size="SIZE_40"
|
||||||
className={ProfileListClasses.listAvatar}
|
className={ProfileListClasses.listAvatar}
|
||||||
>
|
>
|
||||||
|
|
|
@ -16,16 +16,18 @@
|
||||||
* 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 { definePluginSettings } from "@api/Settings";
|
import { definePluginSettings,migratePluginSettings } from "@api/Settings";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { findByPropsLazy } from "@webpack";
|
import { findByPropsLazy } from "@webpack";
|
||||||
|
|
||||||
const { updateGuildNotificationSettings } = findByPropsLazy("updateGuildNotificationSettings");
|
const { updateGuildNotificationSettings } = findByPropsLazy("updateGuildNotificationSettings");
|
||||||
|
const { toggleShowAllChannels } = findByPropsLazy("toggleShowAllChannels");
|
||||||
|
const { isOptInEnabledForGuild } = findByPropsLazy("isOptInEnabledForGuild");
|
||||||
|
|
||||||
const settings = definePluginSettings({
|
const settings = definePluginSettings({
|
||||||
guild: {
|
guild: {
|
||||||
description: "Mute Guild",
|
description: "Mute Guild automatically",
|
||||||
type: OptionType.BOOLEAN,
|
type: OptionType.BOOLEAN,
|
||||||
default: true
|
default: true
|
||||||
},
|
},
|
||||||
|
@ -38,13 +40,20 @@ const settings = definePluginSettings({
|
||||||
description: "Suppress All Role @mentions",
|
description: "Suppress All Role @mentions",
|
||||||
type: OptionType.BOOLEAN,
|
type: OptionType.BOOLEAN,
|
||||||
default: true
|
default: true
|
||||||
|
},
|
||||||
|
showAllChannels: {
|
||||||
|
description: "Show all channels automatically",
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
default: true
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
migratePluginSettings("NewGuildSettings", "MuteNewGuild");
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "MuteNewGuild",
|
name: "NewGuildSettings",
|
||||||
description: "Mutes newly joined guilds",
|
description: "Automatically mute new servers and change various other settings upon joining",
|
||||||
authors: [Devs.Glitch, Devs.Nuckyz, Devs.carince],
|
tags: ["MuteNewGuild", "mute", "server"],
|
||||||
|
authors: [Devs.Glitch, Devs.Nuckyz, Devs.carince, Devs.Mopi],
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
find: ",acceptInvite(",
|
find: ",acceptInvite(",
|
||||||
|
@ -70,7 +79,9 @@ export default definePlugin({
|
||||||
muted: settings.store.guild,
|
muted: settings.store.guild,
|
||||||
suppress_everyone: settings.store.everyone,
|
suppress_everyone: settings.store.everyone,
|
||||||
suppress_roles: settings.store.role
|
suppress_roles: settings.store.role
|
||||||
|
});
|
||||||
|
if (settings.store.showAllChannels && isOptInEnabledForGuild(guildId)) {
|
||||||
|
toggleShowAllChannels(guildId);
|
||||||
}
|
}
|
||||||
);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
|
@ -59,7 +59,7 @@ export function authorize(callback?: any) {
|
||||||
const url = new URL(response.location);
|
const url = new URL(response.location);
|
||||||
url.searchParams.append("clientMod", "vencord");
|
url.searchParams.append("clientMod", "vencord");
|
||||||
const res = await fetch(url, {
|
const res = await fetch(url, {
|
||||||
headers: new Headers({ Accept: "application/json" })
|
headers: { Accept: "application/json" }
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
|
|
|
@ -20,7 +20,7 @@ import { openUserProfile } from "@utils/discord";
|
||||||
import { classes } from "@utils/misc";
|
import { classes } from "@utils/misc";
|
||||||
import { LazyComponent } from "@utils/react";
|
import { LazyComponent } from "@utils/react";
|
||||||
import { filters, findBulk } from "@webpack";
|
import { filters, findBulk } from "@webpack";
|
||||||
import { Alerts, moment, Parser, Timestamp, useState } from "@webpack/common";
|
import { Alerts, Parser, Timestamp, useState } from "@webpack/common";
|
||||||
|
|
||||||
import { Auth, getToken } from "../auth";
|
import { Auth, getToken } from "../auth";
|
||||||
import { Review, ReviewType } from "../entities";
|
import { Review, ReviewType } from "../entities";
|
||||||
|
@ -163,7 +163,7 @@ export default LazyComponent(() => {
|
||||||
|
|
||||||
{
|
{
|
||||||
!settings.store.hideTimestamps && review.type !== ReviewType.System && (
|
!settings.store.hideTimestamps && review.type !== ReviewType.System && (
|
||||||
<Timestamp timestamp={moment(review.timestamp * 1000)} >
|
<Timestamp timestamp={new Date(review.timestamp * 1000)} >
|
||||||
{dateFormat.format(review.timestamp * 1000)}
|
{dateFormat.format(review.timestamp * 1000)}
|
||||||
</Timestamp>)
|
</Timestamp>)
|
||||||
}
|
}
|
||||||
|
|
|
@ -118,10 +118,10 @@ export async function addReview(review: any): Promise<Response | null> {
|
||||||
export async function deleteReview(id: number): Promise<Response | null> {
|
export async function deleteReview(id: number): Promise<Response | null> {
|
||||||
return await rdbRequest(`/users/${id}/reviews`, {
|
return await rdbRequest(`/users/${id}/reviews`, {
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
headers: new Headers({
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
Accept: "application/json",
|
Accept: "application/json",
|
||||||
}),
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
reviewid: id
|
reviewid: id
|
||||||
})
|
})
|
||||||
|
@ -135,10 +135,10 @@ export async function deleteReview(id: number): Promise<Response | null> {
|
||||||
export async function reportReview(id: number) {
|
export async function reportReview(id: number) {
|
||||||
const res = await rdbRequest("/reports", {
|
const res = await rdbRequest("/reports", {
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
headers: new Headers({
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
Accept: "application/json",
|
Accept: "application/json",
|
||||||
}),
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
reviewid: id,
|
reviewid: id,
|
||||||
})
|
})
|
||||||
|
@ -150,10 +150,10 @@ export async function reportReview(id: number) {
|
||||||
async function patchBlock(action: "block" | "unblock", userId: string) {
|
async function patchBlock(action: "block" | "unblock", userId: string) {
|
||||||
const res = await rdbRequest("/blocks", {
|
const res = await rdbRequest("/blocks", {
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
headers: new Headers({
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
Accept: "application/json",
|
Accept: "application/json",
|
||||||
}),
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
action: action,
|
action: action,
|
||||||
discordId: userId
|
discordId: userId
|
||||||
|
@ -180,9 +180,9 @@ export const unblockUser = (userId: string) => patchBlock("unblock", userId);
|
||||||
export async function fetchBlocks(): Promise<ReviewDBUser[]> {
|
export async function fetchBlocks(): Promise<ReviewDBUser[]> {
|
||||||
const res = await rdbRequest("/blocks", {
|
const res = await rdbRequest("/blocks", {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: new Headers({
|
headers: {
|
||||||
Accept: "application/json",
|
Accept: "application/json",
|
||||||
})
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!res.ok) throw new Error(`${res.status}: ${res.statusText}`);
|
if (!res.ok) throw new Error(`${res.status}: ${res.statusText}`);
|
||||||
|
|
|
@ -12,10 +12,9 @@ import { classes } from "@utils/misc";
|
||||||
import { ModalRoot, ModalSize, openModal } from "@utils/modal";
|
import { ModalRoot, ModalSize, openModal } from "@utils/modal";
|
||||||
import { useAwaiter } from "@utils/react";
|
import { useAwaiter } from "@utils/react";
|
||||||
import { findByPropsLazy, findExportedComponentLazy } from "@webpack";
|
import { findByPropsLazy, findExportedComponentLazy } from "@webpack";
|
||||||
import { FluxDispatcher, Forms, GuildChannelStore, GuildMemberStore, moment, Parser, PresenceStore, RelationshipStore, ScrollerThin, SnowflakeUtils, TabBar, Timestamp, useEffect, UserStore, UserUtils, useState, useStateFromStores } from "@webpack/common";
|
import { FluxDispatcher, Forms, GuildChannelStore, GuildMemberStore, IconUtils, Parser, PresenceStore, RelationshipStore, ScrollerThin, SnowflakeUtils, TabBar, Timestamp, useEffect, UserStore, UserUtils, useState, useStateFromStores } from "@webpack/common";
|
||||||
import { Guild, User } from "discord-types/general";
|
import { Guild, User } from "discord-types/general";
|
||||||
|
|
||||||
const IconUtils = findByPropsLazy("getGuildBannerURL");
|
|
||||||
const IconClasses = findByPropsLazy("icon", "acronym", "childWrapper");
|
const IconClasses = findByPropsLazy("icon", "acronym", "childWrapper");
|
||||||
const FriendRow = findExportedComponentLazy("FriendRow");
|
const FriendRow = findExportedComponentLazy("FriendRow");
|
||||||
|
|
||||||
|
@ -50,7 +49,7 @@ const fetched = {
|
||||||
|
|
||||||
function renderTimestamp(timestamp: number) {
|
function renderTimestamp(timestamp: number) {
|
||||||
return (
|
return (
|
||||||
<Timestamp timestamp={moment(timestamp)} />
|
<Timestamp timestamp={new Date(timestamp)} />
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,10 +64,7 @@ function GuildProfileModal({ guild }: GuildProps) {
|
||||||
|
|
||||||
const [currentTab, setCurrentTab] = useState(Tabs.ServerInfo);
|
const [currentTab, setCurrentTab] = useState(Tabs.ServerInfo);
|
||||||
|
|
||||||
const bannerUrl = guild.banner && IconUtils.getGuildBannerURL({
|
const bannerUrl = guild.banner && IconUtils.getGuildBannerURL(guild, true)!.replace(/\?size=\d+$/, "?size=1024");
|
||||||
id: guild.id,
|
|
||||||
banner: guild.banner
|
|
||||||
}, true).replace(/\?size=\d+$/, "?size=1024");
|
|
||||||
|
|
||||||
const iconUrl = guild.icon && IconUtils.getGuildIconURL({
|
const iconUrl = guild.icon && IconUtils.getGuildIconURL({
|
||||||
id: guild.id,
|
id: guild.id,
|
||||||
|
@ -89,7 +85,7 @@ function GuildProfileModal({ guild }: GuildProps) {
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className={cl("header")}>
|
<div className={cl("header")}>
|
||||||
{guild.icon
|
{iconUrl
|
||||||
? <img
|
? <img
|
||||||
src={iconUrl}
|
src={iconUrl}
|
||||||
alt=""
|
alt=""
|
||||||
|
@ -150,7 +146,7 @@ function Owner(guildId: string, owner: User) {
|
||||||
avatar: guildAvatar,
|
avatar: guildAvatar,
|
||||||
guildId,
|
guildId,
|
||||||
canAnimate: true
|
canAnimate: true
|
||||||
}, true)
|
})
|
||||||
: IconUtils.getUserAvatarURL(owner, true);
|
: IconUtils.getUserAvatarURL(owner, true);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -20,7 +20,7 @@ import { Settings } from "@api/Settings";
|
||||||
import ErrorBoundary from "@components/ErrorBoundary";
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { formatDuration } from "@utils/text";
|
import { formatDuration } from "@utils/text";
|
||||||
import { findByPropsLazy, findComponentByCodeLazy, findComponentLazy } from "@webpack";
|
import { findByPropsLazy, findComponentByCodeLazy, findComponentLazy } from "@webpack";
|
||||||
import { EmojiStore, FluxDispatcher, GuildMemberStore, GuildStore, moment, Parser, PermissionsBits, PermissionStore, SnowflakeUtils, Text, Timestamp, Tooltip, useEffect, useState } from "@webpack/common";
|
import { EmojiStore, FluxDispatcher, GuildMemberStore, GuildStore, Parser, PermissionsBits, PermissionStore, SnowflakeUtils, Text, Timestamp, Tooltip, useEffect, useState } from "@webpack/common";
|
||||||
import type { Channel } from "discord-types/general";
|
import type { Channel } from "discord-types/general";
|
||||||
|
|
||||||
import openRolesAndUsersPermissionsModal, { PermissionType, RoleOrUserPermission } from "../../permissionsViewer/components/RolesAndUsersPermissions";
|
import openRolesAndUsersPermissionsModal, { PermissionType, RoleOrUserPermission } from "../../permissionsViewer/components/RolesAndUsersPermissions";
|
||||||
|
@ -216,12 +216,12 @@ function HiddenChannelLockScreen({ channel }: { channel: ExtendedChannel; }) {
|
||||||
{lastMessageId &&
|
{lastMessageId &&
|
||||||
<Text variant="text-md/normal">
|
<Text variant="text-md/normal">
|
||||||
Last {channel.isForumChannel() ? "post" : "message"} created:
|
Last {channel.isForumChannel() ? "post" : "message"} created:
|
||||||
<Timestamp timestamp={moment(SnowflakeUtils.extractTimestamp(lastMessageId))} />
|
<Timestamp timestamp={new Date(SnowflakeUtils.extractTimestamp(lastMessageId))} />
|
||||||
</Text>
|
</Text>
|
||||||
}
|
}
|
||||||
|
|
||||||
{lastPinTimestamp &&
|
{lastPinTimestamp &&
|
||||||
<Text variant="text-md/normal">Last message pin: <Timestamp timestamp={moment(lastPinTimestamp)} /></Text>
|
<Text variant="text-md/normal">Last message pin: <Timestamp timestamp={new Date(lastPinTimestamp)} /></Text>
|
||||||
}
|
}
|
||||||
{(rateLimitPerUser ?? 0) > 0 &&
|
{(rateLimitPerUser ?? 0) > 0 &&
|
||||||
<Text variant="text-md/normal">Slowmode: {formatDuration(rateLimitPerUser!, "seconds")}</Text>
|
<Text variant="text-md/normal">Slowmode: {formatDuration(rateLimitPerUser!, "seconds")}</Text>
|
||||||
|
|
|
@ -22,11 +22,9 @@ import { ImageIcon } from "@components/Icons";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import { openImageModal } from "@utils/discord";
|
import { openImageModal } from "@utils/discord";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { findByPropsLazy } from "@webpack";
|
import { GuildMemberStore, IconUtils, Menu } from "@webpack/common";
|
||||||
import { GuildMemberStore, Menu } from "@webpack/common";
|
|
||||||
import type { Channel, Guild, User } from "discord-types/general";
|
import type { Channel, Guild, User } from "discord-types/general";
|
||||||
|
|
||||||
const BannerStore = findByPropsLazy("getGuildBannerURL");
|
|
||||||
|
|
||||||
interface UserContextProps {
|
interface UserContextProps {
|
||||||
channel: Channel;
|
channel: Channel;
|
||||||
|
@ -91,19 +89,19 @@ 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(BannerStore.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={() => openImage(BannerStore.getGuildMemberAvatarURLSimple({
|
action={() => openImage(IconUtils.getGuildMemberAvatarURLSimple({
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
avatar: memberAvatar,
|
avatar: memberAvatar,
|
||||||
guildId,
|
guildId: guildId!,
|
||||||
canAnimate: true
|
canAnimate: true
|
||||||
}, true))}
|
}))}
|
||||||
icon={ImageIcon}
|
icon={ImageIcon}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
@ -124,11 +122,11 @@ const GuildContext: NavContextMenuPatchCallback = (children, { guild }: GuildCon
|
||||||
id="view-icon"
|
id="view-icon"
|
||||||
label="View Icon"
|
label="View Icon"
|
||||||
action={() =>
|
action={() =>
|
||||||
openImage(BannerStore.getGuildIconURL({
|
openImage(IconUtils.getGuildIconURL({
|
||||||
id,
|
id,
|
||||||
icon,
|
icon,
|
||||||
canAnimate: true
|
canAnimate: true
|
||||||
}))
|
})!)
|
||||||
}
|
}
|
||||||
icon={ImageIcon}
|
icon={ImageIcon}
|
||||||
/>
|
/>
|
||||||
|
@ -138,10 +136,7 @@ const GuildContext: NavContextMenuPatchCallback = (children, { guild }: GuildCon
|
||||||
id="view-banner"
|
id="view-banner"
|
||||||
label="View Banner"
|
label="View Banner"
|
||||||
action={() =>
|
action={() =>
|
||||||
openImage(BannerStore.getGuildBannerURL({
|
openImage(IconUtils.getGuildBannerURL(guild, true)!)
|
||||||
id,
|
|
||||||
banner,
|
|
||||||
}, true))
|
|
||||||
}
|
}
|
||||||
icon={ImageIcon}
|
icon={ImageIcon}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -20,13 +20,15 @@ import "./styles.css";
|
||||||
|
|
||||||
import { addContextMenuPatch, NavContextMenuPatchCallback, removeContextMenuPatch } from "@api/ContextMenu";
|
import { addContextMenuPatch, NavContextMenuPatchCallback, removeContextMenuPatch } from "@api/ContextMenu";
|
||||||
import { Microphone } from "@components/Icons";
|
import { Microphone } from "@components/Icons";
|
||||||
|
import { Link } from "@components/Link";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
|
import { Margins } from "@utils/margins";
|
||||||
import { ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, openModal } from "@utils/modal";
|
import { ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, openModal } from "@utils/modal";
|
||||||
import { useAwaiter } from "@utils/react";
|
import { useAwaiter } from "@utils/react";
|
||||||
import definePlugin from "@utils/types";
|
import definePlugin from "@utils/types";
|
||||||
import { chooseFile } from "@utils/web";
|
import { chooseFile } from "@utils/web";
|
||||||
import { findByPropsLazy, findStoreLazy } from "@webpack";
|
import { findByPropsLazy, findStoreLazy } from "@webpack";
|
||||||
import { Button, FluxDispatcher, Forms, lodash, Menu, MessageActions, PermissionsBits, PermissionStore, RestAPI, SelectedChannelStore, showToast, SnowflakeUtils, Toasts, useEffect, useState } from "@webpack/common";
|
import { Button, Card, FluxDispatcher, Forms, lodash, Menu, MessageActions, PermissionsBits, PermissionStore, RestAPI, SelectedChannelStore, showToast, SnowflakeUtils, Toasts, useEffect, useState } from "@webpack/common";
|
||||||
import { ComponentType } from "react";
|
import { ComponentType } from "react";
|
||||||
|
|
||||||
import { VoiceRecorderDesktop } from "./DesktopRecorder";
|
import { VoiceRecorderDesktop } from "./DesktopRecorder";
|
||||||
|
@ -164,6 +166,11 @@ function Modal({ modalProps }: { modalProps: ModalProps; }) {
|
||||||
fallbackValue: EMPTY_META,
|
fallbackValue: EMPTY_META,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const isUnsupportedFormat = blob && (
|
||||||
|
!blob.type.startsWith("audio/ogg")
|
||||||
|
|| blob.type.includes("codecs") && !blob.type.includes("opus")
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ModalRoot {...modalProps}>
|
<ModalRoot {...modalProps}>
|
||||||
<ModalHeader>
|
<ModalHeader>
|
||||||
|
@ -200,6 +207,16 @@ function Modal({ modalProps }: { modalProps: ModalProps; }) {
|
||||||
recording={isRecording}
|
recording={isRecording}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{isUnsupportedFormat && (
|
||||||
|
<Card className={`vc-plugins-restart-card ${Margins.top16}`}>
|
||||||
|
<Forms.FormText>Voice Messages have to be OggOpus to be playable on iOS. This file is <code>{blob.type}</code> so it will not be playable on iOS.</Forms.FormText>
|
||||||
|
|
||||||
|
<Forms.FormText className={Margins.top8}>
|
||||||
|
To fix it, first convert it to OggOpus, for example using the <Link href="https://convertio.co/mp3-opus/">convertio web converter</Link>
|
||||||
|
</Forms.FormText>
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
|
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
|
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
|
|
|
@ -47,18 +47,23 @@ const settings = definePluginSettings({
|
||||||
});
|
});
|
||||||
|
|
||||||
const MEDIA_PROXY_URL = "https://media.discordapp.net";
|
const MEDIA_PROXY_URL = "https://media.discordapp.net";
|
||||||
const CDN_URL = "https://cdn.discordapp.com";
|
const CDN_URL = "cdn.discordapp.com";
|
||||||
|
|
||||||
function fixImageUrl(urlString: string, explodeWebp: boolean) {
|
function fixImageUrl(urlString: string) {
|
||||||
const url = new URL(urlString);
|
const url = new URL(urlString);
|
||||||
if (url.origin === CDN_URL) return urlString;
|
if (url.host === CDN_URL) return urlString;
|
||||||
if (url.origin === MEDIA_PROXY_URL) return CDN_URL + url.pathname;
|
|
||||||
|
|
||||||
url.searchParams.delete("width");
|
url.searchParams.delete("width");
|
||||||
url.searchParams.delete("height");
|
url.searchParams.delete("height");
|
||||||
|
|
||||||
|
if (url.origin === MEDIA_PROXY_URL) {
|
||||||
|
url.host = CDN_URL;
|
||||||
|
url.searchParams.delete("size");
|
||||||
|
url.searchParams.delete("quality");
|
||||||
|
url.searchParams.delete("format");
|
||||||
|
} else {
|
||||||
url.searchParams.set("quality", "lossless");
|
url.searchParams.set("quality", "lossless");
|
||||||
if (explodeWebp && url.searchParams.get("format") === "webp")
|
}
|
||||||
url.searchParams.set("format", "png");
|
|
||||||
|
|
||||||
return url.toString();
|
return url.toString();
|
||||||
}
|
}
|
||||||
|
@ -199,7 +204,7 @@ export default definePlugin({
|
||||||
],
|
],
|
||||||
|
|
||||||
async copyImage(url: string) {
|
async copyImage(url: string) {
|
||||||
url = fixImageUrl(url, true);
|
url = fixImageUrl(url);
|
||||||
|
|
||||||
let imageData = await fetch(url).then(r => r.blob());
|
let imageData = await fetch(url).then(r => r.blob());
|
||||||
if (imageData.type !== "image/png") {
|
if (imageData.type !== "image/png") {
|
||||||
|
@ -231,7 +236,7 @@ export default definePlugin({
|
||||||
},
|
},
|
||||||
|
|
||||||
async saveImage(url: string) {
|
async saveImage(url: string) {
|
||||||
url = fixImageUrl(url, false);
|
url = fixImageUrl(url);
|
||||||
|
|
||||||
const data = await fetchImage(url);
|
const data = await fetchImage(url);
|
||||||
if (!data) return;
|
if (!data) return;
|
||||||
|
|
|
@ -399,6 +399,10 @@ export const Devs = /* #__PURE__*/ Object.freeze({
|
||||||
name: "maisy",
|
name: "maisy",
|
||||||
id: 257109471589957632n,
|
id: 257109471589957632n,
|
||||||
},
|
},
|
||||||
|
Mopi: {
|
||||||
|
name: "Mopi",
|
||||||
|
id: 1022189106614243350n
|
||||||
|
},
|
||||||
Grzesiek11: {
|
Grzesiek11: {
|
||||||
name: "Grzesiek11",
|
name: "Grzesiek11",
|
||||||
id: 368475654662127616n,
|
id: 368475654662127616n,
|
||||||
|
|
3
src/webpack/common/types/components.d.ts
vendored
3
src/webpack/common/types/components.d.ts
vendored
|
@ -16,7 +16,6 @@
|
||||||
* 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 type { Moment } from "moment";
|
|
||||||
import type { ComponentType, CSSProperties, FunctionComponent, HtmlHTMLAttributes, HTMLProps, KeyboardEvent, MouseEvent, PropsWithChildren, PropsWithRef, ReactNode, Ref } from "react";
|
import type { ComponentType, CSSProperties, FunctionComponent, HtmlHTMLAttributes, HTMLProps, KeyboardEvent, MouseEvent, PropsWithChildren, PropsWithRef, ReactNode, Ref } from "react";
|
||||||
|
|
||||||
export type TextVariant = "heading-sm/normal" | "heading-sm/medium" | "heading-sm/semibold" | "heading-sm/bold" | "heading-md/normal" | "heading-md/medium" | "heading-md/semibold" | "heading-md/bold" | "heading-lg/normal" | "heading-lg/medium" | "heading-lg/semibold" | "heading-lg/bold" | "heading-xl/normal" | "heading-xl/medium" | "heading-xl/bold" | "heading-xxl/normal" | "heading-xxl/medium" | "heading-xxl/bold" | "eyebrow" | "heading-deprecated-14/normal" | "heading-deprecated-14/medium" | "heading-deprecated-14/bold" | "text-xxs/normal" | "text-xxs/medium" | "text-xxs/semibold" | "text-xxs/bold" | "text-xs/normal" | "text-xs/medium" | "text-xs/semibold" | "text-xs/bold" | "text-sm/normal" | "text-sm/medium" | "text-sm/semibold" | "text-sm/bold" | "text-md/normal" | "text-md/medium" | "text-md/semibold" | "text-md/bold" | "text-lg/normal" | "text-lg/medium" | "text-lg/semibold" | "text-lg/bold" | "display-sm" | "display-md" | "display-lg" | "code";
|
export type TextVariant = "heading-sm/normal" | "heading-sm/medium" | "heading-sm/semibold" | "heading-sm/bold" | "heading-md/normal" | "heading-md/medium" | "heading-md/semibold" | "heading-md/bold" | "heading-lg/normal" | "heading-lg/medium" | "heading-lg/semibold" | "heading-lg/bold" | "heading-xl/normal" | "heading-xl/medium" | "heading-xl/bold" | "heading-xxl/normal" | "heading-xxl/medium" | "heading-xxl/bold" | "eyebrow" | "heading-deprecated-14/normal" | "heading-deprecated-14/medium" | "heading-deprecated-14/bold" | "text-xxs/normal" | "text-xxs/medium" | "text-xxs/semibold" | "text-xxs/bold" | "text-xs/normal" | "text-xs/medium" | "text-xs/semibold" | "text-xs/bold" | "text-sm/normal" | "text-sm/medium" | "text-sm/semibold" | "text-sm/bold" | "text-md/normal" | "text-md/medium" | "text-md/semibold" | "text-md/bold" | "text-lg/normal" | "text-lg/medium" | "text-lg/semibold" | "text-lg/bold" | "display-sm" | "display-md" | "display-lg" | "code";
|
||||||
|
@ -154,7 +153,7 @@ export type Switch = ComponentType<PropsWithChildren<{
|
||||||
}>>;
|
}>>;
|
||||||
|
|
||||||
export type Timestamp = ComponentType<PropsWithChildren<{
|
export type Timestamp = ComponentType<PropsWithChildren<{
|
||||||
timestamp: Moment;
|
timestamp: Date;
|
||||||
isEdited?: boolean;
|
isEdited?: boolean;
|
||||||
|
|
||||||
className?: string;
|
className?: string;
|
||||||
|
|
45
src/webpack/common/types/utils.d.ts
vendored
45
src/webpack/common/types/utils.d.ts
vendored
|
@ -16,6 +16,7 @@
|
||||||
* 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 { Guild, GuildMember } from "discord-types/general";
|
||||||
import type { ReactNode } from "react";
|
import type { ReactNode } from "react";
|
||||||
|
|
||||||
import type { FluxEvents } from "./fluxEvents";
|
import type { FluxEvents } from "./fluxEvents";
|
||||||
|
@ -182,3 +183,47 @@ export interface NavigationRouter {
|
||||||
getLastRouteChangeSource(): any;
|
getLastRouteChangeSource(): any;
|
||||||
getLastRouteChangeSourceLocationStack(): any;
|
getLastRouteChangeSourceLocationStack(): any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IconUtils {
|
||||||
|
getUserAvatarURL(user: User, canAnimate?: boolean, size?: number, format?: string): string;
|
||||||
|
getDefaultAvatarURL(id: string, discriminator?: string): string;
|
||||||
|
getUserBannerURL(data: { id: string, banner: string, canAnimate?: boolean, size: number; }): string | undefined;
|
||||||
|
getAvatarDecorationURL(dara: { avatarDecoration: string, size: number; canCanimate?: boolean; }): string | undefined;
|
||||||
|
|
||||||
|
getGuildMemberAvatarURL(member: GuildMember, canAnimate?: string): string | null;
|
||||||
|
getGuildMemberAvatarURLSimple(data: { guildId: string, userId: string, avatar: string, canAnimate?: boolean; size?: number; }): string;
|
||||||
|
getGuildMemberBannerURL(data: { id: string, guildId: string, banner: string, canAnimate?: boolean, size: number; }): string | undefined;
|
||||||
|
|
||||||
|
getGuildIconURL(data: { id: string, icon?: string, size?: number, canAnimate?: boolean; }): string | undefined;
|
||||||
|
getGuildBannerURL(guild: Guild, canAnimate?: boolean): string | null;
|
||||||
|
|
||||||
|
getChannelIconURL(data: { id: string; icon?: string; applicationId?: string; size?: number; }): string | undefined;
|
||||||
|
getEmojiURL(data: { id: string, animated: boolean, size: number, forcePNG?: boolean; }): string;
|
||||||
|
|
||||||
|
hasAnimatedGuildIcon(guild: Guild): boolean;
|
||||||
|
isAnimatedIconHash(hash: string): boolean;
|
||||||
|
|
||||||
|
getGuildSplashURL: any;
|
||||||
|
getGuildDiscoverySplashURL: any;
|
||||||
|
getGuildHomeHeaderURL: any;
|
||||||
|
getResourceChannelIconURL: any;
|
||||||
|
getNewMemberActionIconURL: any;
|
||||||
|
getGuildTemplateIconURL: any;
|
||||||
|
getApplicationIconURL: any;
|
||||||
|
getGameAssetURL: any;
|
||||||
|
getVideoFilterAssetURL: any;
|
||||||
|
|
||||||
|
getGuildMemberAvatarSource: any;
|
||||||
|
getUserAvatarSource: any;
|
||||||
|
getGuildSplashSource: any;
|
||||||
|
getGuildDiscoverySplashSource: any;
|
||||||
|
makeSource: any;
|
||||||
|
getGameAssetSource: any;
|
||||||
|
getGuildIconSource: any;
|
||||||
|
getGuildTemplateIconSource: any;
|
||||||
|
getGuildBannerSource: any;
|
||||||
|
getGuildHomeHeaderSource: any;
|
||||||
|
getChannelIconSource: any;
|
||||||
|
getApplicationIconSource: any;
|
||||||
|
getAnimatableSourceWithFallback: any;
|
||||||
|
}
|
||||||
|
|
|
@ -137,3 +137,5 @@ export const { persist: zustandPersist }: typeof import("zustand/middleware") =
|
||||||
export const MessageActions = findByPropsLazy("editMessage", "sendMessage");
|
export const MessageActions = findByPropsLazy("editMessage", "sendMessage");
|
||||||
export const UserProfileActions = findByPropsLazy("openUserProfileModal", "closeUserProfileModal");
|
export const UserProfileActions = findByPropsLazy("openUserProfileModal", "closeUserProfileModal");
|
||||||
export const InviteActions = findByPropsLazy("resolveInvite");
|
export const InviteActions = findByPropsLazy("resolveInvite");
|
||||||
|
|
||||||
|
export const IconUtils: t.IconUtils = findByPropsLazy("getGuildBannerURL", "getUserAvatarURL");
|
||||||
|
|
Loading…
Reference in a new issue