+
{!!getCurrentChannel()?.guild_id && (
)}
-
+
{getCreatedAtDate(friendsSince, locale.getLocale())}
@@ -80,4 +94,3 @@ export default definePlugin({
);
}, { noop: true })
});
-
diff --git a/src/plugins/ignoreActivities/index.tsx b/src/plugins/ignoreActivities/index.tsx
index e2262129d..f687a0caf 100644
--- a/src/plugins/ignoreActivities/index.tsx
+++ b/src/plugins/ignoreActivities/index.tsx
@@ -228,15 +228,15 @@ export default definePlugin({
{
find: ".activityTitleText,variant",
replacement: {
- match: /(?<=\i\.activityTitleText.+?children:(\i)\.name.*?}\),)/,
- replace: (_, props) => `$self.renderToggleActivityButton(${props}),`
+ match: /\.activityTitleText.+?children:(\i)\.name.*?}\),/,
+ replace: (m, props) => `${m}$self.renderToggleActivityButton(${props}),`
},
},
{
find: ".activityCardDetails,children",
replacement: {
- match: /(?<=\i\.activityCardDetails.+?children:(\i\.application)\.name.*?}\),)/,
- replace: (_, props) => `$self.renderToggleActivityButton(${props}),`
+ match: /\.activityCardDetails.+?children:(\i\.application)\.name.*?}\),/,
+ replace: (m, props) => `${m}$self.renderToggleActivityButton(${props}),`
}
}
],
diff --git a/src/plugins/imageLink/README.md b/src/plugins/imageLink/README.md
new file mode 100644
index 000000000..add657948
--- /dev/null
+++ b/src/plugins/imageLink/README.md
@@ -0,0 +1,3 @@
+# ImageLink
+
+If a message consists of only a link to an image, Discord hides the link and shows only the image embed. This plugin makes the link show regardless.
diff --git a/src/plugins/imageLink/index.ts b/src/plugins/imageLink/index.ts
new file mode 100644
index 000000000..5e8dd23e9
--- /dev/null
+++ b/src/plugins/imageLink/index.ts
@@ -0,0 +1,24 @@
+/*
+ * Vencord, a Discord client mod
+ * Copyright (c) 2024 Vendicated and contributors
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+import { Devs } from "@utils/constants";
+import definePlugin from "@utils/types";
+
+export default definePlugin({
+ name: "ImageLink",
+ description: "Never hide image links in messages, even if it's the only content",
+ authors: [Devs.Kyuuhachi, Devs.Sqaaakoi],
+
+ patches: [
+ {
+ find: "unknownUserMentionPlaceholder:",
+ replacement: {
+ match: /\(0,\i\.isEmbedInline\)\(\i\)/,
+ replace: "false",
+ }
+ }
+ ]
+});
diff --git a/src/plugins/imageZoom/components/Magnifier.tsx b/src/plugins/imageZoom/components/Magnifier.tsx
index 816717350..585026d60 100644
--- a/src/plugins/imageZoom/components/Magnifier.tsx
+++ b/src/plugins/imageZoom/components/Magnifier.tsx
@@ -17,6 +17,7 @@
*/
import { classNameFactory } from "@api/Styles";
+import ErrorBoundary from "@components/ErrorBoundary";
import { FluxDispatcher, React, useRef, useState } from "@webpack/common";
import { ELEMENT_ID } from "../constants";
@@ -36,7 +37,7 @@ export interface MagnifierProps {
const cl = classNameFactory("vc-imgzoom-");
-export const Magnifier: React.FC
= ({ instance, size: initialSize, zoom: initalZoom }) => {
+export const Magnifier = ErrorBoundary.wrap(({ instance, size: initialSize, zoom: initalZoom }) => {
const [ready, setReady] = useState(false);
const [lensPosition, setLensPosition] = useState({ x: 0, y: 0 });
@@ -66,15 +67,18 @@ export const Magnifier: React.FC = ({ instance, size: initialSiz
}
};
const syncVideos = () => {
- currentVideoElementRef.current!.currentTime = originalVideoElementRef.current!.currentTime;
+ if (currentVideoElementRef.current && originalVideoElementRef.current)
+ currentVideoElementRef.current.currentTime = originalVideoElementRef.current.currentTime;
};
const updateMousePosition = (e: MouseEvent) => {
+ if (!element.current) return;
+
if (instance.state.mouseOver && instance.state.mouseDown) {
const offset = size.current / 2;
const pos = { x: e.pageX, y: e.pageY };
- const x = -((pos.x - element.current!.getBoundingClientRect().left) * zoom.current - offset);
- const y = -((pos.y - element.current!.getBoundingClientRect().top) * zoom.current - offset);
+ const x = -((pos.x - element.current.getBoundingClientRect().left) * zoom.current - offset);
+ const y = -((pos.y - element.current.getBoundingClientRect().top) * zoom.current - offset);
setLensPosition({ x: e.x - offset, y: e.y - offset });
setImagePosition({ x, y });
setOpacity(1);
@@ -183,6 +187,7 @@ export const Magnifier: React.FC = ({ instance, size: initialSiz
src={originalVideoElementRef.current?.src ?? instance.props.src}
autoPlay
loop
+ muted
/>
) : (
= ({ instance, size: initialSiz
)}
);
-};
+}, { noop: true });
diff --git a/src/plugins/implicitRelationships/index.ts b/src/plugins/implicitRelationships/index.ts
index 9ae9fb512..4faad2a9d 100644
--- a/src/plugins/implicitRelationships/index.ts
+++ b/src/plugins/implicitRelationships/index.ts
@@ -19,11 +19,12 @@
import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types";
-import { findByProps, findStoreLazy } from "@webpack";
+import { findByPropsLazy, findStoreLazy } from "@webpack";
import { ChannelStore, FluxDispatcher, GuildStore, RelationshipStore, SnowflakeUtils, UserStore } from "@webpack/common";
import { Settings } from "Vencord";
const UserAffinitiesStore = findStoreLazy("UserAffinitiesStore");
+const { FriendsSections } = findByPropsLazy("FriendsSections");
interface UserAffinity {
user_id: string;
@@ -81,8 +82,8 @@ export default definePlugin({
find: "getRelationshipCounts(){",
replacement: {
predicate: () => Settings.plugins.ImplicitRelationships.sortByAffinity,
- match: /\.sortBy\(\i=>\i\.comparator\)/,
- replace: "$&.sortBy((row) => $self.sortList(row))"
+ match: /\}\)\.sortBy\((.+?)\)\.value\(\)/,
+ replace: "}).sortBy(row => $self.wrapSort(($1), row)).value()"
}
},
@@ -120,10 +121,10 @@ export default definePlugin({
}
),
- sortList(row: any) {
+ wrapSort(comparator: Function, row: any) {
return row.type === 5
? -UserAffinitiesStore.getUserAffinity(row.user.id)?.affinity ?? 0
- : row.comparator;
+ : comparator(row);
},
async fetchImplicitRelationships() {
@@ -151,20 +152,25 @@ export default definePlugin({
// OP 8 Request Guild Members allows 100 user IDs at a time
const ignore = new Set(toRequest);
const relationships = RelationshipStore.getRelationships();
- const callback = ({ nonce, members }) => {
- if (nonce !== sentNonce) return;
- members.forEach(member => {
- ignore.delete(member.user.id);
- });
+ const callback = ({ chunks }) => {
+ for (const chunk of chunks) {
+ const { nonce, members } = chunk;
+ if (nonce !== sentNonce) return;
+ members.forEach(member => {
+ ignore.delete(member.user.id);
+ });
- nonFriendAffinities.map(id => UserStore.getUser(id)).filter(user => user && !ignore.has(user.id)).forEach(user => relationships[user.id] = 5);
- RelationshipStore.emitChange();
- if (--count === 0) {
- FluxDispatcher.unsubscribe("GUILD_MEMBERS_CHUNK", callback);
+ nonFriendAffinities.map(id => UserStore.getUser(id)).filter(user => user && !ignore.has(user.id)).forEach(user => relationships[user.id] = 5);
+ RelationshipStore.emitChange();
+ if (--count === 0) {
+ // @ts-ignore
+ FluxDispatcher.unsubscribe("GUILD_MEMBERS_CHUNK_BATCH", callback);
+ }
}
};
- FluxDispatcher.subscribe("GUILD_MEMBERS_CHUNK", callback);
+ // @ts-ignore
+ FluxDispatcher.subscribe("GUILD_MEMBERS_CHUNK_BATCH", callback);
for (let i = 0; i < toRequest.length; i += 100) {
FluxDispatcher.dispatch({
type: "GUILD_MEMBERS_REQUEST",
@@ -176,7 +182,6 @@ export default definePlugin({
},
start() {
- const { FriendsSections } = findByProps("FriendsSections");
FriendsSections.IMPLICIT = "IMPLICIT";
}
});
diff --git a/src/plugins/index.ts b/src/plugins/index.ts
index 7092001ee..48c55c660 100644
--- a/src/plugins/index.ts
+++ b/src/plugins/index.ts
@@ -19,8 +19,10 @@
import { registerCommand, unregisterCommand } from "@api/Commands";
import { addContextMenuPatch, removeContextMenuPatch } from "@api/ContextMenu";
import { Settings } from "@api/Settings";
+import { onceDefined } from "@shared/onceDefined";
import { Logger } from "@utils/Logger";
-import { Patch, Plugin, StartAt } from "@utils/types";
+import { canonicalizeFind } from "@utils/patches";
+import { Patch, Plugin, ReporterTestable, StartAt } from "@utils/types";
import { FluxDispatcher } from "@webpack/common";
import { FluxEvents } from "@webpack/types";
@@ -32,10 +34,25 @@ const logger = new Logger("PluginManager", "#a6d189");
export const PMLogger = logger;
export const plugins = Plugins;
-export const patches = [] as Patch[];
+export let patches = [] as Patch[];
+/** Whether we have subscribed to flux events of all the enabled plugins when FluxDispatcher was ready */
+let enabledPluginsSubscribedFlux = false;
+const subscribedFluxEventsPlugins = new Set