1
0
Fork 1
mirror of https://github.com/Vendicated/Vencord.git synced 2025-01-10 09:56:24 +00:00

Merge branch 'Vendicated:main' into listenbrainz

This commit is contained in:
ConfiG 2024-12-05 15:26:06 +03:00 committed by GitHub
commit e3fb0e6a6a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 121 additions and 86 deletions

View file

@ -31,6 +31,7 @@ Before starting your plugin:
- No FakeDeafen or FakeMute - No FakeDeafen or FakeMute
- No StereoMic - No StereoMic
- No plugins that simply hide or redesign ui elements. This can be done with CSS - No plugins that simply hide or redesign ui elements. This can be done with CSS
- No plugins that interact with specific Discord bots (official Discord apps like Youtube WatchTogether are okay)
- No selfbots or API spam (animated status, message pruner, auto reply, nitro snipers, etc) - No selfbots or API spam (animated status, message pruner, auto reply, nitro snipers, etc)
- No untrusted third party APIs. Popular services like Google or GitHub are fine, but absolutely no self hosted ones - No untrusted third party APIs. Popular services like Google or GitHub are fine, but absolutely no self hosted ones
- No plugins that require the user to enter their own API key - No plugins that require the user to enter their own API key

View file

@ -1,7 +1,7 @@
{ {
"name": "vencord", "name": "vencord",
"private": "true", "private": "true",
"version": "1.10.7", "version": "1.10.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": {

View file

@ -54,5 +54,5 @@ export function sendBotMessage(channelId: string, message: PartialDeep<Message>)
export function findOption<T>(args: Argument[], name: string): T & {} | undefined; export function findOption<T>(args: Argument[], name: string): T & {} | undefined;
export function findOption<T>(args: Argument[], name: string, fallbackValue: T): T & {}; export function findOption<T>(args: Argument[], name: string, fallbackValue: T): T & {};
export function findOption(args: Argument[], name: string, fallbackValue?: any) { export function findOption(args: Argument[], name: string, fallbackValue?: any) {
return (args.find(a => a.name === name)?.value || fallbackValue) as any; return (args.find(a => a.name === name)?.value ?? fallbackValue) as any;
} }

View file

@ -110,6 +110,7 @@ function registerSubCommands(cmd: Command, plugin: string) {
const subCmd = { const subCmd = {
...cmd, ...cmd,
...o, ...o,
options: o.options !== undefined ? o.options : undefined,
type: ApplicationCommandType.CHAT_INPUT, type: ApplicationCommandType.CHAT_INPUT,
name: `${cmd.name} ${o.name}`, name: `${cmd.name} ${o.name}`,
id: `${o.name}-${cmd.id}`, id: `${o.name}-${cmd.id}`,

View file

@ -0,0 +1,5 @@
/* the profile popout badge container(s) */
[class*="biteSize_"] [class*="tags_"] [class*="container_"] {
/* Discord has padding set to 2px instead of 1px, which causes the 12th badge to wrap to a new line. */
padding: 0 1px;
}

View file

@ -16,6 +16,8 @@
* 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 "./fixDiscordBadgePadding.css";
import { _getBadges, BadgePosition, BadgeUserArgs, ProfileBadge } from "@api/Badges"; import { _getBadges, BadgePosition, BadgeUserArgs, ProfileBadge } from "@api/Badges";
import DonateButton from "@components/DonateButton"; import DonateButton from "@components/DonateButton";
import ErrorBoundary from "@components/ErrorBoundary"; import ErrorBoundary from "@components/ErrorBoundary";

View file

@ -23,11 +23,14 @@ export default definePlugin({
name: "MessagePopoverAPI", name: "MessagePopoverAPI",
description: "API to add buttons to message popovers.", description: "API to add buttons to message popovers.",
authors: [Devs.KingFish, Devs.Ven, Devs.Nuckyz], authors: [Devs.KingFish, Devs.Ven, Devs.Nuckyz],
patches: [{ patches: [
find: "#{intl::MESSAGE_UTILITIES_A11Y_LABEL}", {
replacement: { find: "#{intl::MESSAGE_UTILITIES_A11Y_LABEL}",
match: /\.jsx\)\((\i\.\i),\{label:\i\.\i\.string\(\i\.\i#{intl::MESSAGE_ACTION_REPLY}.{0,200}?"reply-self".{0,50}?\}\):null(?=,.+?message:(\i))/, replacement: {
replace: "$&,Vencord.Api.MessagePopover._buildPopoverElements($1,$2)" match: /(?<=:null),(.{0,40}togglePopout:.+?}\))\]}\):null,(?<=\((\i\.\i),{label:.+?:null,(\i&&!\i)\?\(0,\i\.jsxs?\)\(\i\.Fragment.+?message:(\i).+?)/,
replace: (_, ReactButton, ButtonComponent, showReactButton, message) => "" +
`]}):null,Vencord.Api.MessagePopover._buildPopoverElements(${ButtonComponent},${message}),${showReactButton}?${ReactButton}:null,`
}
} }
}], ]
}); });

View file

@ -51,7 +51,7 @@ export default definePlugin({
{ {
find: "bitbucket.org", find: "bitbucket.org",
replacement: { replacement: {
match: /function \i\(\i\){(?=.{0,60}\.parse\(\i\))/, match: /function \i\(\i\){(?=.{0,30}pathname:\i)/,
replace: "$&return null;" replace: "$&return null;"
}, },
predicate: () => settings.store.file predicate: () => settings.store.file

View file

@ -123,7 +123,7 @@ export default definePlugin({
}, },
// If we are rendering the Better Folders sidebar, we filter out everything but the servers and folders from the GuildsBar Guild List children // If we are rendering the Better Folders sidebar, we filter out everything but the servers and folders from the GuildsBar Guild List children
{ {
match: /lastTargetNode:\i\[\i\.length-1\].+?Fragment.+?\]}\)\]/, match: /lastTargetNode:\i\[\i\.length-1\].+?}\)\](?=}\))/,
replace: "$&.filter($self.makeGuildsBarGuildListFilter(!!arguments[0]?.isBetterFolders))" replace: "$&.filter($self.makeGuildsBarGuildListFilter(!!arguments[0]?.isBetterFolders))"
}, },
// If we are rendering the Better Folders sidebar, we filter out everything but the scroller for the guild list from the GuildsBar Tree children // If we are rendering the Better Folders sidebar, we filter out everything but the scroller for the guild list from the GuildsBar Tree children
@ -275,24 +275,30 @@ export default definePlugin({
}, },
makeGuildsBarGuildListFilter(isBetterFolders: boolean) { makeGuildsBarGuildListFilter(isBetterFolders: boolean) {
return child => { return child => {
if (isBetterFolders) { if (!isBetterFolders) return true;
try {
return child?.props?.["aria-label"] === getIntlMessage("SERVERS"); try {
} catch (e) { return child?.props?.["aria-label"] === getIntlMessage("SERVERS");
console.error(e); } catch (e) {
} console.error(e);
} }
return true;
}; return true;
};
}, },
makeGuildsBarTreeFilter(isBetterFolders: boolean) { makeGuildsBarTreeFilter(isBetterFolders: boolean) {
return child => { return child => {
if (isBetterFolders) { if (!isBetterFolders) return true;
return child?.props?.onScroll != null;
if (child?.props?.className?.includes("itemsContainer") && child.props.children != null) {
// Filter out everything but the scroller for the guild list
child.props.children = child.props.children.filter(child => child?.props?.onScroll != null);
return true;
} }
return true;
return false;
}; };
}, },

