This commit is contained in:
Manti 2022-11-25 11:01:33 +03:00
commit 7f65345d3e
16 changed files with 742 additions and 17 deletions

View file

@ -1,3 +1,6 @@
> **Warning**
> These instructions are only for advanced users. If you're not a Developer, you should use our [graphical installer](https://github.com/Vendicated/VencordInstaller#usage) instead.
# Installation Guide # Installation Guide
Welcome to Megu's Installation Guide! In this file, you will learn about how to download, install, and uninstall Vencord! Welcome to Megu's Installation Guide! In this file, you will learn about how to download, install, and uninstall Vencord!

View file

@ -214,7 +214,7 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti
size={Button.Sizes.SMALL} size={Button.Sizes.SMALL}
color={Button.Colors.RED} color={Button.Colors.RED}
> >
Exit Without Saving Cancel
</Button> </Button>
<Tooltip text="You must fix all errors before saving" shouldShow={!canSubmit()}> <Tooltip text="You must fix all errors before saving" shouldShow={!canSubmit()}>
{({ onMouseEnter, onMouseLeave }) => ( {({ onMouseEnter, onMouseLeave }) => (
@ -226,7 +226,7 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti
onMouseLeave={onMouseLeave} onMouseLeave={onMouseLeave}
disabled={!canSubmit()} disabled={!canSubmit()}
> >
Save & Exit Save & Close
</Button> </Button>
)} )}
</Tooltip> </Tooltip>

View file

