Compare commits
9 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
6b10947d06 | ||
|
97b6699afe | ||
|
7e91edc757 | ||
|
a82544e93e | ||
|
d8c8b74ed7 | ||
|
fadd1598f5 | ||
|
e5c0898dd6 | ||
|
9550b74b2a | ||
|
6cfb67a52e |
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@ -42,7 +42,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Clean up obsolete files
|
- name: Clean up obsolete files
|
||||||
run: |
|
run: |
|
||||||
rm -rf dist/*-unpacked Vencord.user.css vencordDesktopRenderer.css vencordDesktopRenderer.css.map
|
rm -rf dist/*-unpacked dist/monaco Vencord.user.css vencordDesktopRenderer.css vencordDesktopRenderer.css.map
|
||||||
|
|
||||||
- name: Get some values needed for the release
|
- name: Get some values needed for the release
|
||||||
id: release_values
|
id: release_values
|
||||||
|
@ -1,32 +0,0 @@
|
|||||||
/**
|
|
||||||
* @template T
|
|
||||||
* @param {T[]} arr
|
|
||||||
* @param {(v: T) => boolean} predicate
|
|
||||||
*/
|
|
||||||
function removeFirst(arr, predicate) {
|
|
||||||
const idx = arr.findIndex(predicate);
|
|
||||||
if (idx !== -1) arr.splice(idx, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
chrome.webRequest.onHeadersReceived.addListener(
|
|
||||||
({ responseHeaders, type, url }) => {
|
|
||||||
if (!responseHeaders) return;
|
|
||||||
|
|
||||||
if (type === "main_frame") {
|
|
||||||
// In main frame requests, the CSP needs to be removed to enable fetching of custom css
|
|
||||||
// as desired by the user
|
|
||||||
removeFirst(responseHeaders, h => h.name.toLowerCase() === "content-security-policy");
|
|
||||||
} else if (type === "stylesheet" && url.startsWith("https://raw.githubusercontent.com/")) {
|
|
||||||
// Most users will load css from GitHub, but GitHub doesn't set the correct content type,
|
|
||||||
// so we fix it here
|
|
||||||
removeFirst(responseHeaders, h => h.name.toLowerCase() === "content-type");
|
|
||||||
responseHeaders.push({
|
|
||||||
name: "Content-Type",
|
|
||||||
value: "text/css"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return { responseHeaders };
|
|
||||||
},
|
|
||||||
{ urls: ["https://raw.githubusercontent.com/*", "*://*.discord.com/*"], types: ["main_frame", "stylesheet"] },
|
|
||||||
["blocking", "responseHeaders"]
|
|
||||||
);
|
|
@ -26,11 +26,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
"background": {
|
"web_accessible_resources": ["dist/*", "third-party/*"],
|
||||||
"scripts": ["background.js"]
|
|
||||||
},
|
|
||||||
|
|
||||||
"web_accessible_resources": ["dist/Vencord.js", "dist/Vencord.css"],
|
|
||||||
|
|
||||||
"browser_specific_settings": {
|
"browser_specific_settings": {
|
||||||
"gecko": {
|
"gecko": {
|
||||||
|
21
browser/third-party/rnnoise/LICENSE
vendored
21
browser/third-party/rnnoise/LICENSE
vendored
@ -1,21 +0,0 @@
|
|||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2022 翠 / green
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
BIN
browser/third-party/rnnoise/rnnoise.wasm
vendored
BIN
browser/third-party/rnnoise/rnnoise.wasm
vendored
Binary file not shown.
File diff suppressed because one or more lines are too long
BIN
browser/third-party/rnnoise/rnnoise_simd.wasm
vendored
BIN
browser/third-party/rnnoise/rnnoise_simd.wasm
vendored
Binary file not shown.
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "vencord",
|
"name": "vencord",
|
||||||
"private": "true",
|
"private": "true",
|
||||||
"version": "1.5.0",
|
"version": "1.5.1",
|
||||||
"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": {
|
||||||
|
@ -57,6 +57,13 @@ const MonacoWorkerEntryPoints = [
|
|||||||
"vs/editor/editor.worker.js"
|
"vs/editor/editor.worker.js"
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const RnNoiseFiles = [
|
||||||
|
"dist/rnnoise.wasm",
|
||||||
|
"dist/rnnoise_simd.wasm",
|
||||||
|
"dist/rnnoise/workletProcessor.js",
|
||||||
|
"LICENSE"
|
||||||
|
];
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
[
|
[
|
||||||
esbuild.build({
|
esbuild.build({
|
||||||
@ -138,12 +145,14 @@ async function loadDir(dir, basePath = "") {
|
|||||||
/**
|
/**
|
||||||
* @type {(target: string, files: string[]) => Promise<void>}
|
* @type {(target: string, files: string[]) => Promise<void>}
|
||||||
*/
|
*/
|
||||||
async function buildExtension(target, files) {
|
async function buildExtension(target, files, noMonaco = false) {
|
||||||
const entries = {
|
const entries = {
|
||||||
"dist/Vencord.js": await readFile("dist/extension.js"),
|
"dist/Vencord.js": await readFile("dist/extension.js"),
|
||||||
"dist/Vencord.css": await readFile("dist/extension.css"),
|
"dist/Vencord.css": await readFile("dist/extension.css"),
|
||||||
...await loadDir("dist/monaco"),
|
...(noMonaco ? {} : await loadDir("dist/monaco")),
|
||||||
...await loadDir("browser/third-party", "browser/"),
|
...Object.fromEntries(await Promise.all(RnNoiseFiles.map(async file =>
|
||||||
|
[`third-party/rnnoise/${file.replace(/^dist\//, "")}`, await readFile(`node_modules/@sapphi-red/web-noise-suppressor/${file}`)]
|
||||||
|
))),
|
||||||
...Object.fromEntries(await Promise.all(files.map(async f => {
|
...Object.fromEntries(await Promise.all(files.map(async f => {
|
||||||
let content = await readFile(join("browser", f));
|
let content = await readFile(join("browser", f));
|
||||||
if (f.startsWith("manifest")) {
|
if (f.startsWith("manifest")) {
|
||||||
@ -186,7 +195,7 @@ const appendCssRuntime = readFile("dist/Vencord.user.css", "utf-8").then(content
|
|||||||
await Promise.all([
|
await Promise.all([
|
||||||
appendCssRuntime,
|
appendCssRuntime,
|
||||||
buildExtension("chromium-unpacked", ["modifyResponseHeaders.json", "content.js", "manifest.json", "icon.png"]),
|
buildExtension("chromium-unpacked", ["modifyResponseHeaders.json", "content.js", "manifest.json", "icon.png"]),
|
||||||
buildExtension("firefox-unpacked", ["background.js", "content.js", "manifestv2.json", "icon.png"]),
|
buildExtension("firefox-unpacked", ["content.js", "manifestv2.json", "icon.png"], true),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
Zip.sync.zip("dist/chromium-unpacked").compress().save("dist/extension.zip");
|
Zip.sync.zip("dist/chromium-unpacked").compress().save("dist/extension.zip");
|
||||||
|
@ -18,8 +18,10 @@
|
|||||||
|
|
||||||
import { useSettings } from "@api/Settings";
|
import { useSettings } from "@api/Settings";
|
||||||
import { classNameFactory } from "@api/Styles";
|
import { classNameFactory } from "@api/Styles";
|
||||||
|
import { ErrorCard } from "@components/ErrorCard";
|
||||||
import { Flex } from "@components/Flex";
|
import { Flex } from "@components/Flex";
|
||||||
import { Link } from "@components/Link";
|
import { Link } from "@components/Link";
|
||||||
|
import { IsFirefox } from "@utils/constants";
|
||||||
import { Margins } from "@utils/margins";
|
import { Margins } from "@utils/margins";
|
||||||
import { classes } from "@utils/misc";
|
import { classes } from "@utils/misc";
|
||||||
import { showItemInFolder } from "@utils/native";
|
import { showItemInFolder } from "@utils/native";
|
||||||
@ -249,12 +251,14 @@ function ThemesTab() {
|
|||||||
>
|
>
|
||||||
Load missing Themes
|
Load missing Themes
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
{!IsFirefox && (
|
||||||
onClick={() => VencordNative.quickCss.openEditor()}
|
<Button
|
||||||
size={Button.Sizes.SMALL}
|
onClick={() => VencordNative.quickCss.openEditor()}
|
||||||
>
|
size={Button.Sizes.SMALL}
|
||||||
Edit QuickCSS
|
>
|
||||||
</Button>
|
Edit QuickCSS
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
@ -316,6 +320,15 @@ function ThemesTab() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<SettingsTab title="Themes">
|
<SettingsTab title="Themes">
|
||||||
|
{IsFirefox && (
|
||||||
|
<ErrorCard>
|
||||||
|
<Forms.FormTitle tag="h5">Warning</Forms.FormTitle>
|
||||||
|
<Forms.FormText>
|
||||||
|
You are using Firefox. Expect the vast majority of themes to not work.
|
||||||
|
If this is a problem, use a chromium browser or Discord Desktop / Vesktop.
|
||||||
|
</Forms.FormText>
|
||||||
|
</ErrorCard>
|
||||||
|
)}
|
||||||
<TabBar
|
<TabBar
|
||||||
type="top"
|
type="top"
|
||||||
look="brand"
|
look="brand"
|
||||||
|
@ -21,6 +21,7 @@ import { Settings, useSettings } from "@api/Settings";
|
|||||||
import { classNameFactory } from "@api/Styles";
|
import { classNameFactory } from "@api/Styles";
|
||||||
import DonateButton from "@components/DonateButton";
|
import DonateButton from "@components/DonateButton";
|
||||||
import { ErrorCard } from "@components/ErrorCard";
|
import { ErrorCard } from "@components/ErrorCard";
|
||||||
|
import { IsFirefox } from "@utils/constants";
|
||||||
import { Margins } from "@utils/margins";
|
import { Margins } from "@utils/margins";
|
||||||
import { identity } from "@utils/misc";
|
import { identity } from "@utils/misc";
|
||||||
import { relaunch, showItemInFolder } from "@utils/native";
|
import { relaunch, showItemInFolder } from "@utils/native";
|
||||||
@ -109,12 +110,14 @@ function VencordSettings() {
|
|||||||
Restart Client
|
Restart Client
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
<Button
|
{!IsFirefox && (
|
||||||
onClick={() => VencordNative.quickCss.openEditor()}
|
<Button
|
||||||
size={Button.Sizes.SMALL}
|
onClick={() => VencordNative.quickCss.openEditor()}
|
||||||
disabled={settingsDir === "Loading..."}>
|
size={Button.Sizes.SMALL}
|
||||||
Open QuickCSS File
|
disabled={settingsDir === "Loading..."}>
|
||||||
</Button>
|
Open QuickCSS File
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
{!IS_WEB && (
|
{!IS_WEB && (
|
||||||
<Button
|
<Button
|
||||||
onClick={() => showItemInFolder(settingsDir)}
|
onClick={() => showItemInFolder(settingsDir)}
|
||||||
@ -257,7 +260,11 @@ function DonateCard({ image }: DonateCardProps) {
|
|||||||
src={image}
|
src={image}
|
||||||
alt=""
|
alt=""
|
||||||
height={128}
|
height={128}
|
||||||
style={{ marginLeft: "auto", transform: image === DEFAULT_DONATE_IMAGE ? "rotate(10deg)" : "" }}
|
style={{
|
||||||
|
imageRendering: image === SHIGGY_DONATE_IMAGE ? "pixelated" : void 0,
|
||||||
|
marginLeft: "auto",
|
||||||
|
transform: image === DEFAULT_DONATE_IMAGE ? "rotate(10deg)" : void 0
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { DataStore } from "@api/index";
|
import { DataStore } from "@api/index";
|
||||||
import { Devs, SUPPORT_CHANNEL_ID } from "@utils/constants";
|
import { Devs, IsFirefox, SUPPORT_CHANNEL_ID } from "@utils/constants";
|
||||||
import { isPluginDev } from "@utils/misc";
|
import { isPluginDev } from "@utils/misc";
|
||||||
import { makeCodeblock } from "@utils/text";
|
import { makeCodeblock } from "@utils/text";
|
||||||
import definePlugin from "@utils/types";
|
import definePlugin from "@utils/types";
|
||||||
@ -30,6 +30,7 @@ import plugins from "~plugins";
|
|||||||
import settings from "./settings";
|
import settings from "./settings";
|
||||||
|
|
||||||
const REMEMBER_DISMISS_KEY = "Vencord-SupportHelper-Dismiss";
|
const REMEMBER_DISMISS_KEY = "Vencord-SupportHelper-Dismiss";
|
||||||
|
const FIREFOX_DISMISS_KEY = "Vencord-Firefox-Warning-Dismiss";
|
||||||
|
|
||||||
const AllowedChannelIds = [
|
const AllowedChannelIds = [
|
||||||
SUPPORT_CHANNEL_ID,
|
SUPPORT_CHANNEL_ID,
|
||||||
@ -115,6 +116,22 @@ ${makeCodeblock(enabledPlugins.join(", ") + "\n\n" + enabledApiPlugins.join(", "
|
|||||||
onConfirm: rememberDismiss
|
onConfirm: rememberDismiss
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (IsFirefox) {
|
||||||
|
const rememberDismiss = () => DataStore.set(FIREFOX_DISMISS_KEY, true);
|
||||||
|
|
||||||
|
Alerts.show({
|
||||||
|
title: "Hold on!",
|
||||||
|
body: <div>
|
||||||
|
<Forms.FormText>You are using Firefox.</Forms.FormText>
|
||||||
|
<Forms.FormText>Due to Firefox's stupid extension guidelines, most themes and many plugins will not function correctly.</Forms.FormText>
|
||||||
|
<Forms.FormText>Do not report bugs. Do not ask for help with broken plugins.</Forms.FormText>
|
||||||
|
<Forms.FormText>Instead, use a chromium browser, Discord Desktop, or Vesktop.</Forms.FormText>
|
||||||
|
</div>,
|
||||||
|
onCancel: rememberDismiss,
|
||||||
|
onConfirm: rememberDismiss
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -23,7 +23,7 @@ import { Clipboard, Toasts } from "@webpack/common";
|
|||||||
|
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "BetterRoleDot",
|
name: "BetterRoleDot",
|
||||||
authors: [Devs.Ven],
|
authors: [Devs.Ven, Devs.AutumnVN],
|
||||||
description:
|
description:
|
||||||
"Copy role colour on RoleDot (accessibility setting) click. Also allows using both RoleDot and coloured names simultaneously",
|
"Copy role colour on RoleDot (accessibility setting) click. Also allows using both RoleDot and coloured names simultaneously",
|
||||||
|
|
||||||
@ -43,6 +43,23 @@ export default definePlugin({
|
|||||||
match: /"(?:username|dot)"===\i(?!\.\i)/g,
|
match: /"(?:username|dot)"===\i(?!\.\i)/g,
|
||||||
replace: "true",
|
replace: "true",
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
find: ".ADD_ROLE_A11Y_LABEL",
|
||||||
|
predicate: () => Settings.plugins.BetterRoleDot.copyRoleColorInProfilePopout,
|
||||||
|
replacement: {
|
||||||
|
match: /"dot"===\i/,
|
||||||
|
replace: "true"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
find: ".roleVerifiedIcon",
|
||||||
|
predicate: () => Settings.plugins.BetterRoleDot.copyRoleColorInProfilePopout,
|
||||||
|
replacement: {
|
||||||
|
match: /"dot"===\i/,
|
||||||
|
replace: "true"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
@ -50,7 +67,14 @@ export default definePlugin({
|
|||||||
bothStyles: {
|
bothStyles: {
|
||||||
type: OptionType.BOOLEAN,
|
type: OptionType.BOOLEAN,
|
||||||
description: "Show both role dot and coloured names",
|
description: "Show both role dot and coloured names",
|
||||||
|
restartNeeded: true,
|
||||||
default: false,
|
default: false,
|
||||||
|
},
|
||||||
|
copyRoleColorInProfilePopout: {
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
description: "Allow click on role dot in profile popout to copy role color",
|
||||||
|
restartNeeded: true,
|
||||||
|
default: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -103,6 +103,11 @@ interface StickerPack {
|
|||||||
stickers: Sticker[];
|
stickers: Sticker[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const enum FakeNoticeType {
|
||||||
|
Sticker,
|
||||||
|
Emoji
|
||||||
|
}
|
||||||
|
|
||||||
const fakeNitroEmojiRegex = /\/emojis\/(\d+?)\.(png|webp|gif)/;
|
const fakeNitroEmojiRegex = /\/emojis\/(\d+?)\.(png|webp|gif)/;
|
||||||
const fakeNitroStickerRegex = /\/stickers\/(\d+?)\./;
|
const fakeNitroStickerRegex = /\/stickers\/(\d+?)\./;
|
||||||
const fakeNitroGifStickerRegex = /\/attachments\/\d+?\/\d+?\/(\d+?)\.gif/;
|
const fakeNitroGifStickerRegex = /\/attachments\/\d+?\/\d+?\/(\d+?)\.gif/;
|
||||||
@ -306,16 +311,24 @@ export default definePlugin({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
match: /(emojiSection.{0,50}description:)(\i)(?<=(\i)\.sticker,.+?)(?=,)/,
|
match: /(emojiSection.{0,50}description:)(\i)(?<=(\i)\.sticker,.+?)(?=,)/,
|
||||||
replace: (_, rest, reactNode, props) => `${rest}$self.addFakeNotice("STICKER",${reactNode},!!${props}.renderableSticker?.fake)`
|
replace: (_, rest, reactNode, props) => `${rest}$self.addFakeNotice(${FakeNoticeType.Sticker},${reactNode},!!${props}.renderableSticker?.fake)`
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
find: ".Messages.EMOJI_POPOUT_PREMIUM_JOINED_GUILD_DESCRIPTION",
|
find: ".EMOJI_UPSELL_POPOUT_MORE_EMOJIS_OPENED,",
|
||||||
predicate: () => settings.store.transformEmojis,
|
predicate: () => settings.store.transformEmojis,
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /((\i)=\i\.node,\i=\i\.expressionSourceGuild)(.+?return )(.{0,450}Messages\.EMOJI_POPOUT_PREMIUM_JOINED_GUILD_DESCRIPTION.+?}\))/,
|
match: /isDiscoverable:\i,shouldHideRoleSubscriptionCTA:\i,(?<=(\i)=\i\.node.+?)/,
|
||||||
replace: (_, rest1, node, rest2, reactNode) => `${rest1},fakeNitroNode=${node}${rest2}$self.addFakeNotice("EMOJI",${reactNode},fakeNitroNode.fake)`
|
replace: (m, node) => `${m}fakeNitroNode:${node},`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
find: ".Messages.EMOJI_POPOUT_UNJOINED_DISCOVERABLE_GUILD_DESCRIPTION",
|
||||||
|
predicate: () => settings.store.transformEmojis,
|
||||||
|
replacement: {
|
||||||
|
match: /(?<=\.Messages\.EMOJI_POPOUT_ADDED_PACK_DESCRIPTION.+?return )(.{0,1200}\.Messages\.EMOJI_POPOUT_UNJOINED_DISCOVERABLE_GUILD_DESCRIPTION.+?)(}\({)/,
|
||||||
|
replace: (_, reactNode, rest) => `$self.addFakeNotice(${FakeNoticeType.Emoji},${reactNode},!!arguments[0]?.fakeNitroNode?.fake)${rest}fakeNitroNode:arguments[0]?.fakeNitroNode,`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@ -611,18 +624,18 @@ export default definePlugin({
|
|||||||
return link.target && fakeNitroEmojiRegex.test(link.target);
|
return link.target && fakeNitroEmojiRegex.test(link.target);
|
||||||
},
|
},
|
||||||
|
|
||||||
addFakeNotice(type: "STICKER" | "EMOJI", node: Array<ReactNode>, fake: boolean) {
|
addFakeNotice(type: FakeNoticeType, node: Array<ReactNode>, fake: boolean) {
|
||||||
if (!fake) return node;
|
if (!fake) return node;
|
||||||
|
|
||||||
node = Array.isArray(node) ? node : [node];
|
node = Array.isArray(node) ? node : [node];
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case "STICKER": {
|
case FakeNoticeType.Sticker: {
|
||||||
node.push(" This is a FakeNitro sticker and renders like a real sticker only for you. Appears as a link to non-plugin users.");
|
node.push(" This is a FakeNitro sticker and renders like a real sticker only for you. Appears as a link to non-plugin users.");
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
case "EMOJI": {
|
case FakeNoticeType.Emoji: {
|
||||||
node.push(" This is a FakeNitro emoji and renders like a real emoji only for you. Appears as a link to non-plugin users.");
|
node.push(" This is a FakeNitro emoji and renders like a real emoji only for you. Appears as a link to non-plugin users.");
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
|
@ -1,56 +0,0 @@
|
|||||||
/*
|
|
||||||
* Vencord, a modification for Discord's desktop app
|
|
||||||
* Copyright (c) 2023 Vendicated and contributors
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { Devs } from "@utils/constants";
|
|
||||||
import definePlugin from "@utils/types";
|
|
||||||
import { Forms } from "@webpack/common";
|
|
||||||
|
|
||||||
export default definePlugin({
|
|
||||||
name: "FixInbox",
|
|
||||||
description: "Fixes the Unreads Inbox from crashing Discord when you're in lots of guilds.",
|
|
||||||
authors: [Devs.Megu],
|
|
||||||
|
|
||||||
patches: [{
|
|
||||||
find: "INBOX_OPEN:function",
|
|
||||||
replacement: {
|
|
||||||
// This function normally dispatches a subscribe event to every guild.
|
|
||||||
// this is badbadbadbadbad so we just get rid of it.
|
|
||||||
match: /INBOX_OPEN:function.+?\{/,
|
|
||||||
replace: "$&return true;"
|
|
||||||
}
|
|
||||||
}],
|
|
||||||
|
|
||||||
settingsAboutComponent() {
|
|
||||||
return (
|
|
||||||
<Forms.FormSection>
|
|
||||||
<Forms.FormTitle tag="h3">What's the problem?</Forms.FormTitle>
|
|
||||||
<Forms.FormText style={{ marginBottom: 8 }}>
|
|
||||||
By default, Discord emits a GUILD_SUBSCRIPTIONS event for every guild you're in.
|
|
||||||
When you're in a lot of guilds, this can cause the gateway to ratelimit you.
|
|
||||||
This causes the client to crash and get stuck in an infinite ratelimit loop as it tries to reconnect.
|
|
||||||
</Forms.FormText>
|
|
||||||
|
|
||||||
<Forms.FormTitle tag="h3">How does it work?</Forms.FormTitle>
|
|
||||||
<Forms.FormText>
|
|
||||||
This plugin works by stopping the client from sending GUILD_SUBSCRIPTIONS events to the gateway when you open the unreads inbox.
|
|
||||||
This means that not all unreads will be shown, instead only already-subscribed guilds' unreads will be shown, but your client won't crash anymore.
|
|
||||||
</Forms.FormText>
|
|
||||||
</Forms.FormSection>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
@ -28,8 +28,8 @@ export default function ReviewBadge(badge: Badge) {
|
|||||||
{({ onMouseEnter, onMouseLeave }) => (
|
{({ onMouseEnter, onMouseLeave }) => (
|
||||||
<img
|
<img
|
||||||
className={cl("badge")}
|
className={cl("badge")}
|
||||||
width="24px"
|
width="22px"
|
||||||
height="24px"
|
height="22px"
|
||||||
onMouseEnter={onMouseEnter}
|
onMouseEnter={onMouseEnter}
|
||||||
onMouseLeave={onMouseLeave}
|
onMouseLeave={onMouseLeave}
|
||||||
src={badge.icon}
|
src={badge.icon}
|
||||||
|
@ -20,7 +20,7 @@ import { openUserProfile } from "@utils/discord";
|
|||||||
import { classes } from "@utils/misc";
|
import { classes } from "@utils/misc";
|
||||||
import { LazyComponent } from "@utils/react";
|
import { LazyComponent } from "@utils/react";
|
||||||
import { filters, findBulk } from "@webpack";
|
import { filters, findBulk } from "@webpack";
|
||||||
import { Alerts, moment, Timestamp, UserStore } from "@webpack/common";
|
import { Alerts, moment, Parser, Timestamp, UserStore } from "@webpack/common";
|
||||||
|
|
||||||
import { Review, ReviewType } from "../entities";
|
import { Review, ReviewType } from "../entities";
|
||||||
import { deleteReview, reportReview } from "../reviewDbApi";
|
import { deleteReview, reportReview } from "../reviewDbApi";
|
||||||
@ -30,12 +30,12 @@ import { DeleteButton, ReportButton } from "./MessageButton";
|
|||||||
import ReviewBadge from "./ReviewBadge";
|
import ReviewBadge from "./ReviewBadge";
|
||||||
|
|
||||||
export default LazyComponent(() => {
|
export default LazyComponent(() => {
|
||||||
// this is terrible, blame mantika
|
// this is terrible, blame ven
|
||||||
const p = filters.byProps;
|
const p = filters.byProps;
|
||||||
const [
|
const [
|
||||||
{ cozyMessage, buttons, message, buttonsInner, groupStart },
|
{ cozyMessage, buttons, message, buttonsInner, groupStart },
|
||||||
{ container, isHeader },
|
{ container, isHeader },
|
||||||
{ avatar, clickable, username, messageContent, wrapper, cozy },
|
{ avatar, clickable, username, wrapper, cozy },
|
||||||
buttonClasses,
|
buttonClasses,
|
||||||
botTag
|
botTag
|
||||||
] = findBulk(
|
] = findBulk(
|
||||||
@ -124,12 +124,10 @@ export default LazyComponent(() => {
|
|||||||
</Timestamp>)
|
</Timestamp>)
|
||||||
}
|
}
|
||||||
|
|
||||||
<p
|
<div className={cl("review-comment")}>
|
||||||
className={classes(messageContent)}
|
{Parser.parseGuildEventDescription(review.comment)}
|
||||||
style={{ fontSize: 15, marginTop: 4, color: "var(--text-normal)" }}
|
</div>
|
||||||
>
|
|
||||||
{review.comment}
|
|
||||||
</p>
|
|
||||||
{review.id !== 0 && (
|
{review.id !== 0 && (
|
||||||
<div className={classes(container, isHeader, buttons)} style={{
|
<div className={classes(container, isHeader, buttons)} style={{
|
||||||
padding: "0px",
|
padding: "0px",
|
||||||
|
@ -16,11 +16,9 @@
|
|||||||
* 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 { classes } from "@utils/misc";
|
import { LazyComponent, useAwaiter, useForceUpdater } from "@utils/react";
|
||||||
import { useAwaiter, useForceUpdater } from "@utils/react";
|
import { find, findByPropsLazy } from "@webpack";
|
||||||
import { findByPropsLazy } from "@webpack";
|
import { Forms, React, RelationshipStore, useRef, UserStore } from "@webpack/common";
|
||||||
import { Forms, React, RelationshipStore, UserStore } from "@webpack/common";
|
|
||||||
import type { KeyboardEvent } from "react";
|
|
||||||
|
|
||||||
import { Review } from "../entities";
|
import { Review } from "../entities";
|
||||||
import { addReview, getReviews, Response, REVIEWS_PER_PAGE } from "../reviewDbApi";
|
import { addReview, getReviews, Response, REVIEWS_PER_PAGE } from "../reviewDbApi";
|
||||||
@ -28,7 +26,12 @@ import { settings } from "../settings";
|
|||||||
import { authorize, cl, showToast } from "../utils";
|
import { authorize, cl, showToast } from "../utils";
|
||||||
import ReviewComponent from "./ReviewComponent";
|
import ReviewComponent from "./ReviewComponent";
|
||||||
|
|
||||||
const Classes = findByPropsLazy("inputDefault", "editable");
|
|
||||||
|
const Editor = findByPropsLazy("start", "end", "addMark");
|
||||||
|
const Transform = findByPropsLazy("unwrapNodes");
|
||||||
|
const InputTypes = findByPropsLazy("VOICE_CHANNEL_STATUS", "SIDEBAR");
|
||||||
|
|
||||||
|
const InputComponent = LazyComponent(() => find(m => m?.type?.render?.toString().includes("CHANNEL_TEXT_AREA).AnalyticsLocationProvider")));
|
||||||
|
|
||||||
interface UserProps {
|
interface UserProps {
|
||||||
discordId: string;
|
discordId: string;
|
||||||
@ -113,48 +116,82 @@ function ReviewList({ refetch, reviews, hideOwnReview }: { refetch(): void; revi
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function ReviewsInputComponent({ discordId, isAuthor, refetch, name }: { discordId: string, name: string; isAuthor: boolean; refetch(): void; }) {
|
export function ReviewsInputComponent({ discordId, isAuthor, refetch, name }: { discordId: string, name: string; isAuthor: boolean; refetch(): void; }) {
|
||||||
const { token } = settings.store;
|
const { token } = settings.store;
|
||||||
|
const editorRef = useRef<any>(null);
|
||||||
|
const inputType = InputTypes.FORM;
|
||||||
|
inputType.disableAutoFocus = true;
|
||||||
|
|
||||||
function onKeyPress({ key, target }: KeyboardEvent<HTMLTextAreaElement>) {
|
const channel = {
|
||||||
if (key === "Enter") {
|
flags_: 256,
|
||||||
addReview({
|
guild_id_: null,
|
||||||
userid: discordId,
|
id: "0",
|
||||||
comment: (target as HTMLInputElement).value,
|
getGuildId: () => null,
|
||||||
star: -1
|
isPrivate: () => true,
|
||||||
}).then(res => {
|
isActiveThread: () => false,
|
||||||
if (res?.success) {
|
isArchivedLockedThread: () => false,
|
||||||
(target as HTMLInputElement).value = ""; // clear the input
|
isDM: () => true,
|
||||||
refetch();
|
roles: { "0": { permissions: 0n } },
|
||||||
} else if (res?.message) {
|
getRecipientId: () => "0",
|
||||||
showToast(res.message);
|
hasFlag: () => false,
|
||||||
}
|
};
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<textarea
|
<>
|
||||||
className={classes(Classes.inputDefault, "enter-comment", cl("input"))}
|
<div onClick={() => {
|
||||||
onKeyDownCapture={e => {
|
|
||||||
if (e.key === "Enter") {
|
|
||||||
e.preventDefault(); // prevent newlines
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
placeholder={
|
|
||||||
!token
|
|
||||||
? "You need to authorize to review users!"
|
|
||||||
: isAuthor
|
|
||||||
? `Update review for @${name}`
|
|
||||||
: `Review @${name}`
|
|
||||||
}
|
|
||||||
onKeyDown={onKeyPress}
|
|
||||||
onClick={() => {
|
|
||||||
if (!token) {
|
if (!token) {
|
||||||
showToast("Opening authorization window...");
|
showToast("Opening authorization window...");
|
||||||
authorize();
|
authorize();
|
||||||
}
|
}
|
||||||
}}
|
}}>
|
||||||
/>
|
<InputComponent
|
||||||
|
className={cl("input")}
|
||||||
|
channel={channel}
|
||||||
|
placeholder={
|
||||||
|
!token
|
||||||
|
? "You need to authorize to review users!"
|
||||||
|
: isAuthor
|
||||||
|
? `Update review for @${name}`
|
||||||
|
: `Review @${name}`
|
||||||
|
}
|
||||||
|
type={inputType}
|
||||||
|
disableThemedBackground={true}
|
||||||
|
setEditorRef={ref => editorRef.current = ref}
|
||||||
|
textValue=""
|
||||||
|
onSubmit={
|
||||||
|
async res => {
|
||||||
|
const response = await addReview({
|
||||||
|
userid: discordId,
|
||||||
|
comment: res.value,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response?.success) {
|
||||||
|
refetch();
|
||||||
|
|
||||||
|
const slateEditor = editorRef.current.ref.current.getSlateEditor();
|
||||||
|
|
||||||
|
// clear editor
|
||||||
|
Transform.delete(slateEditor, {
|
||||||
|
at: {
|
||||||
|
anchor: Editor.start(slateEditor, []),
|
||||||
|
focus: Editor.end(slateEditor, []),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (response?.message) {
|
||||||
|
showToast(response.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
// even tho we need to return this, it doesnt do anything
|
||||||
|
return {
|
||||||
|
shouldClear: false,
|
||||||
|
shouldRefocus: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,13 @@ export const enum ReviewType {
|
|||||||
System = 3
|
System = 3
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const enum NotificationType {
|
||||||
|
Info = 0,
|
||||||
|
Ban = 1,
|
||||||
|
Unban = 2,
|
||||||
|
Warning = 3
|
||||||
|
}
|
||||||
|
|
||||||
export interface Badge {
|
export interface Badge {
|
||||||
name: string;
|
name: string;
|
||||||
description: string;
|
description: string;
|
||||||
@ -45,6 +52,13 @@ export interface BanInfo {
|
|||||||
banEndDate: number;
|
banEndDate: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface Notification {
|
||||||
|
id: number;
|
||||||
|
title: string;
|
||||||
|
content: string;
|
||||||
|
type: NotificationType;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ReviewDBUser {
|
export interface ReviewDBUser {
|
||||||
ID: number;
|
ID: number;
|
||||||
discordID: string;
|
discordID: string;
|
||||||
@ -54,6 +68,7 @@ export interface ReviewDBUser {
|
|||||||
warningCount: number;
|
warningCount: number;
|
||||||
badges: any[];
|
badges: any[];
|
||||||
banInfo: BanInfo | null;
|
banInfo: BanInfo | null;
|
||||||
|
notification: Notification | null;
|
||||||
lastReviewID: number;
|
lastReviewID: number;
|
||||||
type: UserType;
|
type: UserType;
|
||||||
}
|
}
|
||||||
|
@ -24,13 +24,13 @@ import ExpandableHeader from "@components/ExpandableHeader";
|
|||||||
import { OpenExternalIcon } from "@components/Icons";
|
import { OpenExternalIcon } from "@components/Icons";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import definePlugin from "@utils/types";
|
import definePlugin from "@utils/types";
|
||||||
import { Alerts, Menu, useState } from "@webpack/common";
|
import { Alerts, Menu, Parser, useState } from "@webpack/common";
|
||||||
import { Guild, User } from "discord-types/general";
|
import { Guild, User } from "discord-types/general";
|
||||||
|
|
||||||
import { openReviewsModal } from "./components/ReviewModal";
|
import { openReviewsModal } from "./components/ReviewModal";
|
||||||
import ReviewsView from "./components/ReviewsView";
|
import ReviewsView from "./components/ReviewsView";
|
||||||
import { UserType } from "./entities";
|
import { NotificationType } from "./entities";
|
||||||
import { getCurrentUserInfo } from "./reviewDbApi";
|
import { getCurrentUserInfo, readNotification } from "./reviewDbApi";
|
||||||
import { settings } from "./settings";
|
import { settings } from "./settings";
|
||||||
import { showToast } from "./utils";
|
import { showToast } from "./utils";
|
||||||
|
|
||||||
@ -78,40 +78,33 @@ export default definePlugin({
|
|||||||
|
|
||||||
addContextMenuPatch("guild-header-popout", guildPopoutPatch);
|
addContextMenuPatch("guild-header-popout", guildPopoutPatch);
|
||||||
|
|
||||||
if (user.banInfo) {
|
if (user.notification) {
|
||||||
const endDate = new Date(user.banInfo.banEndDate);
|
const props = user.notification.type === NotificationType.Ban ? {
|
||||||
if (endDate.getTime() > Date.now() && (s.user?.banInfo?.banEndDate ?? 0) < endDate.getTime()) {
|
cancelText: "Appeal",
|
||||||
Alerts.show({
|
confirmText: "Ok",
|
||||||
title: "You have been banned from ReviewDB",
|
onCancel: () =>
|
||||||
body: (
|
VencordNative.native.openExternal(
|
||||||
<>
|
"https://reviewdb.mantikafasi.dev/api/redirect?"
|
||||||
<p>
|
+ new URLSearchParams({
|
||||||
You are banned from ReviewDB {
|
token: settings.store.token!,
|
||||||
user.type === UserType.Banned
|
page: "dashboard/appeal"
|
||||||
? "permanently"
|
})
|
||||||
: "until " + endDate.toLocaleString()
|
)
|
||||||
}
|
} : {};
|
||||||
</p>
|
|
||||||
{user.banInfo.reviewContent && (
|
|
||||||
<p>Offending Review: {user.banInfo.reviewContent}</p>
|
|
||||||
)}
|
|
||||||
<p>Continued offenses will result in a permanent ban.</p>
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
cancelText: "Appeal",
|
|
||||||
confirmText: "Ok",
|
|
||||||
onCancel: () =>
|
|
||||||
VencordNative.native.openExternal(
|
|
||||||
"https://reviewdb.mantikafasi.dev/api/redirect?"
|
|
||||||
+ new URLSearchParams({
|
|
||||||
token: settings.store.token!,
|
|
||||||
page: "dashboard/appeal"
|
|
||||||
})
|
|
||||||
)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
Alerts.show({
|
||||||
|
title: user.notification.title,
|
||||||
|
body: (
|
||||||
|
Parser.parse(
|
||||||
|
user.notification.content,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
),
|
||||||
|
...props
|
||||||
|
});
|
||||||
|
|
||||||
|
readNotification(user.notification.id);
|
||||||
|
}
|
||||||
s.user = user;
|
s.user = user;
|
||||||
}, 4000);
|
}, 4000);
|
||||||
},
|
},
|
||||||
|
@ -140,3 +140,12 @@ export function getCurrentUserInfo(token: string): Promise<ReviewDBUser> {
|
|||||||
method: "POST",
|
method: "POST",
|
||||||
}).then(r => r.json());
|
}).then(r => r.json());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function readNotification(id: number) {
|
||||||
|
return fetch(API_URL + `/api/reviewdb/notifications?id=${id}`, {
|
||||||
|
method: "PATCH",
|
||||||
|
headers: {
|
||||||
|
"Authorization": settings.store.token || "",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
@ -14,7 +14,16 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
border: 1px solid var(--profile-message-input-border-color);
|
border: 1px solid var(--profile-message-input-border-color);
|
||||||
font-size: 14px;
|
}
|
||||||
|
|
||||||
|
.vc-rdb-modal-footer > div {
|
||||||
|
width: 100%;
|
||||||
|
margin: 6px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* When input becomes disabled(while sending review), input adds unneccesary padding to left, this prevents it */
|
||||||
|
.vc-rdb-input > div > div {
|
||||||
|
padding-left: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vc-rdb-placeholder {
|
.vc-rdb-placeholder {
|
||||||
@ -24,13 +33,12 @@
|
|||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
}
|
}
|
||||||
|
|
||||||
.vc-rdb-modal-footer {
|
.vc-rdb-input * {
|
||||||
padding: 0;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vc-rdb-modal-footer > div {
|
.vc-rdb-modal-footer {
|
||||||
width: 100%;
|
padding: 0;
|
||||||
margin: 6px 16px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.vc-rdb-modal-footer .vc-rdb-input {
|
.vc-rdb-modal-footer .vc-rdb-input {
|
||||||
@ -49,3 +57,20 @@
|
|||||||
.vc-rdb-modal-reviews {
|
.vc-rdb-modal-reviews {
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.vc-rdb-review {
|
||||||
|
margin-top: 8px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-rdb-review-comment img {
|
||||||
|
vertical-align: text-top;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-rdb-review-comment {
|
||||||
|
overflow-y: hidden;
|
||||||
|
margin-top: 1px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
color: var(--text-normal);
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
@ -385,3 +385,5 @@ export const DevsById = /* #__PURE__*/ (() =>
|
|||||||
.map(([_, v]) => [v.id, v] as const)
|
.map(([_, v]) => [v.id, v] as const)
|
||||||
))
|
))
|
||||||
)() as Record<string, Dev>;
|
)() as Record<string, Dev>;
|
||||||
|
|
||||||
|
export const IsFirefox = IS_EXTENSION && navigator.userAgent.toLowerCase().includes("firefox");
|
||||||
|
Reference in New Issue
Block a user