View file

@ -18,7 +18,7 @@
import { classNameFactory } from "@api/Styles"; import { classNameFactory } from "@api/Styles";
import ErrorBoundary from "@components/ErrorBoundary"; import ErrorBoundary from "@components/ErrorBoundary";
import { FluxDispatcher, React, useRef, useState } from "@webpack/common"; import { FluxDispatcher, useLayoutEffect, useRef, useState } from "@webpack/common";
import { ELEMENT_ID } from "../constants"; import { ELEMENT_ID } from "../constants";
import { settings } from "../index"; import { settings } from "../index";
@ -55,7 +55,7 @@ export const Magnifier = ErrorBoundary.wrap<MagnifierProps>(({ instance, size: i
const imageRef = useRef<HTMLImageElement | null>(null); const imageRef = useRef<HTMLImageElement | null>(null);
// since we accessing document im gonna use useLayoutEffect // since we accessing document im gonna use useLayoutEffect
React.useLayoutEffect(() => { useLayoutEffect(() => {
const onKeyDown = (e: KeyboardEvent) => { const onKeyDown = (e: KeyboardEvent) => {
if (e.key === "Shift") { if (e.key === "Shift") {
isShiftDown.current = true; isShiftDown.current = true;
@ -135,12 +135,14 @@ export const Magnifier = ErrorBoundary.wrap<MagnifierProps>(({ instance, size: i
setReady(true); setReady(true);
}); });
document.addEventListener("keydown", onKeyDown); document.addEventListener("keydown", onKeyDown);
document.addEventListener("keyup", onKeyUp); document.addEventListener("keyup", onKeyUp);
document.addEventListener("mousemove", updateMousePosition); document.addEventListener("mousemove", updateMousePosition);
document.addEventListener("mousedown", onMouseDown); document.addEventListener("mousedown", onMouseDown);
document.addEventListener("mouseup", onMouseUp); document.addEventListener("mouseup", onMouseUp);
document.addEventListener("wheel", onWheel); document.addEventListener("wheel", onWheel);
return () => { return () => {
document.removeEventListener("keydown", onKeyDown); document.removeEventListener("keydown", onKeyDown);
document.removeEventListener("keyup", onKeyUp); document.removeEventListener("keyup", onKeyUp);
@ -148,14 +150,16 @@ export const Magnifier = ErrorBoundary.wrap<MagnifierProps>(({ instance, size: i
document.removeEventListener("mousedown", onMouseDown); document.removeEventListener("mousedown", onMouseDown);
document.removeEventListener("mouseup", onMouseUp); document.removeEventListener("mouseup", onMouseUp);
document.removeEventListener("wheel", onWheel); document.removeEventListener("wheel", onWheel);
if (settings.store.saveZoomValues) {
settings.store.zoom = zoom.current;
settings.store.size = size.current;
}
}; };
}, []); }, []);
useLayoutEffect(() => () => {
if (settings.store.saveZoomValues) {
settings.store.zoom = zoom.current;
settings.store.size = size.current;
}
});
if (!ready) return null; if (!ready) return null;
const box = element.current?.getBoundingClientRect(); const box = element.current?.getBoundingClientRect();

View file

@ -19,6 +19,7 @@
import ErrorBoundary from "@components/ErrorBoundary"; import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import { isNonNullish } from "@utils/guards"; import { isNonNullish } from "@utils/guards";
import { Logger } from "@utils/Logger";
import definePlugin from "@utils/types"; import definePlugin from "@utils/types";
import { findByPropsLazy, findComponentByCodeLazy } from "@webpack"; import { findByPropsLazy, findComponentByCodeLazy } from "@webpack";
import { Avatar, ChannelStore, Clickable, IconUtils, RelationshipStore, ScrollerThin, useMemo, UserStore } from "@webpack/common"; import { Avatar, ChannelStore, Clickable, IconUtils, RelationshipStore, ScrollerThin, useMemo, UserStore } from "@webpack/common";
@ -87,7 +88,7 @@ export default definePlugin({
replacement: [ replacement: [
{ {
match: /\i\.useEffect.{0,100}(\i)\[0\]\.section/, match: /\i\.useEffect.{0,100}(\i)\[0\]\.section/,
replace: "$self.pushSection($1, arguments[0].user);$&" replace: "$self.pushSection($1,arguments[0].user);$&"
}, },
{ {
match: /\(0,\i\.jsx\)\(\i,\{items:\i,section:(\i)/, match: /\(0,\i\.jsx\)\(\i,\{items:\i,section:(\i)/,
@ -97,26 +98,46 @@ export default definePlugin({
}, },
{ {
find: 'section:"MUTUAL_FRIENDS"', find: 'section:"MUTUAL_FRIENDS"',
replacement: { replacement: [
match: /\.openUserProfileModal.+?\)}\)}\)(?<=(\(0,\i\.jsxs?\)\(\i\.\i,{className:(\i)\.divider}\)).+?)/, {
replace: "$&,$self.renderDMPageList({user: arguments[0].user, Divider: $1, listStyle: $2.list})" match: /\i\|\|\i(?=\?\(0,\i\.jsxs?\)\(\i\.\i\.Overlay,)/,
} replace: "$&||$self.getMutualGroupDms(arguments[0].user.id).length>0"
},
{
match: /\.openUserProfileModal.+?\)}\)}\)(?<=,(\i)&&(\i)&&(\(0,\i\.jsxs?\)\(\i\.\i,{className:(\i)\.divider}\)).+?)/,
replace: (m, hasMutualGuilds, hasMutualFriends, Divider, classes) => "" +
`${m},$self.renderDMPageList({user:arguments[0].user,hasDivider:${hasMutualGuilds}||${hasMutualFriends},Divider:${Divider},listStyle:${classes}.list})`
}
]
} }
], ],
pushSection(sections: any[], user: User) { getMutualGroupDms(userId: string) {
if (isBotOrSelf(user) || sections[IS_PATCHED]) return; try {
return getMutualGroupDms(userId);
} catch (e) {
new Logger("MutualGroupDMs").error("Failed to get mutual group dms:", e);
}
sections[IS_PATCHED] = true; return [];
sections.push({ },
section: "MUTUAL_GDMS",
text: getMutualGDMCountText(user) pushSection(sections: any[], user: User) {
}); try {
if (isBotOrSelf(user) || sections[IS_PATCHED]) return;
sections[IS_PATCHED] = true;
sections.push({
section: "MUTUAL_GDMS",
text: getMutualGDMCountText(user)
});
} catch (e) {
new Logger("MutualGroupDMs").error("Failed to push mutual group dms section:", e);
}
}, },
renderMutualGDMs: ErrorBoundary.wrap(({ user, onClose }: { user: User, onClose: () => void; }) => { renderMutualGDMs: ErrorBoundary.wrap(({ user, onClose }: { user: User, onClose: () => void; }) => {
const mutualGDms = useMemo(() => getMutualGroupDms(user.id), [user.id]); const mutualGDms = useMemo(() => getMutualGroupDms(user.id), [user.id]);
const entries = renderClickableGDMs(mutualGDms, onClose); const entries = renderClickableGDMs(mutualGDms, onClose);
return ( return (
@ -138,14 +159,13 @@ export default definePlugin({
); );
}), }),
renderDMPageList: ErrorBoundary.wrap(({ user, Divider, listStyle }: { user: User, Divider: JSX.Element, listStyle: string; }) => { renderDMPageList: ErrorBoundary.wrap(({ user, hasDivider, Divider, listStyle }: { user: User, hasDivider: boolean, Divider: JSX.Element, listStyle: string; }) => {
const mutualGDms = getMutualGroupDms(user.id); const mutualGDms = getMutualGroupDms(user.id);
if (mutualGDms.length === 0) return null; if (mutualGDms.length === 0) return null;
return ( return (
<> <>
{Divider} {hasDivider && Divider}
<ExpandableList <ExpandableList
listClassName={listStyle} listClassName={listStyle}
header={"Mutual Groups"} header={"Mutual Groups"}

View file

@ -20,9 +20,9 @@ import { addServerListElement, removeServerListElement, ServerListRenderPosition
import { Settings } from "@api/Settings"; import { 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 { useForceUpdater } from "@utils/react";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { GuildStore, PresenceStore, RelationshipStore } from "@webpack/common"; import { findStoreLazy } from "@webpack";
import { GuildStore, PresenceStore, RelationshipStore, useStateFromStores } from "@webpack/common";
const enum IndicatorType { const enum IndicatorType {
SERVER = 1 << 0, SERVER = 1 << 0,
@ -30,13 +30,24 @@ const enum IndicatorType {
BOTH = SERVER | FRIEND, BOTH = SERVER | FRIEND,
} }
let onlineFriends = 0; const UserGuildJoinRequestStore = findStoreLazy("UserGuildJoinRequestStore");
let guildCount = 0;
let forceUpdateFriendCount: () => void;
let forceUpdateGuildCount: () => void;
function FriendsIndicator() { function FriendsIndicator() {
forceUpdateFriendCount = useForceUpdater(); const onlineFriendsCount = useStateFromStores([RelationshipStore, PresenceStore], () => {
let count = 0;
const friendIds = RelationshipStore.getFriendIDs();
for (const id of friendIds) {
const status = PresenceStore.getStatus(id) ?? "offline";
if (status === "offline") {
continue;
}
count++;
}
return count;
});
return ( return (
<span id="vc-friendcount" style={{ <span id="vc-friendcount" style={{
@ -48,13 +59,19 @@ function FriendsIndicator() {
textTransform: "uppercase", textTransform: "uppercase",
textAlign: "center", textAlign: "center",
}}> }}>
{onlineFriends} online {onlineFriendsCount} online
</span> </span>
); );
} }
function ServersIndicator() { function ServersIndicator() {
forceUpdateGuildCount = useForceUpdater(); const guildCount = useStateFromStores([GuildStore, UserGuildJoinRequestStore], () => {
const guildJoinRequests: string[] = UserGuildJoinRequestStore.computeGuildIds();
const guilds = GuildStore.getGuilds();
// Filter only pending guild join requests
return GuildStore.getGuildCount() + guildJoinRequests.filter(id => guilds[id] == null).length;
});
return ( return (
<span id="vc-guildcount" style={{ <span id="vc-guildcount" style={{
@ -71,24 +88,6 @@ function ServersIndicator() {
); );
} }
function handlePresenceUpdate() {
onlineFriends = 0;
const relations = RelationshipStore.getRelationships();
for (const id of Object.keys(relations)) {
const type = relations[id];
// FRIEND relationship type
if (type === 1 && PresenceStore.getStatus(id) !== "offline") {
onlineFriends += 1;
}
}
forceUpdateFriendCount?.();
}
function handleGuildUpdate() {
guildCount = GuildStore.getGuildCount();
forceUpdateGuildCount?.();
}
export default definePlugin({ export default definePlugin({
name: "ServerListIndicators", name: "ServerListIndicators",
description: "Add online friend count or server count in the server list", description: "Add online friend count or server count in the server list",
@ -117,18 +116,8 @@ export default definePlugin({
</ErrorBoundary>; </ErrorBoundary>;
}, },
flux: {
PRESENCE_UPDATES: handlePresenceUpdate,
GUILD_CREATE: handleGuildUpdate,
GUILD_DELETE: handleGuildUpdate,
},
start() { start() {
addServerListElement(ServerListRenderPosition.Above, this.renderIndicator); addServerListElement(ServerListRenderPosition.Above, this.renderIndicator);
handlePresenceUpdate();
handleGuildUpdate();
}, },
stop() { stop() {

View file

@ -216,9 +216,12 @@ export default definePlugin({
replace: "true" replace: "true"
}, },
{ {
match: /\i\.\i\.copy/, match: /\i\.\i\.copy(?=\(\i)/,
replace: "Vencord.Webpack.Common.Clipboard.copy" replace: "Vencord.Webpack.Common.Clipboard.copy"
}] }
],
all: true,
noWarn: true
}, },
// Automod add filter words // Automod add filter words
{ {

View file

@ -22,6 +22,7 @@ import { findByPropsLazy, waitFor } from "../webpack";
export let React: typeof import("react"); export let React: typeof import("react");
export let useState: typeof React.useState; export let useState: typeof React.useState;
export let useEffect: typeof React.useEffect; export let useEffect: typeof React.useEffect;
export let useLayoutEffect: typeof React.useLayoutEffect;
export let useMemo: typeof React.useMemo; export let useMemo: typeof React.useMemo;
export let useRef: typeof React.useRef; export let useRef: typeof React.useRef;
export let useReducer: typeof React.useReducer; export let useReducer: typeof React.useReducer;
@ -31,5 +32,5 @@ export const ReactDOM: typeof import("react-dom") & typeof import("react-dom/cli
waitFor("useState", m => { waitFor("useState", m => {
React = m; React = m;
({ useEffect, useState, useMemo, useRef, useReducer, useCallback } = React); ({ useEffect, useState, useLayoutEffect, useMemo, useRef, useReducer, useCallback } = React);
}); });