@ -0,0 +1,61 @@
/*
* Vencord, a modification for Discord's desktop app
* Copyright (c) 2022 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 { Settings } from "../api/settings";
import { Devs } from "../utils/constants";
import { makeLazy } from "../utils/misc";
import definePlugin, { OptionType } from "../utils/types";
export default definePlugin({
name: "BetterNotesBox",
description: "Hide notes or disable spellcheck (Configure in settings!!)",
authors: [Devs.Ven],
patches: [
{
find: "hideNote:",
all: true,
predicate: makeLazy(() => Vencord.Settings.plugins.BetterNotesBox.hide),
replacement: {
match: /hideNote:.+?(?=[,}])/g,
replace: "hideNote:true",
}
}, {
find: "Messages.NOTE_PLACEHOLDER",
replacement: {
match: /\.NOTE_PLACEHOLDER,/,
replace: "$&spellCheck:!Vencord.Settings.plugins.BetterNotesBox.noSpellCheck,"
}
}
],
options: {
hide: {
type: OptionType.BOOLEAN,
description: "Hide notes",
default: false,
restartNeeded: true
},
noSpellCheck: {
type: OptionType.BOOLEAN,
description: "Disable spellcheck in notes",
disabled: () => Settings.plugins.BetterNotesBox.hide,
default: false
}
}
});

View file

@ -0,0 +1,124 @@
/*
* Vencord, a modification for Discord's desktop app
* Copyright (c) 2022 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 { Message } from "discord-types/general";
import { get, set } from "../api/DataStore";
import { Devs } from "../utils/constants";
import Logger from "../utils/Logger";
import definePlugin from "../utils/types";
import { ChannelStore, FluxDispatcher } from "../webpack/common";
let style: HTMLStyleElement;
const KEY = "HideAttachments_HiddenIds";
const ImageVisible = () => (
<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
<path d="M5 21q-.825 0-1.413-.587Q3 19.825 3 19V5q0-.825.587-1.413Q4.175 3 5 3h14q.825 0 1.413.587Q21 4.175 21 5v14q0 .825-.587 1.413Q19.825 21 19 21Zm0-2h14V5H5v14Zm1-2h12l-3.75-5-3 4L9 13Zm-1 2V5v14Z" />
</svg>
);
const ImageInvisible = () => (
<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
<path d="m21 18.15-2-2V5H7.85l-2-2H19q.825 0 1.413.587Q21 4.175 21 5Zm-1.2 4.45L18.2 21H5q-.825 0-1.413-.587Q3 19.825 3 19V5.8L1.4 4.2l1.4-1.4 18.4 18.4ZM6 17l3-4 2.25 3 .825-1.1L5 7.825V19h11.175l-2-2Zm7.425-6.425ZM10.6 13.4Z" />
</svg>
);
let hiddenMessages: Set<string> = new Set();
const getHiddenMessages = () => get(KEY).then(set => {
hiddenMessages = set ?? new Set<string>();
return hiddenMessages;
});
const saveHiddenMessages = (ids: Set<string>) => set(KEY, ids);
export default definePlugin({
name: "HideAttachments",
description: "Hide attachments and Embeds for individual messages via hover button",
authors: [Devs.Ven],
patches: [{
find: "Messages.MESSAGE_UTILITIES_A11Y_LABEL",
replacement: {
match: /(message:(.).{0,100}Fragment,\{children:\[)(.{0,40}renderPopout:.{0,200}message_reaction_emoji_picker.+?return (.{1,3})\(.{0,30}"add-reaction")/,
replace: "$1Vencord.Plugins.plugins.HideAttachments.renderButton($2, $4),$3"
}
}],
async start() {
style = document.createElement("style");
style.id = "VencordHideAttachments";
document.head.appendChild(style);
await getHiddenMessages();
await this.buildCss();
},
stop() {
style.remove();
hiddenMessages.clear();
},
async buildCss() {
const elements = [...hiddenMessages].map(id => `#message-accessories-${id}`).join(",");
style.textContent = `
:is(${elements}) [class*="embedWrapper"] {
/* important is not necessary, but add it to make sure bad themes won't break it */
display: none !important;
}
:is(${elements})::after {
content: "Attachments hidden";
color: var(--text-muted);
font-size: 80%;
}
`;
},
renderButton(msg: Message, makeItem: (data: any) => React.ComponentType) {
try {
if (!msg.attachments.length && !msg.embeds.length) return null;
const isHidden = hiddenMessages.has(msg.id);
return makeItem({
key: "HideAttachments",
label: isHidden ? "Show Attachments" : "Hide Attachments",
icon: isHidden ? ImageVisible : ImageInvisible,
message: msg,
channel: ChannelStore.getChannel(msg.channel_id),
onClick: () => this.toggleHide(msg)
});
} catch (err) {
new Logger("HideAttachments").error(err);
return null;
}
},
async toggleHide(message: Message) {
const ids = await getHiddenMessages();
if (!ids.delete(message.id))
ids.add(message.id);
await saveHiddenMessages(ids);
await this.buildCss();
// update is necessary to rerender the PopOver
FluxDispatcher.dispatch({
type: "MESSAGE_UPDATE",
message
});
}
});

View file

@ -47,6 +47,15 @@ export default definePlugin({
match: /,(.{1,2})\.execute\((.{1,2}),(.{1,2})\)]/, match: /,(.{1,2})\.execute\((.{1,2}),(.{1,2})\)]/,
replace: (_, cmd, args, ctx) => `,Vencord.Api.Commands._handleCommand(${cmd}, ${args}, ${ctx})]` replace: (_, cmd, args, ctx) => `,Vencord.Api.Commands._handleCommand(${cmd}, ${args}, ${ctx})]`
} }
},
// Show plugin name instead of "Built-In"
{
find: "().source,children",
replacement: {
// ...children: p?.name
match: /(?<=:(.{1,3})\.displayDescription\}.{0,200}\(\)\.source,children:)[^}]+/,
replace: "$1.plugin||($&)"
}
} }
], ],
}); });

80
src/plugins/dictionary.ts Normal file
View file

@ -0,0 +1,80 @@
/*
* Vencord, a modification for Discord's desktop app
* Copyright (c) 2022 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 { ApplicationCommandOptionType, sendBotMessage } from "../api/Commands";
import { ApplicationCommandInputType } from "../api/Commands/types";
import { Devs } from "../utils/constants";
import definePlugin from "../utils/types";
export default definePlugin({
name: "UrbanDictionary",
description: "Searches for a word on Urban Dictionary",
authors: [Devs.jewdev],
dependencies: ["CommandsAPI"],
commands: [
{
name: "urban",
description: "Returns the definition of a word from Urban Dictionary",
inputType: ApplicationCommandInputType.BUILT_IN,
options: [
{
type: ApplicationCommandOptionType.STRING,
name: "word",
description: "The word to search for on Urban Dictionary",
required: true
}
],
execute: async (args, ctx) => {
try {
const { list: [definition] } = await (await fetch(`https://api.urbandictionary.com/v0/define?term=${args[0].value}`)).json();
if (!definition)
return void sendBotMessage(ctx.channel.id, { content: "No results found." });
const linkify = text => text.replace(/\[(.+?)\]/g, (_, word) => `[${word}](https://www.urbandictionary.com/define.php?term=${encodeURIComponent(word)})`);
return void sendBotMessage(ctx.channel.id, {
embeds: [
{
type: "rich",
author: {
name: `Definition of ${definition.word}`,
url: definition.permalink
},
description: linkify(definition.definition),
fields: [
{
name: "Example",
value: linkify(definition.example)
}
],
color: 0xFF9900,
footer: { text: `👍 ${definition.thumbs_up.toString()} | 👎 ${definition.thumbs_down.toString()} | Uploaded by ${definition.author}`, icon_url: "https://www.urbandictionary.com/favicon.ico" },
timestamp: new Date(definition.written_on).toISOString()
}
] as any
});
} catch (error) {
return void sendBotMessage(ctx.channel.id, {
content: `Something went wrong: \`${error}\``
});
}
}
}
]
});

