mirror of
https://github.com/Vendicated/Vencord.git
synced 2025-01-10 18:06:22 +00:00
Merge branch 'Vendicated:main' into main
This commit is contained in:
commit
7934318967
37 changed files with 335 additions and 152 deletions
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "vencord",
|
"name": "vencord",
|
||||||
"private": "true",
|
"private": "true",
|
||||||
"version": "1.7.4",
|
"version": "1.7.8",
|
||||||
"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": {
|
||||||
|
|
|
@ -315,7 +315,6 @@ export default function PluginSettings() {
|
||||||
<TextInput autoFocus value={searchValue.value} placeholder="Search for a plugin..." onChange={onSearch} className={Margins.bottom20} />
|
<TextInput autoFocus value={searchValue.value} placeholder="Search for a plugin..." onChange={onSearch} className={Margins.bottom20} />
|
||||||
<div className={InputStyles.inputWrapper}>
|
<div className={InputStyles.inputWrapper}>
|
||||||
<Select
|
<Select
|
||||||
className={InputStyles.inputDefault}
|
|
||||||
options={[
|
options={[
|
||||||
{ label: "Show All", value: SearchStatus.ALL, default: true },
|
{ label: "Show All", value: SearchStatus.ALL, default: true },
|
||||||
{ label: "Show Enabled", value: SearchStatus.ENABLED },
|
{ label: "Show Enabled", value: SearchStatus.ENABLED },
|
||||||
|
|
|
@ -28,7 +28,7 @@ const VENCORD_SRC_DIR = join(__dirname, "..");
|
||||||
|
|
||||||
const execFile = promisify(cpExecFile);
|
const execFile = promisify(cpExecFile);
|
||||||
|
|
||||||
const isFlatpak = process.platform === "linux" && Boolean(process.env.FLATPAK_ID?.includes("discordapp") || process.env.FLATPAK_ID?.includes("Discord"));
|
const isFlatpak = process.platform === "linux" && !!process.env.FLATPAK_ID;
|
||||||
|
|
||||||
if (process.platform === "darwin") process.env.PATH = `/usr/local/bin:${process.env.PATH}`;
|
if (process.platform === "darwin") process.env.PATH = `/usr/local/bin:${process.env.PATH}`;
|
||||||
|
|
||||||
|
@ -60,7 +60,8 @@ async function calculateGitChanges() {
|
||||||
return commits ? commits.split("\n").map(line => {
|
return commits ? commits.split("\n").map(line => {
|
||||||
const [author, hash, ...rest] = line.split("/");
|
const [author, hash, ...rest] = line.split("/");
|
||||||
return {
|
return {
|
||||||
hash, author, message: rest.join("/")
|
hash, author,
|
||||||
|
message: rest.join("/").split("\n")[0]
|
||||||
};
|
};
|
||||||
}) : [];
|
}) : [];
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,7 +53,7 @@ async function calculateGitChanges() {
|
||||||
// github api only sends the long sha
|
// github api only sends the long sha
|
||||||
hash: c.sha.slice(0, 7),
|
hash: c.sha.slice(0, 7),
|
||||||
author: c.author.login,
|
author: c.author.login,
|
||||||
message: c.commit.message.substring(c.commit.message.indexOf("\n") + 1)
|
message: c.commit.message.split("\n")[0]
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,14 +45,14 @@ export default definePlugin({
|
||||||
replacement: {
|
replacement: {
|
||||||
get match() {
|
get match() {
|
||||||
switch (Settings.plugins.Settings.settingsLocation) {
|
switch (Settings.plugins.Settings.settingsLocation) {
|
||||||
case "top": return /\{section:(\i\.\i)\.HEADER,\s*label:(\i)\.\i\.Messages\.USER_SETTINGS\}/;
|
case "top": return /\{section:(\i\.\i)\.HEADER,\s*label:(\i)\.\i\.Messages\.USER_SETTINGS/;
|
||||||
case "aboveNitro": return /\{section:(\i\.\i)\.HEADER,\s*label:(\i)\.\i\.Messages\.BILLING_SETTINGS\}/;
|
case "aboveNitro": return /\{section:(\i\.\i)\.HEADER,\s*label:(\i)\.\i\.Messages\.BILLING_SETTINGS/;
|
||||||
case "belowNitro": return /\{section:(\i\.\i)\.HEADER,\s*label:(\i)\.\i\.Messages\.APP_SETTINGS\}/;
|
case "belowNitro": return /\{section:(\i\.\i)\.HEADER,\s*label:(\i)\.\i\.Messages\.APP_SETTINGS/;
|
||||||
case "belowActivity": return /(?<=\{section:(\i\.\i)\.DIVIDER},)\{section:"changelog"/;
|
case "belowActivity": return /(?<=\{section:(\i\.\i)\.DIVIDER},)\{section:"changelog"/;
|
||||||
case "bottom": return /\{section:(\i\.\i)\.CUSTOM,\s*element:.+?}/;
|
case "bottom": return /\{section:(\i\.\i)\.CUSTOM,\s*element:.+?}/;
|
||||||
case "aboveActivity":
|
case "aboveActivity":
|
||||||
default:
|
default:
|
||||||
return /\{section:(\i\.\i)\.HEADER,\s*label:(\i)\.\i\.Messages\.ACTIVITY_SETTINGS\}/;
|
return /\{section:(\i\.\i)\.HEADER,\s*label:(\i)\.\i\.Messages\.ACTIVITY_SETTINGS/;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
replace: "...$self.makeSettingsCategories($1),$&"
|
replace: "...$self.makeSettingsCategories($1),$&"
|
||||||
|
|
|
@ -22,12 +22,12 @@ import { Devs } from "@utils/constants";
|
||||||
import { isTruthy } from "@utils/guards";
|
import { isTruthy } from "@utils/guards";
|
||||||
import { useAwaiter } from "@utils/react";
|
import { useAwaiter } from "@utils/react";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { findByPropsLazy, findComponentByCodeLazy } from "@webpack";
|
import { findByCodeLazy, findByPropsLazy, findComponentByCodeLazy } from "@webpack";
|
||||||
import { ApplicationAssetUtils, FluxDispatcher, Forms, GuildStore, React, SelectedChannelStore, SelectedGuildStore, UserStore } from "@webpack/common";
|
import { ApplicationAssetUtils, FluxDispatcher, Forms, GuildStore, React, SelectedChannelStore, SelectedGuildStore, UserStore } from "@webpack/common";
|
||||||
|
|
||||||
|
const useProfileThemeStyle = findByCodeLazy("profileThemeStyle:", "--profile-gradient-primary-color");
|
||||||
const ActivityComponent = findComponentByCodeLazy("onOpenGameProfile");
|
const ActivityComponent = findComponentByCodeLazy("onOpenGameProfile");
|
||||||
const ActivityClassName = findByPropsLazy("activity", "buttonColor");
|
const ActivityClassName = findByPropsLazy("activity", "buttonColor");
|
||||||
const Colors = findByPropsLazy("profileColors");
|
|
||||||
|
|
||||||
async function getApplicationAsset(key: string): Promise<string> {
|
async function getApplicationAsset(key: string): Promise<string> {
|
||||||
if (/https?:\/\/(cdn|media)\.discordapp\.(com|net)\/attachments\//.test(key)) return "mp:" + key.replace(/https?:\/\/(cdn|media)\.discordapp\.(com|net)\//, "");
|
if (/https?:\/\/(cdn|media)\.discordapp\.(com|net)\/attachments\//.test(key)) return "mp:" + key.replace(/https?:\/\/(cdn|media)\.discordapp\.(com|net)\//, "");
|
||||||
|
@ -393,6 +393,8 @@ export default definePlugin({
|
||||||
|
|
||||||
settingsAboutComponent: () => {
|
settingsAboutComponent: () => {
|
||||||
const activity = useAwaiter(createActivity);
|
const activity = useAwaiter(createActivity);
|
||||||
|
const { profileThemeStyle } = useProfileThemeStyle({});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Forms.FormText>
|
<Forms.FormText>
|
||||||
|
@ -406,7 +408,7 @@ export default definePlugin({
|
||||||
If you want to use image link, download your image and reupload the image to <Link href="https://imgur.com">Imgur</Link> and get the image link by right-clicking the image and select "Copy image address".
|
If you want to use image link, download your image and reupload the image to <Link href="https://imgur.com">Imgur</Link> and get the image link by right-clicking the image and select "Copy image address".
|
||||||
</Forms.FormText>
|
</Forms.FormText>
|
||||||
<Forms.FormDivider />
|
<Forms.FormDivider />
|
||||||
<div style={{ width: "284px" }} className={Colors.profileColors}>
|
<div style={{ width: "284px", ...profileThemeStyle }}>
|
||||||
{activity[0] && <ActivityComponent activity={activity[0]} className={ActivityClassName.activity} channelId={SelectedChannelStore.getChannelId()}
|
{activity[0] && <ActivityComponent activity={activity[0]} className={ActivityClassName.activity} channelId={SelectedChannelStore.getChannelId()}
|
||||||
guild={GuildStore.getGuild(SelectedGuildStore.getLastSelectedGuildId())}
|
guild={GuildStore.getGuild(SelectedGuildStore.getLastSelectedGuildId())}
|
||||||
application={{ id: settings.store.appID }}
|
application={{ id: settings.store.appID }}
|
||||||
|
|
|
@ -54,9 +54,9 @@ const StickerExt = [, "png", "png", "json", "gif"] as const;
|
||||||
|
|
||||||
function getUrl(data: Data) {
|
function getUrl(data: Data) {
|
||||||
if (data.t === "Emoji")
|
if (data.t === "Emoji")
|
||||||
return `${location.protocol}//${window.GLOBAL_ENV.CDN_HOST}/emojis/${data.id}.${data.isAnimated ? "gif" : "png"}`;
|
return `${location.protocol}//${window.GLOBAL_ENV.CDN_HOST}/emojis/${data.id}.${data.isAnimated ? "gif" : "png"}?size=4096&lossless=true`;
|
||||||
|
|
||||||
return `${window.GLOBAL_ENV.MEDIA_PROXY_ENDPOINT}/stickers/${data.id}.${StickerExt[data.format_type]}`;
|
return `${window.GLOBAL_ENV.MEDIA_PROXY_ENDPOINT}/stickers/${data.id}.${StickerExt[data.format_type]}?size=4096&lossless=true`;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchSticker(id: string) {
|
async function fetchSticker(id: string) {
|
||||||
|
@ -130,7 +130,8 @@ function getGuildCandidates(data: Data) {
|
||||||
|
|
||||||
let count = 0;
|
let count = 0;
|
||||||
for (const emoji of emojis)
|
for (const emoji of emojis)
|
||||||
if (emoji.animated === isAnimated) count++;
|
if (emoji.animated === isAnimated && !emoji.managed)
|
||||||
|
count++;
|
||||||
return count < emojiSlots;
|
return count < emojiSlots;
|
||||||
}).sort((a, b) => a.name.localeCompare(b.name));
|
}).sort((a, b) => a.name.localeCompare(b.name));
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ import { Logger } from "@utils/Logger";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { findByPropsLazy, findStoreLazy, proxyLazyWebpack } from "@webpack";
|
import { findByPropsLazy, findStoreLazy, proxyLazyWebpack } from "@webpack";
|
||||||
import { Alerts, ChannelStore, EmojiStore, FluxDispatcher, Forms, lodash, Parser, PermissionsBits, PermissionStore, UploadHandler, UserSettingsActionCreators, UserStore } from "@webpack/common";
|
import { Alerts, ChannelStore, EmojiStore, FluxDispatcher, Forms, lodash, Parser, PermissionsBits, PermissionStore, UploadHandler, UserSettingsActionCreators, UserStore } from "@webpack/common";
|
||||||
|
import type { CustomEmoji } from "@webpack/types";
|
||||||
import type { Message } from "discord-types/general";
|
import type { Message } from "discord-types/general";
|
||||||
import { applyPalette, GIFEncoder, quantize } from "gifenc";
|
import { applyPalette, GIFEncoder, quantize } from "gifenc";
|
||||||
import type { ReactElement, ReactNode } from "react";
|
import type { ReactElement, ReactNode } from "react";
|
||||||
|
@ -784,6 +785,16 @@ export default definePlugin({
|
||||||
UploadHandler.promptToUpload([file], ChannelStore.getChannel(channelId), DRAFT_TYPE);
|
UploadHandler.promptToUpload([file], ChannelStore.getChannel(channelId), DRAFT_TYPE);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
canUseEmote(e: CustomEmoji, channelId: string) {
|
||||||
|
if (e.require_colons === false) return true;
|
||||||
|
if (e.available === false) return false;
|
||||||
|
|
||||||
|
if (this.canUseEmotes)
|
||||||
|
return e.guildId === this.guildId || hasExternalEmojiPerms(channelId);
|
||||||
|
else
|
||||||
|
return !e.animated && e.guildId === this.guildId;
|
||||||
|
},
|
||||||
|
|
||||||
start() {
|
start() {
|
||||||
const s = settings.store;
|
const s = settings.store;
|
||||||
|
|
||||||
|
@ -882,12 +893,8 @@ export default definePlugin({
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s.enableEmojiBypass) {
|
if (s.enableEmojiBypass) {
|
||||||
const canUseEmotes = this.canUseEmotes && hasExternalEmojiPerms(channelId);
|
|
||||||
|
|
||||||
for (const emoji of messageObj.validNonShortcutEmojis) {
|
for (const emoji of messageObj.validNonShortcutEmojis) {
|
||||||
if (!emoji.require_colons) continue;
|
if (this.canUseEmote(emoji, channelId)) continue;
|
||||||
if (emoji.available !== false && canUseEmotes) continue;
|
|
||||||
if (emoji.guildId === guildId && !emoji.animated) continue;
|
|
||||||
|
|
||||||
hasBypass = true;
|
hasBypass = true;
|
||||||
|
|
||||||
|
@ -917,18 +924,12 @@ export default definePlugin({
|
||||||
this.preEdit = addPreEditListener(async (channelId, __, messageObj) => {
|
this.preEdit = addPreEditListener(async (channelId, __, messageObj) => {
|
||||||
if (!s.enableEmojiBypass) return;
|
if (!s.enableEmojiBypass) return;
|
||||||
|
|
||||||
const { guildId } = this;
|
|
||||||
|
|
||||||
let hasBypass = false;
|
let hasBypass = false;
|
||||||
|
|
||||||
const canUseEmotes = this.canUseEmotes && hasExternalEmojiPerms(channelId);
|
|
||||||
|
|
||||||
messageObj.content = messageObj.content.replace(/(?<!\\)<a?:(?:\w+):(\d+)>/ig, (emojiStr, emojiId, offset, origStr) => {
|
messageObj.content = messageObj.content.replace(/(?<!\\)<a?:(?:\w+):(\d+)>/ig, (emojiStr, emojiId, offset, origStr) => {
|
||||||
const emoji = EmojiStore.getCustomEmojiById(emojiId);
|
const emoji = EmojiStore.getCustomEmojiById(emojiId);
|
||||||
if (emoji == null) return emojiStr;
|
if (emoji == null) return emojiStr;
|
||||||
if (!emoji.require_colons) return emojiStr;
|
if (this.canUseEmote(emoji, channelId)) return emojiStr;
|
||||||
if (emoji.available !== false && canUseEmotes) return emojiStr;
|
|
||||||
if (emoji.guildId === guildId && !emoji.animated) return emojiStr;
|
|
||||||
|
|
||||||
hasBypass = true;
|
hasBypass = true;
|
||||||
|
|
||||||
|
|
|
@ -83,6 +83,6 @@ export default definePlugin({
|
||||||
if (!aIsFavorite && bIsFavorite) return 1;
|
if (!aIsFavorite && bIsFavorite) return 1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}).slice(0, query.results.emojis.sliceTo ?? 10);
|
}).slice(0, query.results.emojis.sliceTo ?? Infinity);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
import ErrorBoundary from "@components/ErrorBoundary";
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
|
import { getCurrentChannel } from "@utils/discord";
|
||||||
import definePlugin from "@utils/types";
|
import definePlugin from "@utils/types";
|
||||||
import { findByPropsLazy } from "@webpack";
|
import { findByPropsLazy } from "@webpack";
|
||||||
import { React, RelationshipStore } from "@webpack/common";
|
import { React, RelationshipStore } from "@webpack/common";
|
||||||
|
@ -49,6 +50,18 @@ export default definePlugin({
|
||||||
</Heading>
|
</Heading>
|
||||||
|
|
||||||
<div className={container.memberSinceContainer}>
|
<div className={container.memberSinceContainer}>
|
||||||
|
{!!getCurrentChannel()?.guild_id && (
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
width="16"
|
||||||
|
height="16"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="var(--interactive-normal)"
|
||||||
|
>
|
||||||
|
<path d="M13 10a4 4 0 1 0 0-8 4 4 0 0 0 0 8Z" />
|
||||||
|
<path d="M3 5v-.75C3 3.56 3.56 3 4.25 3s1.24.56 1.33 1.25C6.12 8.65 9.46 12 13 12h1a8 8 0 0 1 8 8 2 2 0 0 1-2 2 .21.21 0 0 1-.2-.15 7.65 7.65 0 0 0-1.32-2.3c-.15-.2-.42-.06-.39.17l.25 2c.02.15-.1.28-.25.28H9a2 2 0 0 1-2-2v-2.22c0-1.57-.67-3.05-1.53-4.37A15.85 15.85 0 0 1 3 5Z" />
|
||||||
|
</svg>
|
||||||
|
)}
|
||||||
<Text variant="text-sm/normal" className={clydeMoreInfo.body}>
|
<Text variant="text-sm/normal" className={clydeMoreInfo.body}>
|
||||||
{getCreatedAtDate(friendsSince, locale.getLocale())}
|
{getCreatedAtDate(friendsSince, locale.getLocale())}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
|
@ -16,7 +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 { addAccessory } from "@api/MessageAccessories";
|
import { addAccessory, removeAccessory } from "@api/MessageAccessories";
|
||||||
import { definePluginSettings } from "@api/Settings";
|
import { definePluginSettings } from "@api/Settings";
|
||||||
import ErrorBoundary from "@components/ErrorBoundary";
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { Devs } from "@utils/constants.js";
|
import { Devs } from "@utils/constants.js";
|
||||||
|
@ -389,4 +389,8 @@ export default definePlugin({
|
||||||
);
|
);
|
||||||
}, 4 /* just above rich embeds */);
|
}, 4 /* just above rich embeds */);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
stop() {
|
||||||
|
removeAccessory("messageLinkEmbed");
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -337,12 +337,12 @@ export default definePlugin({
|
||||||
{
|
{
|
||||||
// Attachment renderer
|
// Attachment renderer
|
||||||
// Module 96063
|
// Module 96063
|
||||||
find: ".removeAttachmentHoverButton",
|
find: ".removeMosaicItemHoverButton",
|
||||||
group: true,
|
group: true,
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
match: /(className:\i,attachment:\i),/,
|
match: /(className:\i,item:\i),/,
|
||||||
replace: "$1,attachment: {deleted},"
|
replace: "$1,item: deleted,"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
match: /\[\i\.obscured\]:.+?,/,
|
match: /\[\i\.obscured\]:.+?,/,
|
||||||
|
|
|
@ -45,7 +45,7 @@ export default definePlugin({
|
||||||
|
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
find: ".Messages.USER_PROFILE_MODAL", // Note: the module is lazy-loaded
|
find: ".Messages.MUTUAL_GUILDS_WITH_END_COUNT", // Note: the module is lazy-loaded
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(?<=\.tabBarItem.{0,50}MUTUAL_GUILDS.+?}\),)(?=.+?(\(0,\i\.jsxs?\)\(.{0,100}id:))/,
|
match: /(?<=\.tabBarItem.{0,50}MUTUAL_GUILDS.+?}\),)(?=.+?(\(0,\i\.jsxs?\)\(.{0,100}id:))/,
|
||||||
replace: '(arguments[0].user.bot||arguments[0].isCurrentUser)?null:$1"MUTUAL_GDMS",children:"Mutual Groups"}),'
|
replace: '(arguments[0].user.bot||arguments[0].isCurrentUser)?null:$1"MUTUAL_GDMS",children:"Mutual Groups"}),'
|
||||||
|
|
|
@ -5,27 +5,15 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { definePluginSettings } from "@api/Settings";
|
import { definePluginSettings } from "@api/Settings";
|
||||||
import { disableStyle, enableStyle } from "@api/Styles";
|
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
|
|
||||||
import style from "./styles.css?managed";
|
|
||||||
|
|
||||||
const settings = definePluginSettings({
|
const settings = definePluginSettings({
|
||||||
inlineVideo: {
|
inlineVideo: {
|
||||||
description: "Play videos without carousel modal",
|
description: "Play videos without carousel modal",
|
||||||
type: OptionType.BOOLEAN,
|
type: OptionType.BOOLEAN,
|
||||||
default: true,
|
default: true,
|
||||||
restartNeeded: true
|
restartNeeded: true
|
||||||
},
|
|
||||||
mediaLayoutType: {
|
|
||||||
description: "Choose media layout type",
|
|
||||||
type: OptionType.SELECT,
|
|
||||||
restartNeeded: true,
|
|
||||||
options: [
|
|
||||||
{ label: "STATIC, render loading image but image isn't resposive, no problem unless discord window width is too small", value: "STATIC", default: true },
|
|
||||||
{ label: "RESPONSIVE, image is responsive but not render loading image, cause messages shift when loaded", value: "RESPONSIVE" },
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -39,15 +27,11 @@ export default definePlugin({
|
||||||
|
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
find: ".oneByTwoLayoutThreeGrid",
|
find: "isGroupableMedia:function()",
|
||||||
replacement: [{
|
replacement: {
|
||||||
match: /mediaLayoutType:\i\.\i\.MOSAIC/,
|
match: /=>"IMAGE"===\i\|\|"VIDEO"===\i;/,
|
||||||
replace: "mediaLayoutType:$self.mediaLayoutType()",
|
replace: "=>false;"
|
||||||
},
|
}
|
||||||
{
|
|
||||||
match: /null!==\(\i=\i\.get\(\i\)\)&&void 0!==\i\?\i:"INVALID"/,
|
|
||||||
replace: '"INVALID"',
|
|
||||||
}]
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
find: "renderAttachments(",
|
find: "renderAttachments(",
|
||||||
|
@ -57,24 +41,5 @@ export default definePlugin({
|
||||||
replace: "$&$1.content_type?.startsWith('image/')&&"
|
replace: "$&$1.content_type?.startsWith('image/')&&"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
]
|
||||||
find: "Messages.REMOVE_ATTACHMENT_TOOLTIP_TEXT",
|
|
||||||
replacement: {
|
|
||||||
match: /\i===\i\.\i\.MOSAIC/,
|
|
||||||
replace: "true"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
|
|
||||||
mediaLayoutType() {
|
|
||||||
return settings.store.mediaLayoutType;
|
|
||||||
},
|
|
||||||
|
|
||||||
start() {
|
|
||||||
enableStyle(style);
|
|
||||||
},
|
|
||||||
|
|
||||||
stop() {
|
|
||||||
disableStyle(style);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
[class^="nonMediaAttachmentsContainer_"] [class*="messageAttachment_"] {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
|
@ -10,7 +10,7 @@ import { definePluginSettings } from "@api/Settings";
|
||||||
import ErrorBoundary from "@components/ErrorBoundary";
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { React, Tooltip } from "@webpack/common";
|
import { Tooltip } from "@webpack/common";
|
||||||
|
|
||||||
const settings = definePluginSettings({
|
const settings = definePluginSettings({
|
||||||
loop: {
|
loop: {
|
||||||
|
@ -28,9 +28,9 @@ export default definePlugin({
|
||||||
settings,
|
settings,
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
find: ".nonMediaAttachment]",
|
find: ".nonMediaMosaicItem]",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /\.nonMediaAttachment\]:!(\i).{0,10}children:\[(\S)/,
|
match: /\.nonMediaMosaicItem\]:!(\i).{0,10}children:\[(\S)/,
|
||||||
replace: "$&,$1&&$2&&$self.renderPiPButton(),"
|
replace: "$&,$1&&$2&&$self.renderPiPButton(),"
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -9,7 +9,7 @@ import { Settings } from "@api/Settings";
|
||||||
import { UserStore } from "@webpack/common";
|
import { UserStore } from "@webpack/common";
|
||||||
|
|
||||||
import { DEFAULT_COLOR } from "./constants";
|
import { DEFAULT_COLOR } from "./constants";
|
||||||
import { forceUpdate } from "./index";
|
import { forceUpdate, PinOrder, PrivateChannelSortStore, settings } from "./index";
|
||||||
|
|
||||||
export interface Category {
|
export interface Category {
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -106,7 +106,12 @@ export function categoryLen() {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getAllUncollapsedChannels() {
|
export function getAllUncollapsedChannels() {
|
||||||
return categories.filter(c => !c.collapsed).map(c => c.channels).flat();
|
if (settings.store.pinOrder === PinOrder.LastMessage) {
|
||||||
|
const sortedChannels = PrivateChannelSortStore.getPrivateChannelIds();
|
||||||
|
return categories.filter(c => !c.collapsed).flatMap(c => sortedChannels.filter(channel => c.channels.includes(channel)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return categories.filter(c => !c.collapsed).flatMap(c => c.channels);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getSections() {
|
export function getSections() {
|
||||||
|
|
|
@ -29,7 +29,7 @@ interface ChannelComponentProps {
|
||||||
|
|
||||||
const headerClasses = findByPropsLazy("privateChannelsHeaderContainer");
|
const headerClasses = findByPropsLazy("privateChannelsHeaderContainer");
|
||||||
|
|
||||||
const PrivateChannelSortStore = findStoreLazy("PrivateChannelSortStore") as { getPrivateChannelIds: () => string[]; };
|
export const PrivateChannelSortStore = findStoreLazy("PrivateChannelSortStore") as { getPrivateChannelIds: () => string[]; };
|
||||||
|
|
||||||
export let instance: any;
|
export let instance: any;
|
||||||
export const forceUpdate = () => instance?.props?._forceUpdate?.();
|
export const forceUpdate = () => instance?.props?._forceUpdate?.();
|
||||||
|
@ -236,7 +236,7 @@ export default definePlugin({
|
||||||
const category = categories[categoryIndex - 1];
|
const category = categories[categoryIndex - 1];
|
||||||
if (!category) return false;
|
if (!category) return false;
|
||||||
|
|
||||||
return category.collapsed && this.instance.props.selectedChannelId !== category.channels[channelIndex];
|
return category.collapsed && this.instance.props.selectedChannelId !== this.getCategoryChannels(category)[channelIndex];
|
||||||
},
|
},
|
||||||
|
|
||||||
getScrollOffset(channelId: string, rowHeight: number, padding: number, preRenderedChildren: number, originalOffset: number) {
|
getScrollOffset(channelId: string, rowHeight: number, padding: number, preRenderedChildren: number, originalOffset: number) {
|
||||||
|
|
|
@ -36,25 +36,24 @@ export default definePlugin({
|
||||||
authors: [Devs.Tyman, Devs.TheKodeToad, Devs.Ven],
|
authors: [Devs.Tyman, Devs.TheKodeToad, Devs.Ven],
|
||||||
description: "Adds pronouns to user messages using pronoundb",
|
description: "Adds pronouns to user messages using pronoundb",
|
||||||
patches: [
|
patches: [
|
||||||
// Add next to username (compact mode)
|
|
||||||
{
|
{
|
||||||
find: "showCommunicationDisabledStyles",
|
find: "showCommunicationDisabledStyles",
|
||||||
replacement: {
|
replacement: [
|
||||||
match: /("span",{id:\i,className:\i,children:\i}\))/,
|
// Add next to username (compact mode)
|
||||||
replace: "$1, $self.CompactPronounsChatComponentWrapper(arguments[0])"
|
{
|
||||||
}
|
match: /("span",{id:\i,className:\i,children:\i}\))/,
|
||||||
},
|
replace: "$1, $self.CompactPronounsChatComponentWrapper(arguments[0])"
|
||||||
// Patch the chat timestamp element (normal mode)
|
},
|
||||||
{
|
// Patch the chat timestamp element (normal mode)
|
||||||
find: "showCommunicationDisabledStyles",
|
{
|
||||||
replacement: {
|
match: /(?<=return\s*\(0,\i\.jsxs?\)\(.+!\i&&)(\(0,\i.jsxs?\)\(.+?\{.+?\}\))/,
|
||||||
match: /(?<=return\s*\(0,\i\.jsxs?\)\(.+!\i&&)(\(0,\i.jsxs?\)\(.+?\{.+?\}\))/,
|
replace: "[$1, $self.PronounsChatComponentWrapper(arguments[0])]"
|
||||||
replace: "[$1, $self.PronounsChatComponentWrapper(arguments[0])]"
|
}
|
||||||
}
|
]
|
||||||
},
|
},
|
||||||
// Patch the profile popout username header to use our pronoun hook instead of Discord's pronouns
|
// Patch the profile popout username header to use our pronoun hook instead of Discord's pronouns
|
||||||
{
|
{
|
||||||
find: ".userTagNoNickname",
|
find: ".pronouns,children",
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
match: /{user:(\i),[^}]*,pronouns:(\i),[^}]*}=\i;/,
|
match: /{user:(\i),[^}]*,pronouns:(\i),[^}]*}=\i;/,
|
||||||
|
|
|
@ -18,9 +18,68 @@
|
||||||
|
|
||||||
import { findGroupChildrenByChildId } from "@api/ContextMenu";
|
import { findGroupChildrenByChildId } from "@api/ContextMenu";
|
||||||
import { definePluginSettings } from "@api/Settings";
|
import { definePluginSettings } from "@api/Settings";
|
||||||
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { Menu } from "@webpack/common";
|
import { findByPropsLazy } from "@webpack";
|
||||||
|
import { Button, Menu, Tooltip, useEffect, useState } from "@webpack/common";
|
||||||
|
|
||||||
|
const ChannelRowClasses = findByPropsLazy("modeConnected", "modeLocked", "icon");
|
||||||
|
|
||||||
|
let currentShouldViewServerHome = false;
|
||||||
|
const shouldViewServerHomeStates = new Set<React.Dispatch<React.SetStateAction<boolean>>>();
|
||||||
|
|
||||||
|
function ViewServerHomeButton() {
|
||||||
|
return (
|
||||||
|
<Tooltip text="View Server Home">
|
||||||
|
{tooltipProps => (
|
||||||
|
<Button
|
||||||
|
{...tooltipProps}
|
||||||
|
look={Button.Looks.BLANK}
|
||||||
|
size={Button.Sizes.NONE}
|
||||||
|
innerClassName={ChannelRowClasses.icon}
|
||||||
|
onClick={e => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
currentShouldViewServerHome = true;
|
||||||
|
for (const setState of shouldViewServerHomeStates) {
|
||||||
|
setState(true);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
|
||||||
|
>
|
||||||
|
<svg width="20" height="20" viewBox="0 0 24 24">
|
||||||
|
<path fill="currentColor" d="m2.4 8.4 8.38-6.46a2 2 0 0 1 2.44 0l8.39 6.45a2 2 0 0 1-.79 3.54l-.32.07-.82 8.2a2 2 0 0 1-1.99 1.8H16a1 1 0 0 1-1-1v-5a3 3 0 0 0-6 0v5a1 1 0 0 1-1 1H6.31a2 2 0 0 1-1.99-1.8L3.5 12l-.32-.07a2 2 0 0 1-.79-3.54Z" />
|
||||||
|
</svg>
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function useForceServerHome() {
|
||||||
|
const { forceServerHome } = settings.use(["forceServerHome"]);
|
||||||
|
const [shouldViewServerHome, setShouldViewServerHome] = useState(currentShouldViewServerHome);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
shouldViewServerHomeStates.add(setShouldViewServerHome);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
shouldViewServerHomeStates.delete(setShouldViewServerHome);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return shouldViewServerHome || forceServerHome;
|
||||||
|
}
|
||||||
|
|
||||||
|
function useDisableViewServerHome() {
|
||||||
|
useEffect(() => () => {
|
||||||
|
currentShouldViewServerHome = false;
|
||||||
|
for (const setState of shouldViewServerHomeStates) {
|
||||||
|
setState(false);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
|
|
||||||
const settings = definePluginSettings({
|
const settings = definePluginSettings({
|
||||||
forceServerHome: {
|
forceServerHome: {
|
||||||
|
@ -30,12 +89,6 @@ const settings = definePluginSettings({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function useForceServerHome() {
|
|
||||||
const { forceServerHome } = settings.use(["forceServerHome"]);
|
|
||||||
|
|
||||||
return forceServerHome;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "ResurrectHome",
|
name: "ResurrectHome",
|
||||||
description: "Re-enables the Server Home tab when there isn't a Server Guide. Also has an option to force the Server Home over the Server Guide, which is accessible through right-clicking the Server Guide.",
|
description: "Re-enables the Server Home tab when there isn't a Server Guide. Also has an option to force the Server Home over the Server Guide, which is accessible through right-clicking the Server Guide.",
|
||||||
|
@ -89,17 +142,40 @@ export default definePlugin({
|
||||||
{
|
{
|
||||||
find: "61eef9_2",
|
find: "61eef9_2",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(?<=getMutableGuildChannelsForGuild\(\i\)\);)(?=if\(null==\i\|\|)/,
|
match: /getMutableGuildChannelsForGuild\(\i\);return\(0,\i\.useStateFromStores\).+?\]\)(?=}function)/,
|
||||||
replace: "if($self.useForceServerHome())return false;"
|
replace: m => `${m}&&!$self.useForceServerHome()`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// Add View Server Home Button to Server Guide
|
||||||
|
{
|
||||||
|
find: "487e85_1",
|
||||||
|
replacement: {
|
||||||
|
match: /(?<=text:(\i)\?\i\.\i\.Messages\.SERVER_GUIDE:\i\.\i\.Messages\.GUILD_HOME,)/,
|
||||||
|
replace: "badge:$self.ViewServerHomeButton({serverGuide:$1}),"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// Disable view Server Home override when the Server Home is unmouted
|
||||||
|
{
|
||||||
|
find: "69386d_5",
|
||||||
|
replacement: {
|
||||||
|
match: /location:"69386d_5".+?;/,
|
||||||
|
replace: "$&$self.useDisableViewServerHome();"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
|
ViewServerHomeButton: ErrorBoundary.wrap(({ serverGuide }: { serverGuide?: boolean; }) => {
|
||||||
|
if (serverGuide !== true) return null;
|
||||||
|
|
||||||
|
return <ViewServerHomeButton />;
|
||||||
|
}),
|
||||||
|
|
||||||
useForceServerHome,
|
useForceServerHome,
|
||||||
|
useDisableViewServerHome,
|
||||||
|
|
||||||
contextMenus: {
|
contextMenus: {
|
||||||
"guild-context"(children, props) {
|
"guild-context"(children, props) {
|
||||||
const forceServerHome = useForceServerHome();
|
const { forceServerHome } = settings.use(["forceServerHome"]);
|
||||||
|
|
||||||
if (!props?.guild) return;
|
if (!props?.guild) return;
|
||||||
|
|
||||||
|
|
|
@ -71,7 +71,7 @@ export const settings = definePluginSettings({
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button onClick={async () => {
|
<Button onClick={async () => {
|
||||||
let url = "https://reviewdb.mantikafasi.dev/";
|
let url = "https://reviewdb.mantikafasi.dev";
|
||||||
const token = await getToken();
|
const token = await getToken();
|
||||||
if (token)
|
if (token)
|
||||||
url += "/api/redirect?token=" + encodeURIComponent(token);
|
url += "/api/redirect?token=" + encodeURIComponent(token);
|
||||||
|
|
|
@ -94,7 +94,7 @@ export default definePlugin({
|
||||||
find: "renderPrioritySpeaker",
|
find: "renderPrioritySpeaker",
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
match: /renderName\(\).{0,100}speaking:.{50,100}jsx.{5,10}{/,
|
match: /renderName\(\).{0,100}speaking:.{50,150}"div",{/,
|
||||||
replace: "$&...$self.getVoiceProps(this.props),"
|
replace: "$&...$self.getVoiceProps(this.props),"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
|
@ -35,7 +35,7 @@ const Section = findComponentByCodeLazy(".lastSection", "children:");
|
||||||
const ThemeStore = findStoreLazy("ThemeStore");
|
const ThemeStore = findStoreLazy("ThemeStore");
|
||||||
const platformHooks: { useLegacyPlatformType(platform: string): string; } = findByPropsLazy("useLegacyPlatformType");
|
const platformHooks: { useLegacyPlatformType(platform: string): string; } = findByPropsLazy("useLegacyPlatformType");
|
||||||
const platforms: { get(type: string): ConnectionPlatform; } = findByPropsLazy("isSupported", "getByUrl");
|
const platforms: { get(type: string): ConnectionPlatform; } = findByPropsLazy("isSupported", "getByUrl");
|
||||||
const getTheme: (user: User, displayProfile: any) => any = findByCodeLazy(',"--profile-gradient-primary-color"');
|
const getProfileThemeProps = findByCodeLazy(".getPreviewThemeColors", "primaryColor:");
|
||||||
|
|
||||||
const enum Spacing {
|
const enum Spacing {
|
||||||
COMPACT,
|
COMPACT,
|
||||||
|
@ -74,8 +74,8 @@ interface ConnectionPlatform {
|
||||||
icon: { lightSVG: string, darkSVG: string; };
|
icon: { lightSVG: string, darkSVG: string; };
|
||||||
}
|
}
|
||||||
|
|
||||||
const profilePopoutComponent = ErrorBoundary.wrap(({ user, displayProfile }: { user: User, displayProfile; }) =>
|
const profilePopoutComponent = ErrorBoundary.wrap((props: { user: User, displayProfile; }) =>
|
||||||
<ConnectionsComponent id={user.id} theme={getTheme(user, displayProfile).profileTheme} />
|
<ConnectionsComponent id={props.user.id} theme={getProfileThemeProps(props).theme} />
|
||||||
);
|
);
|
||||||
|
|
||||||
const profilePanelComponent = ErrorBoundary.wrap(({ id }: { id: string; }) =>
|
const profilePanelComponent = ErrorBoundary.wrap(({ id }: { id: string; }) =>
|
||||||
|
|
|
@ -452,7 +452,7 @@ export default definePlugin({
|
||||||
{
|
{
|
||||||
// Filter hidden channels from GuildChannelStore.getChannels unless told otherwise
|
// Filter hidden channels from GuildChannelStore.getChannels unless told otherwise
|
||||||
match: /(?<=getChannels\(\i)(\){.+?)return (.+?)}/,
|
match: /(?<=getChannels\(\i)(\){.+?)return (.+?)}/,
|
||||||
replace: (_, rest, channels) => `,shouldIncludeHidden${rest}return $self.resolveGuildChannels(${channels},shouldIncludeHidden??false);}`
|
replace: (_, rest, channels) => `,shouldIncludeHidden${rest}return $self.resolveGuildChannels(${channels},shouldIncludeHidden??arguments[0]==="@favorites");}`
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
@ -49,7 +49,7 @@ export default definePlugin({
|
||||||
{
|
{
|
||||||
find: ".useCanSeeRemixBadge)",
|
find: ".useCanSeeRemixBadge)",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(?<=onContextMenu:\i,children:).*?\}/,
|
match: /(?<=onContextMenu:\i,children:).*?\)}/,
|
||||||
replace: "$self.renderUsername(arguments[0])}"
|
replace: "$self.renderUsername(arguments[0])}"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
|
|
||||||
import "./spotifyStyles.css";
|
import "./spotifyStyles.css";
|
||||||
|
|
||||||
import ErrorBoundary from "@components/ErrorBoundary";
|
|
||||||
import { Flex } from "@components/Flex";
|
import { Flex } from "@components/Flex";
|
||||||
import { ImageIcon, LinkIcon, OpenExternalIcon } from "@components/Icons";
|
import { ImageIcon, LinkIcon, OpenExternalIcon } from "@components/Icons";
|
||||||
import { debounce } from "@shared/debounce";
|
import { debounce } from "@shared/debounce";
|
||||||
|
@ -376,17 +375,10 @@ export function Player() {
|
||||||
} as React.CSSProperties;
|
} as React.CSSProperties;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ErrorBoundary fallback={() => (
|
<div id={cl("player")} style={exportTrackImageStyle}>
|
||||||
<div className="vc-spotify-fallback">
|
<Info track={track} />
|
||||||
<p>Failed to render Spotify Modal :(</p>
|
<SeekBar />
|
||||||
<p >Check the console for errors</p>
|
<Controls />
|
||||||
</div>
|
</div>
|
||||||
)}>
|
|
||||||
<div id={cl("player")} style={exportTrackImageStyle}>
|
|
||||||
<Info track={track} />
|
|
||||||
<SeekBar />
|
|
||||||
<Controls />
|
|
||||||
</div>
|
|
||||||
</ErrorBoundary>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
|
|
||||||
import { Settings } from "@api/Settings";
|
import { Settings } from "@api/Settings";
|
||||||
import { disableStyle, enableStyle } from "@api/Styles";
|
import { disableStyle, enableStyle } from "@api/Styles";
|
||||||
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
|
|
||||||
|
@ -49,10 +50,10 @@ export default definePlugin({
|
||||||
{
|
{
|
||||||
find: "showTaglessAccountPanel:",
|
find: "showTaglessAccountPanel:",
|
||||||
replacement: {
|
replacement: {
|
||||||
// return React.createElement(AccountPanel, { ..., showTaglessAccountPanel: blah })
|
// react.jsx)(AccountPanel, { ..., showTaglessAccountPanel: blah })
|
||||||
match: /return ?(.{0,30}\(.{1,3},\{[^}]+?,showTaglessAccountPanel:.+?\}\))/,
|
match: /(?<=\i\.jsxs?\)\()(\i),{(?=[^}]*?showTaglessAccountPanel:)/,
|
||||||
// return [Player, Panel]
|
// react.jsx(WrapperComponent, { VencordOriginal: AccountPanel, ...
|
||||||
replace: "return [$self.renderPlayer(),$1]"
|
replace: "$self.PanelWrapper,{VencordOriginal:$1,"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -78,6 +79,25 @@ export default definePlugin({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
start: () => toggleHoverControls(Settings.plugins.SpotifyControls.hoverControls),
|
start: () => toggleHoverControls(Settings.plugins.SpotifyControls.hoverControls),
|
||||||
renderPlayer: () => <Player />
|
|
||||||
|
PanelWrapper({ VencordOriginal, ...props }) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ErrorBoundary
|
||||||
|
fallback={() => (
|
||||||
|
<div className="vc-spotify-fallback">
|
||||||
|
<p>Failed to render Spotify Modal :(</p>
|
||||||
|
<p >Check the console for errors</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Player />
|
||||||
|
</ErrorBoundary>
|
||||||
|
|
||||||
|
<VencordOriginal {...props} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -17,9 +17,10 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import { LazyComponent } from "@utils/react";
|
|
||||||
import definePlugin from "@utils/types";
|
import definePlugin from "@utils/types";
|
||||||
|
|
||||||
|
import StartupTimingPage from "./StartupTimingPage";
|
||||||
|
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "StartupTimings",
|
name: "StartupTimings",
|
||||||
description: "Adds Startup Timings to the Settings menu",
|
description: "Adds Startup Timings to the Settings menu",
|
||||||
|
@ -31,5 +32,5 @@ export default definePlugin({
|
||||||
replace: '{section:"StartupTimings",label:"Startup Timings",element:$self.StartupTimingPage},$&'
|
replace: '{section:"StartupTimings",label:"Startup Timings",element:$self.StartupTimingPage},$&'
|
||||||
}
|
}
|
||||||
}],
|
}],
|
||||||
StartupTimingPage: LazyComponent(() => require("./StartupTimingPage").default)
|
StartupTimingPage
|
||||||
});
|
});
|
||||||
|
|
|
@ -16,20 +16,28 @@
|
||||||
* 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 "./style.css";
|
||||||
|
|
||||||
import { definePluginSettings, Settings } from "@api/Settings";
|
import { definePluginSettings, Settings } from "@api/Settings";
|
||||||
import ErrorBoundary from "@components/ErrorBoundary";
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { findExportedComponentLazy, findStoreLazy } from "@webpack";
|
import { findComponentByCodeLazy, findExportedComponentLazy, findStoreLazy } from "@webpack";
|
||||||
import { ChannelStore, GuildMemberStore, i18n, RelationshipStore, SelectedChannelStore, Tooltip, UserStore, useStateFromStores } from "@webpack/common";
|
import { ChannelStore, GuildMemberStore, i18n, RelationshipStore, SelectedChannelStore, Tooltip, UserStore, useStateFromStores } from "@webpack/common";
|
||||||
|
|
||||||
import { buildSeveralUsers } from "../typingTweaks";
|
import { buildSeveralUsers } from "../typingTweaks";
|
||||||
|
|
||||||
const ThreeDots = findExportedComponentLazy("Dots", "AnimatedDots");
|
const ThreeDots = findExportedComponentLazy("Dots", "AnimatedDots");
|
||||||
|
const UserSummaryItem = findComponentByCodeLazy("defaultRenderUser", "showDefaultAvatarsForNullUsers");
|
||||||
|
|
||||||
const TypingStore = findStoreLazy("TypingStore");
|
const TypingStore = findStoreLazy("TypingStore");
|
||||||
const UserGuildSettingsStore = findStoreLazy("UserGuildSettingsStore");
|
const UserGuildSettingsStore = findStoreLazy("UserGuildSettingsStore");
|
||||||
|
|
||||||
|
const enum IndicatorMode {
|
||||||
|
Dots = 1 << 0,
|
||||||
|
Avatars = 1 << 1
|
||||||
|
}
|
||||||
|
|
||||||
function getDisplayName(guildId: string, userId: string) {
|
function getDisplayName(guildId: string, userId: string) {
|
||||||
const user = UserStore.getUser(userId);
|
const user = UserStore.getUser(userId);
|
||||||
return GuildMemberStore.getNick(guildId, userId) ?? (user as any).globalName ?? user.username;
|
return GuildMemberStore.getNick(guildId, userId) ?? (user as any).globalName ?? user.username;
|
||||||
|
@ -90,11 +98,24 @@ function TypingIndicator({ channelId }: { channelId: string; }) {
|
||||||
return (
|
return (
|
||||||
<Tooltip text={tooltipText!}>
|
<Tooltip text={tooltipText!}>
|
||||||
{props => (
|
{props => (
|
||||||
<div
|
<div className="vc-typing-indicator" {...props}>
|
||||||
{...props}
|
{((settings.store.indicatorMode & IndicatorMode.Avatars) === IndicatorMode.Avatars) && (
|
||||||
style={{ marginLeft: 6, height: 16, display: "flex", alignItems: "center", zIndex: 0, cursor: "pointer" }}
|
<UserSummaryItem
|
||||||
>
|
users={typingUsersArray.map(id => UserStore.getUser(id))}
|
||||||
<ThreeDots dotRadius={3} themed={true} />
|
guildId={guildId}
|
||||||
|
renderIcon={false}
|
||||||
|
max={3}
|
||||||
|
showDefaultAvatarsForNullUsers
|
||||||
|
showUserPopout
|
||||||
|
size={16}
|
||||||
|
className="vc-typing-indicator-avatars"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{((settings.store.indicatorMode & IndicatorMode.Dots) === IndicatorMode.Dots) && (
|
||||||
|
<div className="vc-typing-indicator-dots">
|
||||||
|
<ThreeDots dotRadius={3} themed={true} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
@ -119,13 +140,22 @@ const settings = definePluginSettings({
|
||||||
type: OptionType.BOOLEAN,
|
type: OptionType.BOOLEAN,
|
||||||
description: "Whether to show the typing indicator for blocked users.",
|
description: "Whether to show the typing indicator for blocked users.",
|
||||||
default: false
|
default: false
|
||||||
|
},
|
||||||
|
indicatorMode: {
|
||||||
|
type: OptionType.SELECT,
|
||||||
|
description: "How should the indicator be displayed?",
|
||||||
|
options: [
|
||||||
|
{ label: "Avatars and animated dots", value: IndicatorMode.Dots | IndicatorMode.Avatars, default: true },
|
||||||
|
{ label: "Animated dots", value: IndicatorMode.Dots },
|
||||||
|
{ label: "Avatars", value: IndicatorMode.Avatars },
|
||||||
|
],
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "TypingIndicator",
|
name: "TypingIndicator",
|
||||||
description: "Adds an indicator if someone is typing on a channel.",
|
description: "Adds an indicator if someone is typing on a channel.",
|
||||||
authors: [Devs.Nuckyz, Devs.fawn],
|
authors: [Devs.Nuckyz, Devs.fawn, Devs.Sqaaakoi],
|
||||||
settings,
|
settings,
|
||||||
|
|
||||||
patches: [
|
patches: [
|
||||||
|
|
18
src/plugins/typingIndicator/style.css
Normal file
18
src/plugins/typingIndicator/style.css
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
.vc-typing-indicator {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-typing-indicator-avatars {
|
||||||
|
margin-left: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-typing-indicator-dots {
|
||||||
|
margin-left: 6px;
|
||||||
|
height: 16px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
z-index: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
5
src/plugins/unlockedAvatarZoom/README.md
Normal file
5
src/plugins/unlockedAvatarZoom/README.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
# UnlockedAvatarZoom
|
||||||
|
|
||||||
|
Allows you to zoom in further in the image crop tool when changing your avatar
|
||||||
|
|
||||||
|
![](https://raw.githubusercontent.com/Vencord/plugin-assets/main/UnlockedAvatarZoom/demo.avif)
|
35
src/plugins/unlockedAvatarZoom/index.ts
Normal file
35
src/plugins/unlockedAvatarZoom/index.ts
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
* Vencord, a Discord client mod
|
||||||
|
* Copyright (c) 2024 Vendicated and contributors
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { definePluginSettings } from "@api/Settings";
|
||||||
|
import { makeRange } from "@components/PluginSettings/components";
|
||||||
|
import { Devs } from "@utils/constants";
|
||||||
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
|
|
||||||
|
const settings = definePluginSettings({
|
||||||
|
zoomMultiplier: {
|
||||||
|
type: OptionType.SLIDER,
|
||||||
|
description: "Zoom multiplier",
|
||||||
|
markers: makeRange(2, 16),
|
||||||
|
default: 4,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default definePlugin({
|
||||||
|
name: "UnlockedAvatarZoom",
|
||||||
|
description: "Allows you to zoom in further in the image crop tool when changing your avatar",
|
||||||
|
authors: [Devs.nakoyasha],
|
||||||
|
settings,
|
||||||
|
patches: [
|
||||||
|
{
|
||||||
|
find: ".Messages.AVATAR_UPLOAD_EDIT_MEDIA",
|
||||||
|
replacement: {
|
||||||
|
match: /maxValue:\d/,
|
||||||
|
replace: "maxValue:$self.settings.store.zoomMultiplier",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
|
@ -104,7 +104,7 @@ export default definePlugin({
|
||||||
},
|
},
|
||||||
// below username
|
// below username
|
||||||
{
|
{
|
||||||
find: ".USER_PROFILE_MODAL",
|
find: ".Messages.MUTUAL_GUILDS_WITH_END_COUNT", // Lazy-loaded
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /\.body.+?displayProfile:\i}\),/,
|
match: /\.body.+?displayProfile:\i}\),/,
|
||||||
replace: "$&$self.patchModal(arguments[0]),",
|
replace: "$&$self.patchModal(arguments[0]),",
|
||||||
|
|
|
@ -174,7 +174,7 @@ export default definePlugin({
|
||||||
find: ".NITRO_BANNER,",
|
find: ".NITRO_BANNER,",
|
||||||
replacement: {
|
replacement: {
|
||||||
// style: { backgroundImage: shouldShowBanner ? "url(".concat(bannerUrl,
|
// style: { backgroundImage: shouldShowBanner ? "url(".concat(bannerUrl,
|
||||||
match: /style:\{(?=backgroundImage:(\i)\?"url\("\.concat\((\i),)/,
|
match: /style:\{(?=backgroundImage:(null!=\i)\?"url\("\.concat\((\i),)/,
|
||||||
replace:
|
replace:
|
||||||
// onClick: () => shouldShowBanner && ev.target.style.backgroundImage && openImage(bannerUrl), style: { cursor: shouldShowBanner ? "pointer" : void 0,
|
// onClick: () => shouldShowBanner && ev.target.style.backgroundImage && openImage(bannerUrl), style: { cursor: shouldShowBanner ? "pointer" : void 0,
|
||||||
'onClick:ev=>$1&&ev.target.style.backgroundImage&&$self.openImage($2),style:{cursor:$1?"pointer":void 0,'
|
'onClick:ev=>$1&&ev.target.style.backgroundImage&&$self.openImage($2),style:{cursor:$1?"pointer":void 0,'
|
||||||
|
|
|
@ -155,7 +155,7 @@ export const Devs = /* #__PURE__*/ Object.freeze({
|
||||||
},
|
},
|
||||||
kemo: {
|
kemo: {
|
||||||
name: "kemo",
|
name: "kemo",
|
||||||
id: 299693897859465228n
|
id: 715746190813298788n
|
||||||
},
|
},
|
||||||
dzshn: {
|
dzshn: {
|
||||||
name: "dzshn",
|
name: "dzshn",
|
||||||
|
@ -429,6 +429,18 @@ export const Devs = /* #__PURE__*/ Object.freeze({
|
||||||
newwares: {
|
newwares: {
|
||||||
name: "newwares",
|
name: "newwares",
|
||||||
id: 421405303951851520n
|
id: 421405303951851520n
|
||||||
|
},
|
||||||
|
nakoyasha: {
|
||||||
|
name: "nakoyasha",
|
||||||
|
id: 222069018507345921n
|
||||||
|
},
|
||||||
|
Sqaaakoi: {
|
||||||
|
name: "Sqaaakoi",
|
||||||
|
id: 259558259491340288n
|
||||||
|
},
|
||||||
|
Byron: {
|
||||||
|
name: "byeoon",
|
||||||
|
id: 1167275288036655133n
|
||||||
}
|
}
|
||||||
} satisfies Record<string, Dev>);
|
} satisfies Record<string, Dev>);
|
||||||
|
|
||||||
|
|
|
@ -116,8 +116,11 @@ export function proxyLazy<T>(factory: () => T, attempts = 5, isChild = false): T
|
||||||
attempts,
|
attempts,
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
|
const lazyTarget = target[kGET]();
|
||||||
return Reflect.get(target[kGET](), p, receiver);
|
if (typeof lazyTarget === "object" || typeof lazyTarget === "function") {
|
||||||
|
return Reflect.get(lazyTarget, p, receiver);
|
||||||
|
}
|
||||||
|
throw new Error("proxyLazy called on a primitive value");
|
||||||
}
|
}
|
||||||
}) as any;
|
}) as any;
|
||||||
}
|
}
|
||||||
|
|
|
@ -406,13 +406,15 @@ export function findExportedComponentLazy<T extends object = any>(...props: stri
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const DefaultExtractAndLoadChunksRegex = /(?:Promise\.all\((\[\i\.\i\(".+?"\).+?\])\)|Promise\.resolve\(\)).then\(\i\.bind\(\i,"(.+?)"\)\)/;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract and load chunks using their entry point
|
* Extract and load chunks using their entry point
|
||||||
* @param code An array of all the code the module factory containing the lazy chunk loading must include
|
* @param code An array of all the code the module factory containing the lazy chunk loading must include
|
||||||
* @param matcher A RegExp that returns the chunk ids array as the first capture group and the entry point id as the second. Defaults to a matcher that captures the lazy chunk loading found in the module factory
|
* @param matcher A RegExp that returns the chunk ids array as the first capture group and the entry point id as the second. Defaults to a matcher that captures the lazy chunk loading found in the module factory
|
||||||
* @returns A promise that resolves when the chunks were loaded
|
* @returns A promise that resolves when the chunks were loaded
|
||||||
*/
|
*/
|
||||||
export async function extractAndLoadChunks(code: string[], matcher: RegExp = /Promise\.all\((\[\i\.\i\(".+?"\).+?\])\).then\(\i\.bind\(\i,"(.+?)"\)\)/) {
|
export async function extractAndLoadChunks(code: string[], matcher: RegExp = DefaultExtractAndLoadChunksRegex) {
|
||||||
const module = findModuleFactory(...code);
|
const module = findModuleFactory(...code);
|
||||||
if (!module) {
|
if (!module) {
|
||||||
const err = new Error("extractAndLoadChunks: Couldn't find module factory");
|
const err = new Error("extractAndLoadChunks: Couldn't find module factory");
|
||||||
|
@ -434,7 +436,7 @@ export async function extractAndLoadChunks(code: string[], matcher: RegExp = /Pr
|
||||||
}
|
}
|
||||||
|
|
||||||
const [, rawChunkIds, entryPointId] = match;
|
const [, rawChunkIds, entryPointId] = match;
|
||||||
if (!rawChunkIds || Number.isNaN(entryPointId)) {
|
if (Number.isNaN(entryPointId)) {
|
||||||
const err = new Error("extractAndLoadChunks: Matcher didn't return a capturing group with the chunk ids array, or the entry point id returned as the second group wasn't a number");
|
const err = new Error("extractAndLoadChunks: Matcher didn't return a capturing group with the chunk ids array, or the entry point id returned as the second group wasn't a number");
|
||||||
logger.warn(err, "Code:", code, "Matcher:", matcher);
|
logger.warn(err, "Code:", code, "Matcher:", matcher);
|
||||||
|
|
||||||
|
@ -445,9 +447,11 @@ export async function extractAndLoadChunks(code: string[], matcher: RegExp = /Pr
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const chunkIds = Array.from(rawChunkIds.matchAll(/\("(.+?)"\)/g)).map((m: any) => m[1]);
|
if (rawChunkIds) {
|
||||||
|
const chunkIds = Array.from(rawChunkIds.matchAll(/\("(.+?)"\)/g)).map((m: any) => m[1]);
|
||||||
|
await Promise.all(chunkIds.map(id => wreq.e(id)));
|
||||||
|
}
|
||||||
|
|
||||||
await Promise.all(chunkIds.map(id => wreq.e(id)));
|
|
||||||
wreq(entryPointId);
|
wreq(entryPointId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -459,7 +463,7 @@ export async function extractAndLoadChunks(code: string[], matcher: RegExp = /Pr
|
||||||
* @param matcher A RegExp that returns the chunk ids array as the first capture group and the entry point id as the second. Defaults to a matcher that captures the lazy chunk loading found in the module factory
|
* @param matcher A RegExp that returns the chunk ids array as the first capture group and the entry point id as the second. Defaults to a matcher that captures the lazy chunk loading found in the module factory
|
||||||
* @returns A function that returns a promise that resolves when the chunks were loaded, on first call
|
* @returns A function that returns a promise that resolves when the chunks were loaded, on first call
|
||||||
*/
|
*/
|
||||||
export function extractAndLoadChunksLazy(code: string[], matcher: RegExp = /Promise\.all\((\[\i\.\i\(".+?"\).+?\])\).then\(\i\.bind\(\i,"(.+?)"\)\)/) {
|
export function extractAndLoadChunksLazy(code: string[], matcher = DefaultExtractAndLoadChunksRegex) {
|
||||||
if (IS_DEV) lazyWebpackSearchHistory.push(["extractAndLoadChunks", [code, matcher]]);
|
if (IS_DEV) lazyWebpackSearchHistory.push(["extractAndLoadChunks", [code, matcher]]);
|
||||||
|
|
||||||
return () => extractAndLoadChunks(code, matcher);
|
return () => extractAndLoadChunks(code, matcher);
|
||||||
|
|
Loading…
Reference in a new issue