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
|
||||
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
|
||||
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": {
|
||||
"scripts": ["background.js"]
|
||||
},
|
||||
|
||||
"web_accessible_resources": ["dist/Vencord.js", "dist/Vencord.css"],
|
||||
"web_accessible_resources": ["dist/*", "third-party/*"],
|
||||
|
||||
"browser_specific_settings": {
|
||||
"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",
|
||||
"private": "true",
|
||||
"version": "1.5.0",
|
||||
"version": "1.5.1",
|
||||
"description": "The cutest Discord client mod",
|
||||
"homepage": "https://github.com/Vendicated/Vencord#readme",
|
||||
"bugs": {
|
||||
|
@ -57,6 +57,13 @@ const MonacoWorkerEntryPoints = [
|
||||
"vs/editor/editor.worker.js"
|
||||
];
|
||||
|
||||
const RnNoiseFiles = [
|
||||
"dist/rnnoise.wasm",
|
||||
"dist/rnnoise_simd.wasm",
|
||||
"dist/rnnoise/workletProcessor.js",
|
||||
"LICENSE"
|
||||
];
|
||||
|
||||
await Promise.all(
|
||||
[
|
||||
esbuild.build({
|
||||
@ -138,12 +145,14 @@ async function loadDir(dir, basePath = "") {
|
||||
/**
|
||||
* @type {(target: string, files: string[]) => Promise<void>}
|
||||
*/
|
||||
async function buildExtension(target, files) {
|
||||
async function buildExtension(target, files, noMonaco = false) {
|
||||
const entries = {
|
||||
"dist/Vencord.js": await readFile("dist/extension.js"),
|
||||
"dist/Vencord.css": await readFile("dist/extension.css"),
|
||||
...await loadDir("dist/monaco"),
|
||||
...await loadDir("browser/third-party", "browser/"),
|
||||
...(noMonaco ? {} : await loadDir("dist/monaco")),
|
||||
...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 => {
|
||||
let content = await readFile(join("browser", f));
|
||||
if (f.startsWith("manifest")) {
|
||||
@ -186,7 +195,7 @@ const appendCssRuntime = readFile("dist/Vencord.user.css", "utf-8").then(content
|
||||
await Promise.all([
|
||||
appendCssRuntime,
|
||||
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");
|
||||
|
@ -18,8 +18,10 @@
|
||||
|
||||
import { useSettings } from "@api/Settings";
|
||||
import { classNameFactory } from "@api/Styles";
|
||||
import { ErrorCard } from "@components/ErrorCard";
|
||||
import { Flex } from "@components/Flex";
|
||||
import { Link } from "@components/Link";
|
||||
import { IsFirefox } from "@utils/constants";
|
||||
import { Margins } from "@utils/margins";
|
||||
import { classes } from "@utils/misc";
|
||||
import { showItemInFolder } from "@utils/native";
|
||||
@ -249,12 +251,14 @@ function ThemesTab() {
|
||||
>
|
||||
Load missing Themes
|
||||
</Button>
|
||||
{!IsFirefox && (
|
||||
<Button
|
||||
onClick={() => VencordNative.quickCss.openEditor()}
|
||||
size={Button.Sizes.SMALL}
|
||||
>
|
||||
Edit QuickCSS
|
||||
</Button>
|
||||
)}
|
||||
</>
|
||||
</Card>
|
||||
|
||||
@ -316,6 +320,15 @@ function ThemesTab() {
|
||||
|
||||
return (
|
||||
<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
|
||||
type="top"
|
||||
look="brand"
|
||||
|
@ -21,6 +21,7 @@ import { Settings, useSettings } from "@api/Settings";
|
||||
import { classNameFactory } from "@api/Styles";
|
||||
import DonateButton from "@components/DonateButton";
|
||||
import { ErrorCard } from "@components/ErrorCard";
|
||||
import { IsFirefox } from "@utils/constants";
|
||||
import { Margins } from "@utils/margins";
|
||||
import { identity } from "@utils/misc";
|
||||
import { relaunch, showItemInFolder } from "@utils/native";
|
||||
@ -109,12 +110,14 @@ function VencordSettings() {
|
||||
Restart Client
|
||||
</Button>
|
||||
)}
|
||||
{!IsFirefox && (
|
||||
<Button
|
||||
onClick={() => VencordNative.quickCss.openEditor()}
|
||||
size={Button.Sizes.SMALL}
|
||||
disabled={settingsDir === "Loading..."}>
|
||||
Open QuickCSS File
|
||||
</Button>
|
||||
)}
|
||||
{!IS_WEB && (
|
||||
<Button
|
||||
onClick={() => showItemInFolder(settingsDir)}
|
||||
@ -257,7 +260,11 @@ function DonateCard({ image }: DonateCardProps) {
|
||||
src={image}
|
||||
alt=""
|
||||
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>
|
||||
);
|
||||
|
@ -17,7 +17,7 @@
|
||||
*/
|
||||
|
||||
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 { makeCodeblock } from "@utils/text";
|
||||
import definePlugin from "@utils/types";
|
||||
@ -30,6 +30,7 @@ import plugins from "~plugins";
|
||||
import settings from "./settings";
|
||||
|
||||
const REMEMBER_DISMISS_KEY = "Vencord-SupportHelper-Dismiss";
|
||||
const FIREFOX_DISMISS_KEY = "Vencord-Firefox-Warning-Dismiss";
|
||||
|
||||
const AllowedChannelIds = [
|
||||
SUPPORT_CHANNEL_ID,
|
||||
@ -115,6 +116,22 @@ ${makeCodeblock(enabledPlugins.join(", ") + "\n\n" + enabledApiPlugins.join(", "
|
||||
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({
|
||||
name: "BetterRoleDot",
|
||||
authors: [Devs.Ven],
|
||||
authors: [Devs.Ven, Devs.AutumnVN],
|
||||
description:
|
||||
"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,
|
||||
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: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Show both role dot and coloured names",
|
||||
restartNeeded: true,
|
||||
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[];
|
||||
}
|
||||
|
||||
const enum FakeNoticeType {
|
||||
Sticker,
|
||||
Emoji
|
||||
}
|
||||
|
||||
const fakeNitroEmojiRegex = /\/emojis\/(\d+?)\.(png|webp|gif)/;
|
||||
const fakeNitroStickerRegex = /\/stickers\/(\d+?)\./;
|
||||
const fakeNitroGifStickerRegex = /\/attachments\/\d+?\/\d+?\/(\d+?)\.gif/;
|
||||
@ -306,16 +311,24 @@ export default definePlugin({
|
||||
},
|
||||
{
|
||||
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,
|
||||
replacement: {
|
||||
match: /((\i)=\i\.node,\i=\i\.expressionSourceGuild)(.+?return )(.{0,450}Messages\.EMOJI_POPOUT_PREMIUM_JOINED_GUILD_DESCRIPTION.+?}\))/,
|
||||
replace: (_, rest1, node, rest2, reactNode) => `${rest1},fakeNitroNode=${node}${rest2}$self.addFakeNotice("EMOJI",${reactNode},fakeNitroNode.fake)`
|
||||
match: /isDiscoverable:\i,shouldHideRoleSubscriptionCTA:\i,(?<=(\i)=\i\.node.+?)/,
|
||||
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);
|
||||
},
|
||||
|
||||
addFakeNotice(type: "STICKER" | "EMOJI", node: Array<ReactNode>, fake: boolean) {
|
||||
addFakeNotice(type: FakeNoticeType, node: Array<ReactNode>, fake: boolean) {
|
||||
if (!fake) return node;
|
||||
|
||||
node = Array.isArray(node) ? node : [node];
|
||||
|
||||
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.");
|
||||
|
||||
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.");
|
||||
|
||||
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 }) => (
|
||||
<img
|
||||
className={cl("badge")}
|
||||
width="24px"
|
||||
height="24px"
|
||||
width="22px"
|
||||
height="22px"
|
||||
onMouseEnter={onMouseEnter}
|
||||
onMouseLeave={onMouseLeave}
|
||||
src={badge.icon}
|
||||
|
@ -20,7 +20,7 @@ import { openUserProfile } from "@utils/discord";
|
||||
import { classes } from "@utils/misc";
|
||||
import { LazyComponent } from "@utils/react";
|
||||
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 { deleteReview, reportReview } from "../reviewDbApi";
|
||||
@ -30,12 +30,12 @@ import { DeleteButton, ReportButton } from "./MessageButton";
|
||||
import ReviewBadge from "./ReviewBadge";
|
||||
|
||||
export default LazyComponent(() => {
|
||||
// this is terrible, blame mantika
|
||||
// this is terrible, blame ven
|
||||
const p = filters.byProps;
|
||||
const [
|
||||
{ cozyMessage, buttons, message, buttonsInner, groupStart },
|
||||
{ container, isHeader },
|
||||
{ avatar, clickable, username, messageContent, wrapper, cozy },
|
||||
{ avatar, clickable, username, wrapper, cozy },
|
||||
buttonClasses,
|
||||
botTag
|
||||
] = findBulk(
|
||||
@ -124,12 +124,10 @@ export default LazyComponent(() => {
|
||||
</Timestamp>)
|
||||
}
|
||||
|
||||
<p
|
||||
className={classes(messageContent)}
|
||||
style={{ fontSize: 15, marginTop: 4, color: "var(--text-normal)" }}
|
||||
>
|
||||
{review.comment}
|
||||
</p>
|
||||
<div className={cl("review-comment")}>
|
||||
{Parser.parseGuildEventDescription(review.comment)}
|
||||
</div>
|
||||
|
||||
{review.id !== 0 && (
|
||||
<div className={classes(container, isHeader, buttons)} style={{
|
||||
padding: "0px",
|
||||
|
@ -16,11 +16,9 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { classes } from "@utils/misc";
|
||||
import { useAwaiter, useForceUpdater } from "@utils/react";
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
import { Forms, React, RelationshipStore, UserStore } from "@webpack/common";
|
||||
import type { KeyboardEvent } from "react";
|
||||
import { LazyComponent, useAwaiter, useForceUpdater } from "@utils/react";
|
||||
import { find, findByPropsLazy } from "@webpack";
|
||||
import { Forms, React, RelationshipStore, useRef, UserStore } from "@webpack/common";
|
||||
|
||||
import { Review } from "../entities";
|
||||
import { addReview, getReviews, Response, REVIEWS_PER_PAGE } from "../reviewDbApi";
|
||||
@ -28,7 +26,12 @@ import { settings } from "../settings";
|
||||
import { authorize, cl, showToast } from "../utils";
|
||||
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 {
|
||||
discordId: string;
|
||||
@ -113,34 +116,38 @@ function ReviewList({ refetch, reviews, hideOwnReview }: { refetch(): void; revi
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
export function ReviewsInputComponent({ discordId, isAuthor, refetch, name }: { discordId: string, name: string; isAuthor: boolean; refetch(): void; }) {
|
||||
const { token } = settings.store;
|
||||
const editorRef = useRef<any>(null);
|
||||
const inputType = InputTypes.FORM;
|
||||
inputType.disableAutoFocus = true;
|
||||
|
||||
function onKeyPress({ key, target }: KeyboardEvent<HTMLTextAreaElement>) {
|
||||
if (key === "Enter") {
|
||||
addReview({
|
||||
userid: discordId,
|
||||
comment: (target as HTMLInputElement).value,
|
||||
star: -1
|
||||
}).then(res => {
|
||||
if (res?.success) {
|
||||
(target as HTMLInputElement).value = ""; // clear the input
|
||||
refetch();
|
||||
} else if (res?.message) {
|
||||
showToast(res.message);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
const channel = {
|
||||
flags_: 256,
|
||||
guild_id_: null,
|
||||
id: "0",
|
||||
getGuildId: () => null,
|
||||
isPrivate: () => true,
|
||||
isActiveThread: () => false,
|
||||
isArchivedLockedThread: () => false,
|
||||
isDM: () => true,
|
||||
roles: { "0": { permissions: 0n } },
|
||||
getRecipientId: () => "0",
|
||||
hasFlag: () => false,
|
||||
};
|
||||
|
||||
return (
|
||||
<textarea
|
||||
className={classes(Classes.inputDefault, "enter-comment", cl("input"))}
|
||||
onKeyDownCapture={e => {
|
||||
if (e.key === "Enter") {
|
||||
e.preventDefault(); // prevent newlines
|
||||
<>
|
||||
<div onClick={() => {
|
||||
if (!token) {
|
||||
showToast("Opening authorization window...");
|
||||
authorize();
|
||||
}
|
||||
}}
|
||||
}}>
|
||||
<InputComponent
|
||||
className={cl("input")}
|
||||
channel={channel}
|
||||
placeholder={
|
||||
!token
|
||||
? "You need to authorize to review users!"
|
||||
@ -148,13 +155,43 @@ export function ReviewsInputComponent({ discordId, isAuthor, refetch, name }: {
|
||||
? `Update review for @${name}`
|
||||
: `Review @${name}`
|
||||
}
|
||||
onKeyDown={onKeyPress}
|
||||
onClick={() => {
|
||||
if (!token) {
|
||||
showToast("Opening authorization window...");
|
||||
authorize();
|
||||
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
|
||||
}
|
||||
|
||||
export const enum NotificationType {
|
||||
Info = 0,
|
||||
Ban = 1,
|
||||
Unban = 2,
|
||||
Warning = 3
|
||||
}
|
||||
|
||||
export interface Badge {
|
||||
name: string;
|
||||
description: string;
|
||||
@ -45,6 +52,13 @@ export interface BanInfo {
|
||||
banEndDate: number;
|
||||
}
|
||||
|
||||
export interface Notification {
|
||||
id: number;
|
||||
title: string;
|
||||
content: string;
|
||||
type: NotificationType;
|
||||
}
|
||||
|
||||
export interface ReviewDBUser {
|
||||
ID: number;
|
||||
discordID: string;
|
||||
@ -54,6 +68,7 @@ export interface ReviewDBUser {
|
||||
warningCount: number;
|
||||
badges: any[];
|
||||
banInfo: BanInfo | null;
|
||||
notification: Notification | null;
|
||||
lastReviewID: number;
|
||||
type: UserType;
|
||||
}
|
||||
|
@ -24,13 +24,13 @@ import ExpandableHeader from "@components/ExpandableHeader";
|
||||
import { OpenExternalIcon } from "@components/Icons";
|
||||
import { Devs } from "@utils/constants";
|
||||
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 { openReviewsModal } from "./components/ReviewModal";
|
||||
import ReviewsView from "./components/ReviewsView";
|
||||
import { UserType } from "./entities";
|
||||
import { getCurrentUserInfo } from "./reviewDbApi";
|
||||
import { NotificationType } from "./entities";
|
||||
import { getCurrentUserInfo, readNotification } from "./reviewDbApi";
|
||||
import { settings } from "./settings";
|
||||
import { showToast } from "./utils";
|
||||
|
||||
@ -78,26 +78,8 @@ export default definePlugin({
|
||||
|
||||
addContextMenuPatch("guild-header-popout", guildPopoutPatch);
|
||||
|
||||
if (user.banInfo) {
|
||||
const endDate = new Date(user.banInfo.banEndDate);
|
||||
if (endDate.getTime() > Date.now() && (s.user?.banInfo?.banEndDate ?? 0) < endDate.getTime()) {
|
||||
Alerts.show({
|
||||
title: "You have been banned from ReviewDB",
|
||||
body: (
|
||||
<>
|
||||
<p>
|
||||
You are banned from ReviewDB {
|
||||
user.type === UserType.Banned
|
||||
? "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>
|
||||
</>
|
||||
),
|
||||
if (user.notification) {
|
||||
const props = user.notification.type === NotificationType.Ban ? {
|
||||
cancelText: "Appeal",
|
||||
confirmText: "Ok",
|
||||
onCancel: () =>
|
||||
@ -108,10 +90,21 @@ export default definePlugin({
|
||||
page: "dashboard/appeal"
|
||||
})
|
||||
)
|
||||
});
|
||||
}
|
||||
}
|
||||
} : {};
|
||||
|
||||
Alerts.show({
|
||||
title: user.notification.title,
|
||||
body: (
|
||||
Parser.parse(
|
||||
user.notification.content,
|
||||
false
|
||||
)
|
||||
),
|
||||
...props
|
||||
});
|
||||
|
||||
readNotification(user.notification.id);
|
||||
}
|
||||
s.user = user;
|
||||
}, 4000);
|
||||
},
|
||||
|
@ -140,3 +140,12 @@ export function getCurrentUserInfo(token: string): Promise<ReviewDBUser> {
|
||||
method: "POST",
|
||||
}).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;
|
||||
background: transparent;
|
||||
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 {
|
||||
@ -24,13 +33,12 @@
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.vc-rdb-modal-footer {
|
||||
padding: 0;
|
||||
.vc-rdb-input * {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.vc-rdb-modal-footer > div {
|
||||
width: 100%;
|
||||
margin: 6px 16px;
|
||||
.vc-rdb-modal-footer {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.vc-rdb-modal-footer .vc-rdb-input {
|
||||
@ -49,3 +57,20 @@
|
||||
.vc-rdb-modal-reviews {
|
||||
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)
|
||||
))
|
||||
)() as Record<string, Dev>;
|
||||
|
||||
export const IsFirefox = IS_EXTENSION && navigator.userAgent.toLowerCase().includes("firefox");
|
||||
|
Reference in New Issue
Block a user