View file

@ -0,0 +1,99 @@
/*
* Vencord, a modification for Discord's desktop app
* Copyright (c) 2022 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 { DataStore } from "../api";
import { Devs } from "../utils/constants";
import definePlugin from "../utils/types";
import { ChannelStore, FluxDispatcher, NavigationRouter, SelectedChannelStore, SelectedGuildStore } from "../webpack/common";
export interface LogoutEvent {
type: "LOGOUT";
isSwitchingAccount: boolean;
}
interface ChannelSelectEvent {
type: "CHANNEL_SELECT";
channelId: string | null;
guildId: string | null;
}
interface PreviousChannel {
guildId: string | null;
channelId: string | null;
}
export default definePlugin({
name: "KeepCurrentChannel",
description: "Attempt to navigate the channel you were in before switching accounts or loading Discord.",
authors: [Devs.Nuckyz],
isSwitchingAccount: false,
previousCache: {} as PreviousChannel,
attemptToNavigateToChannel(guildId: string | null, channelId: string) {
if (!ChannelStore.hasChannel(channelId)) return;
NavigationRouter.transitionTo(`/channels/${guildId ?? "@me"}/${channelId}`);
},
onLogout(e: LogoutEvent) {
this.isSwitchingAccount = e.isSwitchingAccount;
},
onConnectionOpen() {
if (!this.isSwitchingAccount) return;
this.isSwitchingAccount = false;
if (this.previousCache.channelId) this.attemptToNavigateToChannel(this.previousCache.guildId, this.previousCache.channelId);
},
async onChannelSelect({ guildId, channelId }: ChannelSelectEvent) {
if (this.isSwitchingAccount) return;
this.previousCache = {
guildId,
channelId
};
await DataStore.set("KeepCurrentChannel_previousData", this.previousCache);
},
async start() {
const previousData = await DataStore.get<PreviousChannel>("KeepCurrentChannel_previousData");
if (previousData) {
this.previousCache = previousData;
if (this.previousCache.channelId) this.attemptToNavigateToChannel(this.previousCache.guildId, this.previousCache.channelId);
} else {
this.previousCache = {
guildId: SelectedGuildStore.getGuildId(),
channelId: SelectedChannelStore.getChannelId() ?? null
};
await DataStore.set("KeepCurrentChannel_previousData", this.previousCache);
}
FluxDispatcher.subscribe("LOGOUT", this.onLogout.bind(this));
FluxDispatcher.subscribe("CONNECTION_OPEN", this.onConnectionOpen.bind(this));
FluxDispatcher.subscribe("CHANNEL_SELECT", this.onChannelSelect.bind(this));
},
stop() {
FluxDispatcher.unsubscribe("LOGOUT", this.onLogout);
FluxDispatcher.unsubscribe("CONNECTION_OPEN", this.onConnectionOpen);
FluxDispatcher.unsubscribe("CHANNEL_SELECT", this.onChannelSelect);
}
});

View file

@ -41,13 +41,28 @@ const quotes = [
"mdmt", "mdmt",
"Wdn`khc+(oxbeof", "Wdn`khc+(oxbeof",
'Ig"zkp*\'g{*xolglj`&~g|*gowg/$mgt(Eclm`.#ticf{l*xed"wl`&Kangj igbhqn\'d`dn `v#lqrw{3%$bhv-h|)kangj_imwhlhb', 'Ig"zkp*\'g{*xolglj`&~g|*gowg/$mgt(Eclm`.#ticf{l*xed"wl`&Kangj igbhqn\'d`dn `v#lqrw{3%$bhv-h|)kangj_imwhlhb',
"Tscmw%Tnoa~x" "Tscmw%Tnoa~x",
"If#npus(ec`e!vl$lhsm{`ncu\"ekw&f(defeov-$Rnf|)sdupf$wcam{ceg!vl$du'D`d~x-\"jw%oi(okht-\"DJP)Kags,!mq$du'A|n sg`akrkq)~jkdl#pj&diefbnf\"jp)&@F\\*{ltq#Hlhrp'",
"Ynw$v`&cg`dl fml`%rhlhs*",
"Dnl$p%qhz{s' hv$w%hh|aceg!;#gpvt(fl+cndea`&dg|fon&v#wjjqm(",
"\ud83d)pft`gs(ec`e!13$qojmz#",
"a!njcmr'ide~nu\"lb%rheoedldpz$lu'gbkr",
"dn\"zkp&kgo4",
"hnpqkw",
"sn\"fau",
"Sn\"tmqnh}}*musvkaw&flf&+ldv$w%lr{}*aulr#vlao|)cetn\"jp$",
"Dxkmc%ot(hhxomwwai'{hln",
"hd{#}js&(pe~'sg#gprb(3#\"",
"hd{b${",
"<;vqkijbq33271:56<3799?24944:",
"Thof$lu'ofdn,!qsefc'az*bnrcma+&Om{o+iu\"`khct$)bnrd\"bcdoi&",
"snofplkb{)c'r\"lod'|f*aurv#cpno`abchijklmno"
]; ];
export default definePlugin({ export default definePlugin({
name: "LoadingQuotes", name: "LoadingQuotes",
description: "Replace Discords loading quotes", description: "Replace Discords loading quotes",
authors: [Devs.Ven], authors: [Devs.Ven, Devs.KraXen72],
patches: [ patches: [
{ {
find: ".LOADING_DID_YOU_KNOW", find: ".LOADING_DID_YOU_KNOW",

246
src/plugins/messageTags.ts Normal file
View file

@ -0,0 +1,246 @@
/*
* Vencord, a modification for Discord's desktop app
* Copyright (c) 2022 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 { DataStore } from "../api";
import { ApplicationCommandInputType, ApplicationCommandOptionType, findOption, registerCommand, sendBotMessage, unregisterCommand } from "../api/Commands";
import { Settings } from "../api/settings";
import { Devs } from "../utils/constants";
import definePlugin, { OptionType } from "../utils/types";
const EMOTE = "<:luna:1035316192220553236>";
const DATA_KEY = "MessageTags_TAGS";
const MessageTagsMarker = Symbol("MessageTags");
const author = {
id: "821472922140803112",
bot: false
};
interface Tag {
name: string;
message: string;
enabled: boolean;
}
const getTags = () => DataStore.get(DATA_KEY).then<Tag[]>(t => t ?? []);
const getTag = (name: string) => DataStore.get(DATA_KEY).then<Tag | null>((t: Tag[]) => (t ?? []).find((tt: Tag) => tt.name === name) ?? null);
const addTag = async (tag: Tag) => {
const tags = await getTags();
tags.push(tag);
DataStore.set(DATA_KEY, tags);
return tags;
};
const removeTag = async (name: string) => {
let tags = await getTags();
tags = await tags.filter((t: Tag) => t.name !== name);
DataStore.set(DATA_KEY, tags);
return tags;
};
function createTagCommand(tag: Tag) {
registerCommand({
name: tag.name,
description: tag.name,
inputType: ApplicationCommandInputType.BUILT_IN_TEXT,
execute: async (_, ctx) => {
if (!await getTag(tag.name)) {
sendBotMessage(ctx.channel.id, {
author,
content: `${EMOTE} The tag **${tag.name}** does not exist anymore! Please reload ur Discord to fix :)`
});
return { content: `/${tag.name}` };
}
if (Settings.plugins.MessageTags.clyde) sendBotMessage(ctx.channel.id, {
author,
content: `${EMOTE} The tag **${tag.name}** has been sent!`
});
return { content: tag.message.replaceAll("\\n", "\n") };
},
[MessageTagsMarker]: true,
}, "CustomTags");
}
export default definePlugin({
name: "MessageTags",
description: "Allows you to save messages and to use them with a simple command.",
authors: [Devs.Luna],
options: {
clyde: {
name: "Clyde message on send",
description: "If enabled, clyde will send you an ephemeral message when a tag was used.",
type: OptionType.BOOLEAN,
default: true
}
},
dependencies: ["CommandsAPI"],
async start() {
for (const tag of await getTags()) createTagCommand(tag);
},
commands: [
{
name: "tags",
description: "Manage all the tags for yourself",
inputType: ApplicationCommandInputType.BUILT_IN,
options: [
{
name: "create",
description: "Create a new tag",
type: ApplicationCommandOptionType.SUB_COMMAND,
options: [
{
name: "tag-name",
description: "The name of the tag to trigger the response",
type: ApplicationCommandOptionType.STRING,
required: true
},
{
name: "message",
description: "The message that you will send when using this tag",
type: ApplicationCommandOptionType.STRING,
required: true
}
]
},
{
name: "list",
description: "List all tags from yourself",
type: ApplicationCommandOptionType.SUB_COMMAND,
options: []
},
{
name: "delete",
description: "Remove a tag from your yourself",
type: ApplicationCommandOptionType.SUB_COMMAND,
options: [
{
name: "tag-name",
description: "The name of the tag to trigger the response",
type: ApplicationCommandOptionType.STRING,
required: true
}
]
},
{
name: "preview",
description: "Preview a tag without sending it publicly",
type: ApplicationCommandOptionType.SUB_COMMAND,
options: [
{
name: "tag-name",
description: "The name of the tag to trigger the response",
type: ApplicationCommandOptionType.STRING,
required: true
}
]
}
],
async execute(args, ctx) {
switch (args[0].name) {
case "create": {
const name: string = findOption(args[0].options, "tag-name", "");
const message: string = findOption(args[0].options, "message", "");
if (await getTag(name))
return sendBotMessage(ctx.channel.id, {
author,
content: `${EMOTE} A Tag with the name **${name}** already exists!`
});
const tag = {
name: name,
enabled: true,
message: message
};
createTagCommand(tag);
await addTag(tag);
sendBotMessage(ctx.channel.id, {
author,
content: `${EMOTE} Successfully created the tag **${name}**!`
});
break; // end 'create'
}
case "delete": {
const name: string = findOption(args[0].options, "tag-name", "");
if (!await getTag(name))
return sendBotMessage(ctx.channel.id, {
author,
content: `${EMOTE} A Tag with the name **${name}** does not exist!`
});
unregisterCommand(name);
await removeTag(name);
sendBotMessage(ctx.channel.id, {
author,
content: `${EMOTE} Successfully deleted the tag **${name}**!`
});
break; // end 'delete'
}
case "list": {
sendBotMessage(ctx.channel.id, {
author,
embeds: [
{
// @ts-ignore
title: "All Tags:",
// @ts-ignore
description: (await getTags())
.map(tag => `\`${tag.name}\`: ${tag.message.slice(0, 72).replaceAll("\\n", " ")}${tag.message.length > 72 ? "..." : ""}`)
.join("\n") || `${EMOTE} Woops! There are no tags yet, use \`/tags create\` to create one!`,
// @ts-ignore
color: 0xd77f7f,
type: "rich",
}
]
});
break; // end 'list'
}
case "preview": {
const name: string = findOption(args[0].options, "tag-name", "");
const tag = await getTag(name);
if (!tag)
return sendBotMessage(ctx.channel.id, {
author,
content: `${EMOTE} A Tag with the name **${name}** does not exist!`
});
sendBotMessage(ctx.channel.id, {
author,
content: tag.message.replaceAll("\\n", "\n")
});
break; // end 'preview'
}
}
return sendBotMessage(ctx.channel.id, {
author,
content: "Invalid sub-command"
});
}
}
]
});

View file

@ -58,7 +58,7 @@ const PlatformIcon = ({ platform, status }: { platform: Platform, status: string
const tooltip = platform[0].toUpperCase() + platform.slice(1); const tooltip = platform[0].toUpperCase() + platform.slice(1);
const Icon = Icons[platform] ?? Icons.desktop; const Icon = Icons[platform] ?? Icons.desktop;
return <Icon color={getStatusColor(status)} tooltip={tooltip} />; return <Icon color={`var(--${getStatusColor(status)}`} tooltip={tooltip} />;
}; };
const PlatformIndicator = ({ user }: { user: User; }) => { const PlatformIndicator = ({ user }: { user: User; }) => {

View file

@ -72,13 +72,13 @@ export default LazyComponent(() => {
body: "Do you really you want to report this review?", body: "Do you really you want to report this review?",
confirmText: "Report", confirmText: "Report",
cancelText: "Nevermind", cancelText: "Nevermind",
confirmColor: "red", // confirmColor: "red", this just adds a class name and breaks the submit button guh
onConfirm: () => reportReview(review.id) onConfirm: () => reportReview(review.id)
}); });
} }
return ( return (
<div className={classes(cozyMessage, wrapper)} style={ <div className={classes(cozyMessage, wrapper,message, groupStart, cozy, "user-review")} style={
{ {
marginLeft: "0px", marginLeft: "0px",
paddingLeft: "52px", paddingLeft: "52px",
@ -89,6 +89,7 @@ export default LazyComponent(() => {
<div className={contents} style={{ paddingLeft: "0px" }}> <div className={contents} style={{ paddingLeft: "0px" }}>
<img <img
className={classes(avatar, clickable)} className={classes(avatar, clickable)}
style={{ left: "8px" }}
onClick={openModal} onClick={openModal}
src={review.profile_photo || "/assets/1f0bfc0865d324c2587920a7d80c609b.png?size=128"} src={review.profile_photo || "/assets/1f0bfc0865d324c2587920a7d80c609b.png?size=128"}
style={{ left: "0px" }} style={{ left: "0px" }}
@ -103,12 +104,14 @@ export default LazyComponent(() => {
{review.badges.map(badge => <ReviewBadge {...badge} />)} {review.badges.map(badge => <ReviewBadge {...badge} />)}
<p <p
className={classes(messageContent, defaultColor)} className={classes(messageContent, defaultColor)}
style={{ fontSize: 15, marginTop: 4, marginRight: "0px" }} style={{ fontSize: 15, marginTop: 4 }}
> >
{review.comment} {review.comment}
</p> </p>
<div className={classes(container, isHeader, buttons)}> <div className={classes(container, isHeader, buttons)} style={{
<div className={buttonClasses.wrapper}> padding: "0px",
}}>
<div className={buttonClasses.wrapper} >
<MessageButton type="report" callback={reportRev} /> <MessageButton type="report" callback={reportRev} />
{canDeleteReview(review, UserStore.getCurrentUser().id) && ( {canDeleteReview(review, UserStore.getCurrentUser().id) && (
<MessageButton type="delete" callback={delReview} /> <MessageButton type="delete" callback={delReview} />

View file

@ -18,8 +18,8 @@
import type { KeyboardEvent } from "react"; import type { KeyboardEvent } from "react";
import { lazyWebpack, useAwaiter } from "../../../utils/misc"; import { classes, lazyWebpack, useAwaiter } from "../../../utils/misc";
import { Forms, Text } from "../../../webpack/common"; import { Forms, Text, UserStore } from "../../../webpack/common";
import { addReview, getReviews } from "../Utils/ReviewDBAPI"; import { addReview, getReviews } from "../Utils/ReviewDBAPI";
import ReviewComponent from "./ReviewComponent"; import ReviewComponent from "./ReviewComponent";
@ -46,7 +46,7 @@ export default function ReviewsView({ userId }: { userId: string; }) {
} }
return ( return (
<> <div className="ReviewDB">
<Text <Text
tag="h2" tag="h2"
variant="eyebrow" variant="eyebrow"
@ -70,16 +70,24 @@ export default function ReviewsView({ userId }: { userId: string; }) {
</Forms.FormText> </Forms.FormText>
)} )}
<textarea <textarea
className={Classes.textarea.replace("textarea", "")} className={classes(Classes.textarea.replace("textarea", ""),"enter-comment")}
// this produces something like '-_59yqs ...' but since no class exists with that name its fine // this produces something like '-_59yqs ...' but since no class exists with that name its fine
placeholder="Enter a comment" placeholder= {"Review @" + UserStore.getUser(userId)?.username ?? ""}
onKeyDown={onKeyPress} onKeyDown={onKeyPress}
style={{ style={{
marginTop: "6px", marginTop: "6px",
resize: "none", resize: "none",
marginBottom: "12px", marginBottom: "12px",
padding: "12px",
marginBottom: "12px",
color: "var(--text-normal)",
border: "1px solid var(--profile-message-input-border-color)",
fontSize: "14px",
borderRadius: "3px",
}} }}
/> />
</> <div/>
); );
} }

View file

@ -0,0 +1,60 @@
/*
* Vencord, a modification for Discord's desktop app
* Copyright (c) 2022 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 { makeRange } from "../components/PluginSettings/components/SettingSliderComponent";
import { Devs } from "../utils/constants";
import definePlugin, { OptionType } from "../utils/types";
export default definePlugin({
name: "VolumeBooster",
authors: [Devs.Nuckyz],
description: "Allows you to set the user and stream volume above the default maximum.",
patches: [
{
find: ".Messages.USER_VOLUME",
replacement: {
match: /maxValue:(.{1,2}\..{1,2})\?(\d+?):(\d+?),/,
replace: (_, defaultMaxVolumePredicate, higherMaxVolume, minorMaxVolume) => ""
+ `maxValue:${defaultMaxVolumePredicate}`
+ `?${higherMaxVolume}*Vencord.Settings.plugins.VolumeBooster.multiplier`
+ `:${minorMaxVolume}*Vencord.Settings.plugins.VolumeBooster.multiplier,`
}
},
{
find: "currentVolume:",
replacement: {
match: /maxValue:(.{1,2}\..{1,2})\?(\d+?):(\d+?),/,
replace: (_, defaultMaxVolumePredicate, higherMaxVolume, minorMaxVolume) => ""
+ `maxValue:${defaultMaxVolumePredicate}`
+ `?${higherMaxVolume}*Vencord.Settings.plugins.VolumeBooster.multiplier`
+ `:${minorMaxVolume}*Vencord.Settings.plugins.VolumeBooster.multiplier,`
}
}
],
options: {
multiplier: {
description: "Volume Multiplier",
type: OptionType.SLIDER,
markers: makeRange(1, 5, 1),
default: 2,
stickToMarkers: true,
}
}
});

View file

@ -148,5 +148,13 @@ export const Devs = Object.freeze({
Ducko: { Ducko: {
name: "Ducko", name: "Ducko",
id: 506482395269169153n id: 506482395269169153n
},
jewdev: {
name: "jewdev",
id: 222369866529636353n
},
Luna: {
name: "Luny",
id: 821472922140803112n
} }
}); });

View file

@ -28,7 +28,7 @@ export async function toggle(isEnabled: boolean) {
style.id = "vencord-custom-css"; style.id = "vencord-custom-css";
document.head.appendChild(style); document.head.appendChild(style);
VencordNative.ipc.on(IpcEvents.QUICK_CSS_UPDATE, (_, css: string) => style.innerText = css); VencordNative.ipc.on(IpcEvents.QUICK_CSS_UPDATE, (_, css: string) => style.innerText = css);
style.innerText = await VencordNative.ipc.invoke(IpcEvents.GET_QUICK_CSS); style.textContent = await VencordNative.ipc.invoke(IpcEvents.GET_QUICK_CSS);
} }
} else // @ts-ignore yes typescript, property 'disabled' does exist on type 'HTMLStyleElement' u should try reading the docs some time } else // @ts-ignore yes typescript, property 'disabled' does exist on type 'HTMLStyleElement' u should try reading the docs some time
style.disabled = !isEnabled; style.disabled = !isEnabled;

View file

@ -41,6 +41,7 @@ export const PresenceStore = lazyWebpack(filters.byProps("setCurrentUserOnConnec
export let GuildStore: Stores.GuildStore; export let GuildStore: Stores.GuildStore;
export let UserStore: Stores.UserStore; export let UserStore: Stores.UserStore;
export let SelectedChannelStore: Stores.SelectedChannelStore; export let SelectedChannelStore: Stores.SelectedChannelStore;
export let SelectedGuildStore: any;
export let ChannelStore: Stores.ChannelStore; export let ChannelStore: Stores.ChannelStore;
export const Forms = {} as { export const Forms = {} as {
@ -125,6 +126,13 @@ export const Clipboard = mapMangledModuleLazy('document.queryCommandEnabled("cop
SUPPORTS_COPY: x => typeof x === "boolean", SUPPORTS_COPY: x => typeof x === "boolean",
}); });
export const NavigationRouter = mapMangledModuleLazy("Transitioning to external path", {
transitionTo: filters.byCode("Transitioning to external path"),
transitionToGuild: filters.byCode("transitionToGuild"),
goBack: filters.byCode("goBack()"),
goForward: filters.byCode("goForward()"),
});
waitFor("useState", m => React = m); waitFor("useState", m => React = m);
waitFor(["dispatch", "subscribe"], m => { waitFor(["dispatch", "subscribe"], m => {
@ -139,6 +147,7 @@ waitFor(["dispatch", "subscribe"], m => {
waitFor(["getCurrentUser", "initialize"], m => UserStore = m); waitFor(["getCurrentUser", "initialize"], m => UserStore = m);
waitFor("getSortedPrivateChannels", m => ChannelStore = m); waitFor("getSortedPrivateChannels", m => ChannelStore = m);
waitFor("getCurrentlySelectedChannelId", m => SelectedChannelStore = m); waitFor("getCurrentlySelectedChannelId", m => SelectedChannelStore = m);
waitFor("getLastSelectedGuildId", m => SelectedGuildStore = m);
waitFor("getGuildCount", m => GuildStore = m); waitFor("getGuildCount", m => GuildStore = m);
waitFor(["Hovers", "Looks", "Sizes"], m => Button = m); waitFor(["Hovers", "Looks", "Sizes"], m => Button = m);