diff --git a/docs/1_INSTALLING.md b/docs/1_INSTALLING.md index e234874d5..f27dfc14f 100644 --- a/docs/1_INSTALLING.md +++ b/docs/1_INSTALLING.md @@ -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 Welcome to Megu's Installation Guide! In this file, you will learn about how to download, install, and uninstall Vencord! diff --git a/src/components/PluginSettings/PluginModal.tsx b/src/components/PluginSettings/PluginModal.tsx index 592b6cb92..7331fffaf 100644 --- a/src/components/PluginSettings/PluginModal.tsx +++ b/src/components/PluginSettings/PluginModal.tsx @@ -214,7 +214,7 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti size={Button.Sizes.SMALL} color={Button.Colors.RED} > - Exit Without Saving + Cancel {({ onMouseEnter, onMouseLeave }) => ( @@ -226,7 +226,7 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti onMouseLeave={onMouseLeave} disabled={!canSubmit()} > - Save & Exit + Save & Close )} diff --git a/src/plugins/BetterNotes.ts b/src/plugins/BetterNotes.ts new file mode 100644 index 000000000..15282b6a0 --- /dev/null +++ b/src/plugins/BetterNotes.ts @@ -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 . +*/ + +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 + } + } +}); diff --git a/src/plugins/HideAttachments.tsx b/src/plugins/HideAttachments.tsx new file mode 100644 index 000000000..067b4f010 --- /dev/null +++ b/src/plugins/HideAttachments.tsx @@ -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 . +*/ + +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 = () => ( + +); +const ImageInvisible = () => ( + +); + +let hiddenMessages: Set = new Set(); +const getHiddenMessages = () => get(KEY).then(set => { + hiddenMessages = set ?? new Set(); + return hiddenMessages; +}); +const saveHiddenMessages = (ids: Set) => 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 + }); + } +}); diff --git a/src/plugins/apiCommands.ts b/src/plugins/apiCommands.ts index 7b08b8a8b..4136deb62 100644 --- a/src/plugins/apiCommands.ts +++ b/src/plugins/apiCommands.ts @@ -47,6 +47,15 @@ export default definePlugin({ match: /,(.{1,2})\.execute\((.{1,2}),(.{1,2})\)]/, 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||($&)" + } } ], }); diff --git a/src/plugins/dictionary.ts b/src/plugins/dictionary.ts new file mode 100644 index 000000000..547636d00 --- /dev/null +++ b/src/plugins/dictionary.ts @@ -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 . +*/ + +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}\`` + }); + } + } + } + ] +}); diff --git a/src/plugins/keepCurrentChannel.ts b/src/plugins/keepCurrentChannel.ts new file mode 100644 index 000000000..395fd7689 --- /dev/null +++ b/src/plugins/keepCurrentChannel.ts @@ -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 . +*/ + +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("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); + } +}); diff --git a/src/plugins/loadingQuotes.ts b/src/plugins/loadingQuotes.ts index c70011955..7ee55d8ef 100644 --- a/src/plugins/loadingQuotes.ts +++ b/src/plugins/loadingQuotes.ts @@ -41,13 +41,28 @@ const quotes = [ "mdmt", "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', - "Tscmw%Tnoa~x" + "Tscmw%Tnoa~x", + "I‘f#npus(ec`e!vl$lhsm{`ncu\"ekw&f(defeov-$Rnf|)sdu‘pf$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({ name: "LoadingQuotes", description: "Replace Discords loading quotes", - authors: [Devs.Ven], + authors: [Devs.Ven, Devs.KraXen72], patches: [ { find: ".LOADING_DID_YOU_KNOW", diff --git a/src/plugins/messageTags.ts b/src/plugins/messageTags.ts new file mode 100644 index 000000000..bf01cad26 --- /dev/null +++ b/src/plugins/messageTags.ts @@ -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 . +*/ + +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(t => t ?? []); +const getTag = (name: string) => DataStore.get(DATA_KEY).then((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" + }); + } + } + ] +}); diff --git a/src/plugins/platformIndicators.tsx b/src/plugins/platformIndicators.tsx index 2917ba890..1c9bbf170 100644 --- a/src/plugins/platformIndicators.tsx +++ b/src/plugins/platformIndicators.tsx @@ -58,7 +58,7 @@ const PlatformIcon = ({ platform, status }: { platform: Platform, status: string const tooltip = platform[0].toUpperCase() + platform.slice(1); const Icon = Icons[platform] ?? Icons.desktop; - return ; + return ; }; const PlatformIndicator = ({ user }: { user: User; }) => { diff --git a/src/plugins/reviewDB/components/ReviewComponent.tsx b/src/plugins/reviewDB/components/ReviewComponent.tsx index bde04e54d..0ad8ca7e6 100644 --- a/src/plugins/reviewDB/components/ReviewComponent.tsx +++ b/src/plugins/reviewDB/components/ReviewComponent.tsx @@ -72,13 +72,13 @@ export default LazyComponent(() => { body: "Do you really you want to report this review?", confirmText: "Report", cancelText: "Nevermind", - confirmColor: "red", + // confirmColor: "red", this just adds a class name and breaks the submit button guh onConfirm: () => reportReview(review.id) }); } return ( -
{
{ {review.badges.map(badge => )}

{review.comment}

-
-
+
+
{canDeleteReview(review, UserStore.getCurrentUser().id) && ( diff --git a/src/plugins/reviewDB/components/ReviewsView.tsx b/src/plugins/reviewDB/components/ReviewsView.tsx index 17a5dcbc0..e1ab4db1d 100644 --- a/src/plugins/reviewDB/components/ReviewsView.tsx +++ b/src/plugins/reviewDB/components/ReviewsView.tsx @@ -18,8 +18,8 @@ import type { KeyboardEvent } from "react"; -import { lazyWebpack, useAwaiter } from "../../../utils/misc"; -import { Forms, Text } from "../../../webpack/common"; +import { classes, lazyWebpack, useAwaiter } from "../../../utils/misc"; +import { Forms, Text, UserStore } from "../../../webpack/common"; import { addReview, getReviews } from "../Utils/ReviewDBAPI"; import ReviewComponent from "./ReviewComponent"; @@ -46,7 +46,7 @@ export default function ReviewsView({ userId }: { userId: string; }) { } return ( - <> +
)}