Merge branch 'main' into plugin/memberListActivities

This commit is contained in:
D3SOX 2024-02-16 09:07:43 +01:00
commit 11a1c81916
No known key found for this signature in database
GPG key ID: 39EC1673FC37B048
9 changed files with 135 additions and 86 deletions

View file

@ -123,14 +123,13 @@ export const Magnifier: React.FC<MagnifierProps> = ({ instance, size: initialSiz
waitFor(() => instance.state.readyState === "READY", () => {
const elem = document.getElementById(ELEMENT_ID) as HTMLDivElement;
element.current = elem;
elem.firstElementChild!.setAttribute("draggable", "false");
elem.querySelector("img,video")?.setAttribute("draggable", "false");
if (instance.props.animated) {
originalVideoElementRef.current = elem!.querySelector("video")!;
originalVideoElementRef.current.addEventListener("timeupdate", syncVideos);
setReady(true);
} else {
setReady(true);
}
setReady(true);
});
document.addEventListener("keydown", onKeyDown);
document.addEventListener("keyup", onKeyUp);
@ -155,7 +154,9 @@ export const Magnifier: React.FC<MagnifierProps> = ({ instance, size: initialSiz
if (!ready) return null;
const box = element.current!.getBoundingClientRect();
const box = element.current?.getBoundingClientRect();
if (!box) return null;
return (
<div

View file

@ -171,7 +171,7 @@ export default definePlugin({
find: "handleImageLoad=",
replacement: [
{
match: /showThumbhashPlaceholder:\i,/,
match: /placeholderVersion:\i,/,
replace: "...$self.makeProps(this),$&"
},

View file

@ -29,15 +29,17 @@ import {
ChannelStore,
FluxDispatcher,
GuildStore,
IconUtils,
MessageStore,
Parser,
PermissionsBits,
PermissionStore,
RestAPI,
Text,
TextAndImagesSettingsStores,
UserStore
} from "@webpack/common";
import { Channel, Guild, Message } from "discord-types/general";
import { Channel, Message } from "discord-types/general";
const messageCache = new Map<string, {
message?: Message;
@ -49,8 +51,9 @@ const AutoModEmbed = findComponentByCodeLazy(".withFooter]:", "childrenMessageCo
const ChannelMessage = findComponentByCodeLazy("renderSimpleAccessories)");
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\//;
interface Attachment {
@ -63,7 +66,6 @@ interface Attachment {
interface MessageEmbedProps {
message: Message;
channel: Channel;
guildID: string;
}
const messageFetchQueue = new Queue();
@ -226,19 +228,19 @@ function MessageEmbedAccessory({ message }: { message: Message; }) {
let match = null as RegExpMatchArray | null;
while ((match = messageLinkRegex.exec(message.content!)) !== null) {
const [_, guildID, channelID, messageID] = match;
const [_, channelID, messageID] = match;
if (embeddedBy.includes(messageID)) {
continue;
}
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;
}
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 === "whitelist" && !isListed) continue;
@ -265,8 +267,7 @@ function MessageEmbedAccessory({ message }: { message: Message; }) {
const messageProps: MessageEmbedProps = {
message: withEmbeddedBy(linkedMessage, [...embeddedBy, message.id]),
channel: linkedChannel,
guildID
channel: linkedChannel
};
const type = settings.store.automodEmbeds;
@ -280,28 +281,28 @@ function MessageEmbedAccessory({ message }: { message: Message; }) {
return accessories.length ? <>{accessories}</> : null;
}
function ChannelMessageEmbedAccessory({ message, channel, guildID }: MessageEmbedProps): JSX.Element | null {
const isDM = guildID === "@me";
function getChannelLabelAndIconUrl(channel: Channel) {
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 [channelLabel, iconUrl] = getChannelLabelAndIconUrl(channel);
return <Embed
return (
<Embed
embed={{
rawDescription: "",
color: "var(--background-secondary)",
author: {
name: <Text variant="text-xs/medium" tag="span">
<span>{isDM ? "Direct Message - " : (guild as Guild).name + " - "}</span>
{isDM
? Parser.parse(`<@${dmReceiver.id}>`)
: Parser.parse(`<#${channel.id}>`)
}
<span>{channelLabel} - </span>
{Parser.parse(channel.isDM() ? `<@${dmReceiver.id}>` : `<#${channel.id}>`)}
</Text>,
iconProxyURL: guild
? `https://${window.GLOBAL_ENV.CDN_HOST}/icons/${guild.id}/${guild.icon}.png`
: `https://${window.GLOBAL_ENV.CDN_HOST}/avatars/${dmReceiver.id}/${dmReceiver.avatar}`
iconProxyURL: iconUrl
}
}}
renderDescription={() => (
@ -314,25 +315,30 @@ function ChannelMessageEmbedAccessory({ message, channel, guildID }: MessageEmbe
/>
</div>
)}
/>;
/>
);
}
function AutomodEmbedAccessory(props: MessageEmbedProps): JSX.Element | null {
const { message, channel, guildID } = props;
const { message, channel } = props;
const compact = TextAndImagesSettingsStores.MessageDisplayCompact.useSetting();
const isDM = guildID === "@me";
const images = getImages(message);
const { parse } = Parser;
const [channelLabel, iconUrl] = getChannelLabelAndIconUrl(channel);
return <AutoModEmbed
channel={channel}
childrenAccessories={
<Text color="text-muted" variant="text-xs/medium" tag="span">
{isDM
? parse(`<@${ChannelStore.getChannel(channel.id).recipients[0]}>`)
: parse(`<#${channel.id}>`)
<Text color="text-muted" variant="text-xs/medium" tag="span" className={`${EmbedClasses.embedAuthor} ${EmbedClasses.embedMargin}`}>
{iconUrl && <img src={iconUrl} className={EmbedClasses.embedAuthorIcon} alt="" />}
<span>
<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>
}
compact={compact}

View file

@ -20,11 +20,10 @@ import { Devs } from "@utils/constants";
import { isNonNullish } from "@utils/guards";
import definePlugin from "@utils/types";
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";
const SelectedChannelActionCreators = findByPropsLazy("selectPrivateChannel");
const AvatarUtils = findByPropsLazy("getChannelIconURL");
const UserUtils = findByPropsLazy("getGlobalName");
const ProfileListClasses = findByPropsLazy("emptyIconFriends", "emptyIconGuilds");
@ -71,7 +70,7 @@ export default definePlugin({
}}
>
<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"
className={ProfileListClasses.listAvatar}
>

View file

@ -12,10 +12,9 @@ import { classes } from "@utils/misc";
import { ModalRoot, ModalSize, openModal } from "@utils/modal";
import { useAwaiter } from "@utils/react";
import { findByPropsLazy, findExportedComponentLazy } from "@webpack";
import { FluxDispatcher, Forms, GuildChannelStore, GuildMemberStore, 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";
const IconUtils = findByPropsLazy("getGuildBannerURL");
const IconClasses = findByPropsLazy("icon", "acronym", "childWrapper");
const FriendRow = findExportedComponentLazy("FriendRow");
@ -65,10 +64,7 @@ function GuildProfileModal({ guild }: GuildProps) {
const [currentTab, setCurrentTab] = useState(Tabs.ServerInfo);
const bannerUrl = guild.banner && IconUtils.getGuildBannerURL({
id: guild.id,
banner: guild.banner
}, true).replace(/\?size=\d+$/, "?size=1024");
const bannerUrl = guild.banner && IconUtils.getGuildBannerURL(guild, true)!.replace(/\?size=\d+$/, "?size=1024");
const iconUrl = guild.icon && IconUtils.getGuildIconURL({
id: guild.id,
@ -89,7 +85,7 @@ function GuildProfileModal({ guild }: GuildProps) {
)}
<div className={cl("header")}>
{guild.icon
{iconUrl
? <img
src={iconUrl}
alt=""
@ -150,7 +146,7 @@ function Owner(guildId: string, owner: User) {
avatar: guildAvatar,
guildId,
canAnimate: true
}, true)
})
: IconUtils.getUserAvatarURL(owner, true);
return (

View file

@ -22,11 +22,9 @@ import { ImageIcon } from "@components/Icons";
import { Devs } from "@utils/constants";
import { openImageModal } from "@utils/discord";
import definePlugin, { OptionType } from "@utils/types";
import { findByPropsLazy } from "@webpack";
import { GuildMemberStore, Menu } from "@webpack/common";
import { GuildMemberStore, IconUtils, Menu } from "@webpack/common";
import type { Channel, Guild, User } from "discord-types/general";
const BannerStore = findByPropsLazy("getGuildBannerURL");
interface UserContextProps {
channel: Channel;
@ -91,19 +89,19 @@ const UserContext: NavContextMenuPatchCallback = (children, { user, guildId }: U
<Menu.MenuItem
id="view-avatar"
label="View Avatar"
action={() => openImage(BannerStore.getUserAvatarURL(user, true))}
action={() => openImage(IconUtils.getUserAvatarURL(user, true))}
icon={ImageIcon}
/>
{memberAvatar && (
<Menu.MenuItem
id="view-server-avatar"
label="View Server Avatar"
action={() => openImage(BannerStore.getGuildMemberAvatarURLSimple({
action={() => openImage(IconUtils.getGuildMemberAvatarURLSimple({
userId: user.id,
avatar: memberAvatar,
guildId,
guildId: guildId!,
canAnimate: true
}, true))}
}))}
icon={ImageIcon}
/>
)}
@ -124,11 +122,11 @@ const GuildContext: NavContextMenuPatchCallback = (children, { guild }: GuildCon
id="view-icon"
label="View Icon"
action={() =>
openImage(BannerStore.getGuildIconURL({
openImage(IconUtils.getGuildIconURL({
id,
icon,
canAnimate: true
}))
})!)
}
icon={ImageIcon}
/>
@ -138,10 +136,7 @@ const GuildContext: NavContextMenuPatchCallback = (children, { guild }: GuildCon
id="view-banner"
label="View Banner"
action={() =>
openImage(BannerStore.getGuildBannerURL({
id,
banner,
}, true))
openImage(IconUtils.getGuildBannerURL(guild, true)!)
}
icon={ImageIcon}
/>

View file

@ -47,18 +47,23 @@ const settings = definePluginSettings({
});
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);
if (url.origin === CDN_URL) return urlString;
if (url.origin === MEDIA_PROXY_URL) return CDN_URL + url.pathname;
if (url.host === CDN_URL) return urlString;
url.searchParams.delete("width");
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");
if (explodeWebp && url.searchParams.get("format") === "webp")
url.searchParams.set("format", "png");
}
return url.toString();
}
@ -199,7 +204,7 @@ export default definePlugin({
],
async copyImage(url: string) {
url = fixImageUrl(url, true);
url = fixImageUrl(url);
let imageData = await fetch(url).then(r => r.blob());
if (imageData.type !== "image/png") {
@ -231,7 +236,7 @@ export default definePlugin({
},
async saveImage(url: string) {
url = fixImageUrl(url, false);
url = fixImageUrl(url);
const data = await fetchImage(url);
if (!data) return;

View file

@ -16,6 +16,7 @@
* 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 { FluxEvents } from "./fluxEvents";
@ -182,3 +183,47 @@ export interface NavigationRouter {
getLastRouteChangeSource(): 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;
}

View file

@ -137,3 +137,5 @@ export const { persist: zustandPersist }: typeof import("zustand/middleware") =
export const MessageActions = findByPropsLazy("editMessage", "sendMessage");
export const UserProfileActions = findByPropsLazy("openUserProfileModal", "closeUserProfileModal");
export const InviteActions = findByPropsLazy("resolveInvite");
export const IconUtils: t.IconUtils = findByPropsLazy("getGuildBannerURL", "getUserAvatarURL");