diff --git a/browser/GMPolyfill.js b/browser/GMPolyfill.js
index f8801551e..387389ce6 100644
--- a/browser/GMPolyfill.js
+++ b/browser/GMPolyfill.js
@@ -62,7 +62,7 @@ function GM_fetch(url, opt) {
resp.arrayBuffer = () => blobTo("arrayBuffer", blob);
resp.text = () => blobTo("text", blob);
resp.json = async () => JSON.parse(await blobTo("text", blob));
- resp.headers = new Headers(parseHeaders(resp.responseHeaders));
+ resp.headers = parseHeaders(resp.responseHeaders);
resp.ok = resp.status >= 200 && resp.status < 300;
resolve(resp);
};
diff --git a/scripts/generateReport.ts b/scripts/generateReport.ts
index 0a17e8d7e..33b099ef8 100644
--- a/scripts/generateReport.ts
+++ b/scripts/generateReport.ts
@@ -428,10 +428,11 @@ function runTime(token: string) {
if (searchType === "findComponent") method = "find";
if (searchType === "findExportedComponent") method = "findByProps";
- if (searchType === "waitFor" || searchType === "waitForComponent" || searchType === "waitForStore") {
+ if (searchType === "waitFor" || searchType === "waitForComponent") {
if (typeof args[0] === "string") method = "findByProps";
else method = "find";
}
+ if (searchType === "waitForStore") method = "findStore";
try {
let result: any;
diff --git a/src/components/VencordSettings/CloudTab.tsx b/src/components/VencordSettings/CloudTab.tsx
index 0392a451c..080dd8dd9 100644
--- a/src/components/VencordSettings/CloudTab.tsx
+++ b/src/components/VencordSettings/CloudTab.tsx
@@ -39,9 +39,7 @@ function validateUrl(url: string) {
async function eraseAllData() {
const res = await fetch(new URL("/v1/", getCloudUrl()), {
method: "DELETE",
- headers: new Headers({
- Authorization: await getCloudAuth()
- })
+ headers: { Authorization: await getCloudAuth() }
});
if (!res.ok) {
diff --git a/src/plugins/fakeNitro/index.tsx b/src/plugins/fakeNitro/index.tsx
index 20125c7d1..1cf1e536f 100644
--- a/src/plugins/fakeNitro/index.tsx
+++ b/src/plugins/fakeNitro/index.tsx
@@ -369,8 +369,8 @@ export default definePlugin({
predicate: () => settings.store.transformEmojis,
replacement: {
// Add the fake nitro emoji notice
- match: /(?<=isDiscoverable:\i,emojiComesFromCurrentGuild:\i,.+?}=(\i).+?;)(.*?return )(.{0,1000}\.Messages\.EMOJI_POPOUT_UNJOINED_DISCOVERABLE_GUILD_DESCRIPTION.+?)(?=},)/,
- replace: (_, props, rest, reactNode) => `let{fakeNitroNode}=${props};${rest}$self.addFakeNotice(${FakeNoticeType.Emoji},${reactNode},!!fakeNitroNode?.fake)`
+ match: /(?<=emojiDescription:)(\i)(?<=\1=\i\((\i)\).+?)/,
+ replace: (_, reactNode, props) => `$self.addFakeNotice(${FakeNoticeType.Emoji},${reactNode},!!${props}?.fakeNitroNode?.fake)`
}
},
// Allow using custom app icons
@@ -474,7 +474,7 @@ export default definePlugin({
if (typeof firstContent === "string") {
content[0] = firstContent.trimStart();
content[0] || content.shift();
- } else if (firstContent?.type === "span") {
+ } else if (typeof firstContent?.props?.children === "string") {
firstContent.props.children = firstContent.props.children.trimStart();
firstContent.props.children || content.shift();
}
@@ -484,7 +484,7 @@ export default definePlugin({
if (typeof lastContent === "string") {
content[lastIndex] = lastContent.trimEnd();
content[lastIndex] || content.pop();
- } else if (lastContent?.type === "span") {
+ } else if (typeof lastContent?.props?.children === "string") {
lastContent.props.children = lastContent.props.children.trimEnd();
lastContent.props.children || content.pop();
}
diff --git a/src/plugins/memberCount/MemberCount.tsx b/src/plugins/memberCount/MemberCount.tsx
new file mode 100644
index 000000000..50665353e
--- /dev/null
+++ b/src/plugins/memberCount/MemberCount.tsx
@@ -0,0 +1,66 @@
+/*
+ * Vencord, a Discord client mod
+ * Copyright (c) 2024 Vendicated and contributors
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+import { getCurrentChannel } from "@utils/discord";
+import { SelectedChannelStore, Tooltip, useEffect, useStateFromStores } from "@webpack/common";
+
+import { ChannelMemberStore, cl, GuildMemberCountStore, numberFormat } from ".";
+import { OnlineMemberCountStore } from "./OnlineMemberCountStore";
+
+export function MemberCount({ isTooltip, tooltipGuildId }: { isTooltip?: true; tooltipGuildId?: string; }) {
+ const currentChannel = useStateFromStores([SelectedChannelStore], () => getCurrentChannel());
+
+ const guildId = isTooltip ? tooltipGuildId! : currentChannel.guild_id;
+
+ const totalCount = useStateFromStores(
+ [GuildMemberCountStore],
+ () => GuildMemberCountStore.getMemberCount(guildId)
+ );
+
+ let onlineCount = useStateFromStores(
+ [OnlineMemberCountStore],
+ () => OnlineMemberCountStore.getCount(guildId)
+ );
+
+ const { groups } = useStateFromStores(
+ [ChannelMemberStore],
+ () => ChannelMemberStore.getProps(guildId, currentChannel.id)
+ );
+
+ if (!isTooltip && (groups.length >= 1 || groups[0].id !== "unknown")) {
+ onlineCount = groups.reduce((total, curr) => total + (curr.id === "offline" ? 0 : curr.count), 0);
+ }
+
+ useEffect(() => {
+ OnlineMemberCountStore.ensureCount(guildId);
+ }, [guildId]);
+
+ if (totalCount == null)
+ return null;
+
+ const formattedOnlineCount = onlineCount != null ? numberFormat(onlineCount) : "?";
+
+ return (
+
+
+ {props => (
+
+
+ {formattedOnlineCount}
+
+ )}
+
+
+ {props => (
+
+
+ {numberFormat(totalCount)}
+
+ )}
+
+
+ );
+}
diff --git a/src/plugins/memberCount/OnlineMemberCountStore.ts b/src/plugins/memberCount/OnlineMemberCountStore.ts
new file mode 100644
index 000000000..8790f5e29
--- /dev/null
+++ b/src/plugins/memberCount/OnlineMemberCountStore.ts
@@ -0,0 +1,52 @@
+/*
+ * Vencord, a Discord client mod
+ * Copyright (c) 2024 Vendicated and contributors
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+import { proxyLazy } from "@utils/lazy";
+import { sleep } from "@utils/misc";
+import { Queue } from "@utils/Queue";
+import { Flux, FluxDispatcher, GuildChannelStore, PrivateChannelsStore } from "@webpack/common";
+
+export const OnlineMemberCountStore = proxyLazy(() => {
+ const preloadQueue = new Queue();
+
+ const onlineMemberMap = new Map();
+
+ class OnlineMemberCountStore extends Flux.Store {
+ getCount(guildId: string) {
+ return onlineMemberMap.get(guildId);
+ }
+
+ async _ensureCount(guildId: string) {
+ if (onlineMemberMap.has(guildId)) return;
+
+ await PrivateChannelsStore.preload(guildId, GuildChannelStore.getDefaultChannel(guildId).id);
+ }
+
+ ensureCount(guildId: string) {
+ if (onlineMemberMap.has(guildId)) return;
+
+ preloadQueue.push(() =>
+ this._ensureCount(guildId)
+ .then(
+ () => sleep(200),
+ () => sleep(200)
+ )
+ );
+ }
+ }
+
+ return new OnlineMemberCountStore(FluxDispatcher, {
+ GUILD_MEMBER_LIST_UPDATE({ guildId, groups }: { guildId: string, groups: { count: number; id: string; }[]; }) {
+ onlineMemberMap.set(
+ guildId,
+ groups.reduce((total, curr) => total + (curr.id === "offline" ? 0 : curr.count), 0)
+ );
+ },
+ ONLINE_GUILD_MEMBER_COUNT_UPDATE({ guildId, count }) {
+ onlineMemberMap.set(guildId, count);
+ }
+ });
+});
diff --git a/src/plugins/memberCount/index.tsx b/src/plugins/memberCount/index.tsx
index d9cd548e9..eb4ce372c 100644
--- a/src/plugins/memberCount/index.tsx
+++ b/src/plugins/memberCount/index.tsx
@@ -16,101 +16,48 @@
* along with this program. If not, see .
*/
+import "./style.css";
+
+import { classNameFactory } from "@api/Styles";
import ErrorBoundary from "@components/ErrorBoundary";
-import { Flex } from "@components/Flex";
import { Devs } from "@utils/constants";
-import { getCurrentChannel } from "@utils/discord";
import definePlugin from "@utils/types";
import { findStoreLazy } from "@webpack";
-import { SelectedChannelStore, Tooltip, useStateFromStores } from "@webpack/common";
import { FluxStore } from "@webpack/types";
-const GuildMemberCountStore = findStoreLazy("GuildMemberCountStore") as FluxStore & { getMemberCount(guildId: string): number | null; };
-const ChannelMemberStore = findStoreLazy("ChannelMemberStore") as FluxStore & {
+import { MemberCount } from "./MemberCount";
+
+export const GuildMemberCountStore = findStoreLazy("GuildMemberCountStore") as FluxStore & { getMemberCount(guildId: string): number | null; };
+export const ChannelMemberStore = findStoreLazy("ChannelMemberStore") as FluxStore & {
getProps(guildId: string, channelId: string): { groups: { count: number; id: string; }[]; };
};
const sharedIntlNumberFormat = new Intl.NumberFormat();
-const numberFormat = (value: number) => sharedIntlNumberFormat.format(value);
-
-function MemberCount() {
- const { id: channelId, guild_id: guildId } = useStateFromStores([SelectedChannelStore], () => getCurrentChannel());
- const { groups } = useStateFromStores(
- [ChannelMemberStore],
- () => ChannelMemberStore.getProps(guildId, channelId)
- );
- const total = useStateFromStores(
- [GuildMemberCountStore],
- () => GuildMemberCountStore.getMemberCount(guildId)
- );
-
- if (total == null)
- return null;
-
- const online =
- (groups.length === 1 && groups[0].id === "unknown")
- ? 0
- : groups.reduce((count, curr) => count + (curr.id === "offline" ? 0 : curr.count), 0);
-
- return (
-
-
- {props => (
-
-
- {numberFormat(online)}
-
- )}
-
-
- {props => (
-
-
- {numberFormat(total)}
-
- )}
-
-
- );
-}
+export const numberFormat = (value: number) => sharedIntlNumberFormat.format(value);
+export const cl = classNameFactory("vc-membercount-");
export default definePlugin({
name: "MemberCount",
- description: "Shows the amount of online & total members in the server member list",
+ description: "Shows the amount of online & total members in the server member list and tooltip",
authors: [Devs.Ven, Devs.Commandtechno],
- patches: [{
- find: "{isSidebarVisible:",
- replacement: {
- match: /(?<=let\{className:(\i),.+?children):\[(\i\.useMemo[^}]+"aria-multiselectable")/,
- replace: ":[$1?.startsWith('members')?$self.render():null,$2"
+ patches: [
+ {
+ find: "{isSidebarVisible:",
+ replacement: {
+ match: /(?<=let\{className:(\i),.+?children):\[(\i\.useMemo[^}]+"aria-multiselectable")/,
+ replace: ":[$1?.startsWith('members')?$self.render():null,$2"
+ }
+ },
+ {
+ find: ".invitesDisabledTooltip",
+ replacement: {
+ match: /(?<=\.VIEW_AS_ROLES_MENTIONS_WARNING.{0,100})]/,
+ replace: ",$self.renderTooltip(arguments[0].guild)]"
+ }
}
- }],
+ ],
- render: ErrorBoundary.wrap(MemberCount, { noop: true })
+ render: ErrorBoundary.wrap(MemberCount, { noop: true }),
+ renderTooltip: ErrorBoundary.wrap(guild => , { noop: true })
});
diff --git a/src/plugins/memberCount/style.css b/src/plugins/memberCount/style.css
new file mode 100644
index 000000000..f43bff830
--- /dev/null
+++ b/src/plugins/memberCount/style.css
@@ -0,0 +1,44 @@
+.vc-membercount-widget {
+ display: flex;
+ align-content: center;
+
+ --color-online: var(--green-360);
+ --color-total: var(--primary-400);
+}
+
+.vc-membercount-tooltip {
+ margin-top: 0.25em;
+ margin-left: 2px;
+}
+
+.vc-membercount-member-list {
+ justify-content: center;
+ margin-top: 1em;
+ padding-inline: 1em;
+}
+
+.vc-membercount-online {
+ color: var(--color-online);
+}
+
+.vc-membercount-total {
+ color: var(--color-total);
+}
+
+.vc-membercount-online-dot {
+ background-color: var(--color-online);
+ display: inline-block;
+ width: 12px;
+ height: 12px;
+ border-radius: 50%;
+ margin-right: 0.5em;
+}
+
+.vc-membercount-total-dot {
+ display: inline-block;
+ width: 6px;
+ height: 6px;
+ border-radius: 50%;
+ border: 3px solid var(--color-total);
+ margin: 0 0.5em 0 1em;
+}
diff --git a/src/plugins/notificationVolume/index.ts b/src/plugins/notificationVolume/index.ts
index 50eabee73..bc3c7539d 100644
--- a/src/plugins/notificationVolume/index.ts
+++ b/src/plugins/notificationVolume/index.ts
@@ -27,8 +27,8 @@ export default definePlugin({
{
find: "_ensureAudio(){",
replacement: {
- match: /onloadeddata=\(\)=>\{.\.volume=/,
- replace: "$&$self.settings.store.notificationVolume/100*"
+ match: /(?=Math\.min\(\i\.\i\.getOutputVolume\(\)\/100)/,
+ replace: "$self.settings.store.notificationVolume/100*"
},
},
],
diff --git a/src/utils/cloud.tsx b/src/utils/cloud.tsx
index f56c78dc5..508b1c7ef 100644
--- a/src/utils/cloud.tsx
+++ b/src/utils/cloud.tsx
@@ -106,7 +106,7 @@ export async function authorizeCloud() {
try {
const res = await fetch(location, {
- headers: new Headers({ Accept: "application/json" })
+ headers: { Accept: "application/json" }
});
const { secret } = await res.json();
if (secret) {
diff --git a/src/utils/settingsSync.ts b/src/utils/settingsSync.ts
index ec32e2b1e..9a0f260af 100644
--- a/src/utils/settingsSync.ts
+++ b/src/utils/settingsSync.ts
@@ -118,10 +118,10 @@ export async function putCloudSettings(manual?: boolean) {
try {
const res = await fetch(new URL("/v1/settings", getCloudUrl()), {
method: "PUT",
- headers: new Headers({
+ headers: {
Authorization: await getCloudAuth(),
"Content-Type": "application/octet-stream"
- }),
+ },
body: deflateSync(new TextEncoder().encode(settings))
});
@@ -162,11 +162,11 @@ export async function getCloudSettings(shouldNotify = true, force = false) {
try {
const res = await fetch(new URL("/v1/settings", getCloudUrl()), {
method: "GET",
- headers: new Headers({
+ headers: {
Authorization: await getCloudAuth(),
Accept: "application/octet-stream",
"If-None-Match": Settings.cloud.settingsSyncVersion.toString()
- }),
+ },
});
if (res.status === 404) {
@@ -251,9 +251,7 @@ export async function deleteCloudSettings() {
try {
const res = await fetch(new URL("/v1/settings", getCloudUrl()), {
method: "DELETE",
- headers: new Headers({
- Authorization: await getCloudAuth()
- }),
+ headers: { Authorization: await getCloudAuth() },
});
if (!res.ok) {
diff --git a/src/webpack/webpack.ts b/src/webpack/webpack.ts
index d65f57fcb..a68890a83 100644
--- a/src/webpack/webpack.ts
+++ b/src/webpack/webpack.ts
@@ -83,8 +83,8 @@ export function _initWebpack(instance: typeof window.webpackChunkdiscord_app) {
return true;
}
+let devToolsOpen = false;
if (IS_DEV && IS_DISCORD_DESKTOP) {
- var devToolsOpen = false;
// At this point in time, DiscordNative has not been exposed yet, so setImmediate is needed
setTimeout(() => {
DiscordNative/* just to make sure */?.window.setDevtoolsCallbacks(() => devToolsOpen = true, () => devToolsOpen = false);