diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6a1c8ee8..5b637d6a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,7 +16,7 @@ permissions: write-all jobs: Build: name: Build Equicord - runs-on: self-hosted + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -39,7 +39,7 @@ jobs: run: pnpm buildStandalone - name: Generate plugin list - run: pnpm generatePluginJson dist/vencordplugins.json + run: pnpm generatePluginJson dist/plugins.json - name: Generate Equicord plugin list run: pnpm generateEquicordPluginJson dist/equicordplugins.json @@ -66,15 +66,6 @@ jobs: rm release/package.json rm release/*.map - - name: get-npm-version - id: package-version - uses: martinbeentjes/npm-get-version-action@v1.3.1 - - - name: Upload Equicord Tagged - if: startsWith(github.ref, 'refs/tags/') - run: | - gh release upload v${{ steps.package-version.outputs.current-version}} --clobber dist/release/* - - name: Upload Equicord Stable if: ${{ github.ref_name == 'main' }} run: | diff --git a/.github/workflows/nixosBuild.yml b/.github/workflows/nixosBuild.yml new file mode 100644 index 00000000..1be05c75 --- /dev/null +++ b/.github/workflows/nixosBuild.yml @@ -0,0 +1,72 @@ +name: NixOS Build +on: + workflow_dispatch: + schedule: + - cron: 0 0 * * * +env: + FORCE_COLOR: true + GITHUB_TOKEN: ${{ secrets.ETOKEN }} + +permissions: write-all + +jobs: + Build: + name: Build Equicord + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v3 + + - name: Use Node.js 20 + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: "pnpm" + + - name: Install dependencies + run: pnpm install --no-frozen-lockfile + + - name: Build web + run: pnpm buildWebStandalone + + - name: Build + run: pnpm buildStandalone + + - name: Generate plugin list + run: pnpm generatePluginJson dist/vencordplugins.json + + - name: Generate Equicord plugin list + run: pnpm generateEquicordPluginJson dist/equicordplugins.json + + - name: Collect files to be released + run: | + cd dist + mkdir release + + cp browser/browser.* release + cp Vencord.user.{js,js.LEGAL.txt} release + + # copy the plugin data jsons, the extension zips and the desktop/vesktop asars + cp *.{json,zip,asar} release + + # legacy un-asared files + cp desktop/* release + for file in equibop/*; do + filename=$(basename "$file") + cp "$file" "release/equibop${filename^}" + done + + find release -size 0 -delete + rm release/package.json + rm release/*.map + + - name: Get current date + id: date + run: echo "::set-output name=date::$(date +'%Y-%m-%d')" + + - name: Upload Equicord Stable + run: | + gh release create ${{ steps.date.outputs.date }} --latest=false + gh release upload ${{ steps.date.outputs.date }} --clobber dist/release/* diff --git a/.github/workflows/reportBrokenPlugins.yml b/.github/workflows/reportBrokenPlugins.yml index 85156be3..f4706300 100644 --- a/.github/workflows/reportBrokenPlugins.yml +++ b/.github/workflows/reportBrokenPlugins.yml @@ -10,6 +10,10 @@ on: - stable - canary default: both + webhook_url: + type: string + description: "Webhook URL that the report will be posted to. This will be visible for everyone, so DO NOT pass sensitive webhooks like discord webhook. This is meant to be used by Venbot." + required: false schedule: # # Every day at midnight - cron: 0 0 * * * @@ -17,7 +21,7 @@ on: jobs: TestPlugins: name: Test Patches - runs-on: self-hosted + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -87,5 +91,5 @@ jobs: cat "$stable_output_file" "$canary_output_file" >> $GITHUB_STEP_SUMMARY exit $exit_code env: - WEBHOOK_URL: ${{ secrets.WEBHOOK }} + WEBHOOK_URL: ${{ inputs.webhook_url || secrets.WEBHOOK_URL }} WEBHOOK_SECRET: ${{ secrets.WEBHOOK_SECRET }} diff --git a/README.md b/README.md index 329a364a..87e80cb0 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Equicord [Equicord](https://github.com/Equicord/Equicord) +# [Equicord](https://github.com/Equicord/Equicord) Equicord [![Equibop](https://img.shields.io/badge/Equibop-grey?style=flat)](https://github.com/Equicord/Equibop) [![Tests](https://github.com/Equicord/Equicord/actions/workflows/test.yml/badge.svg?branch=main)](https://github.com/Equicord/Equicord/actions/workflows/test.yml) @@ -11,7 +11,7 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch ### Extra included plugins
-163 additional plugins +175 additional plugins ### All Platforms @@ -25,20 +25,21 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch - BetterActivities by D3SOX, Arjix, AutumnVN - BetterAudioPlayer by creations - BetterBanReasons by Inbestigator -- BetterBlockedUsers by TheArmagan & Elvyra +- BetterBlockedUsers by TheArmagan - BetterInvites by iamme - BetterPlusReacts by Joona - BetterQuickReact by Ven & Sqaaakoi - BlockKeywords by catcraft - BlockKrisp by D3SOX +- BypassPinPrompt by thororen - BypassStatus by Inbestigator & thororen - ChannelBadges by Creations - ChannelTabs by TheSun, TheKodeToad, keifufu, Nickyux - CharacterCounter by Creations & Panniku - CleanChannelName by AutumnVN - ClientSideBlock by Samwich -- ClipsEnhancements by niko - CommandPalette by Ethan +- CopyStickerLinks by Byeoon - CopyUserMention by Cortex & castdrian - CustomSounds by TheKodeToad & SpikeHD - CustomTimestamps by Rini & nvhrr @@ -51,17 +52,18 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch - DisableAnimations by S€th - DisableCameras by Joona - DontFilterMe by Samwich -- EmojiDumper by Cortex, Samwich, Woosh - Encryptcord by Inbestigator -- EquicordCSS by thororen, Panniku, Dablulite, Coolesding, MiniDiscordThemes, LuckFire, gold_me - EquicordHelper by thororen & nyx - Equissant by SomeAspy & thororen - ExportContacts by dat_insanity - FakeProfileThemesAndEffects by ryan +- CopyProfileColors by Crxa +- FastDeleteChannels by thororen - FindReply by newwares - FixFileExtensions by thororen - FollowVoiceUser by TheArmagan - FontLoader by vmohammad +- ForwardAnywhere by thororen - Freaky by nyx - FrequentQuickSwitcher by Samwich - FriendCodes by HypedDomi @@ -75,6 +77,7 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch - Glide by Samwich - GlobalBadges by HypedDomi & Hosted by Wolfie - GoogleThat by Samwich +- GuildPickerDumper by Cortex, Samwich, Synth, thororen - HideChatButtons by iamme - HideServers by bepvte - HolyNotes by Wolfie @@ -87,6 +90,7 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch - IgnoreTerms by D3SOX - ImagePreview by Creations - ImgToGif by zyqunix +- Ingtoninator by zyqunix - InRole by nin0dev - InstantScreenshare by HAHALOSAH & thororen - IRememberYou by zoodogood @@ -94,6 +98,7 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch - JumpToStart by Samwich - KeyboardSounds by HypedDomi - KeywordNotify by camila314 & x3rt +- LastActive by Crxa - LimitMiddleClickPaste by no dev listed - LoginWithQR by nexpid - MediaPlaybackSpeed by D3SOX @@ -105,6 +110,8 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch - MessageTranslate by Samwich - ModalFade by Kyuuhachi - MoreStickers by Leko & Arjix +- MoreUserTags by Cyn, TheSun, RyanCaoDev, LordElias, AutumnVN, hen +- Morse by zyqunix - NeverPausePreviews by vappstar - NewPluginsManager by Sqaaakoi - NoAppsAllowed by kvba @@ -113,9 +120,9 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch - NoMirroredCamera by Nyx - NoModalAnimation by AutumnVN - NoNitroUpsell by thororen +- NoOnboarding by omaw & Glitch - NoRoleHeaders by Samwich - NotificationTitle by Kyuuhachi -- OnePingPerDM by ProffDea - PingNotifications by smuki - PinIcon by iamme - PlatformSpoofer by Drag @@ -124,7 +131,7 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch - QuestCompleter by Amia - QuestionMarkReplacement by nyx - Quoter by Samwich -- RandomVoice by xijexo & omaw +- RandomVoice by xijexo, omaw, thororen - Remix by MrDiamond - RemixMe by kvba - RepeatMessage by Tolgchu @@ -138,7 +145,10 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch - SidebarChat by Joona - Signature by Ven, Rini, ImBanana, KrystalSkull - Slap by Korbo +- Soggy by sliwka - SoundBoardLogger by Moxxie, fres, echo, maintained by thororen +- SplitLargeMessages by Reycko +- SpotifyActivityToggle by thororen - SpotifyLyrics by Joona - StatsfmPresence by Crxa - StatusPresets by iamme @@ -150,6 +160,7 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch - ThemeLibrary by Fafa - Timezones by Aria - Title by Kyuuhachi +- ToastNotifications by Skully, Ethan, Buzzy - ToggleVideoBind by mochie - TosuRPC by AutumnVN - Translate+ by Prince527 & Ven @@ -166,6 +177,7 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch - ViewRawVariant by Kyuuhachi - VoiceChatUtilities by D3SOX - VoiceJoinMessages by Sqaaakoi & maintained by thororen +- WallpaperFree by Joona - WebpackTarball by Kyuuhachi - WhitelistedEmojis by Creations - WhosWatching by fres @@ -184,6 +196,7 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch ### Discord Desktop Only +- ClipsEnhancements by niko - MediaDownloader by Colorman - StatusWhilePlaying by thororen @@ -209,7 +222,6 @@ MacOS Linux - [GUI-X11](https://github.com/Equicord/Equilotl/releases/latest/download/Equilotl-x11) -- [GUI-Wayland](https://github.com/Equicord/Equilotl/releases/latest/download/Equilotl-wayland) - [CLI](https://github.com/Equicord/Equilotl/releases/latest/download/EquilotlCli-Linux) - [AUR](https://aur.archlinux.org/packages?O=0&K=equicord) diff --git a/browser/GMPolyfill.js b/browser/GMPolyfill.js index 387389ce..1f1b8878 100644 --- a/browser/GMPolyfill.js +++ b/browser/GMPolyfill.js @@ -17,9 +17,10 @@ */ function parseHeaders(headers) { + const result = new Headers(); if (!headers) - return {}; - const result = {}; + return result; + const headersArr = headers.trim().split("\n"); for (var i = 0; i < headersArr.length; i++) { var row = headersArr[i]; @@ -27,13 +28,7 @@ function parseHeaders(headers) { , key = row.slice(0, index).trim().toLowerCase() , value = row.slice(index + 1).trim(); - if (result[key] === undefined) { - result[key] = value; - } else if (Array.isArray(result[key])) { - result[key].push(value); - } else { - result[key] = [result[key], value]; - } + result.append(key, value); } return result; } diff --git a/browser/userscript.meta.js b/browser/userscript.meta.js index 32f3c381..7b3278d2 100644 --- a/browser/userscript.meta.js +++ b/browser/userscript.meta.js @@ -9,6 +9,7 @@ // @license GPL-3.0 // @match *://*.discord.com/* // @grant GM_xmlhttpRequest +// @grant unsafeWindow // @run-at document-start // @compatible chrome Chrome + Tampermonkey or Violentmonkey // @compatible firefox Firefox Tampermonkey diff --git a/misc/install.sh b/misc/install.sh old mode 100644 new mode 100755 index 08fe8bdd..d245733f --- a/misc/install.sh +++ b/misc/install.sh @@ -62,8 +62,16 @@ check_for_updates() { local_modified=$(stat -c "%y" "$INSTALLER_PATH" | cut -d' ' -f1-2) || error "Failed to get local modified date" if [ "$local_modified" != "$latest_modified" ]; then - echo -e "${YELLOW}Installer is outdated. Updating...${NC}" - download_installer + echo -e "${YELLOW}Installer is outdated. Do you wish to update? [y/n]${NC}" + read -p "" -n 1 -r retval + + # Create a new line before printing our next notice, otherwise it will be printed on the same line + # that the prompt was created on! + echo "" + case "$retval" in + y|Y ) download_installer;; + n|N ) echo -e "${YELLOW}Update cancelled. Running installer...${NC}" && return;; + esac else echo -e "${GREEN}Installer is up-to-date.${NC}" fi diff --git a/package.json b/package.json index 91e72a54..a02b7dcc 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "equicord", "private": "true", - "version": "1.11.8", + "version": "1.12.2", "description": "The other cutest Discord client mod", "homepage": "https://github.com/Equicord/Equicord#readme", "bugs": { diff --git a/scripts/build/build.mjs b/scripts/build/build.mjs index b4e3050f..00c0b45b 100644 --- a/scripts/build/build.mjs +++ b/scripts/build/build.mjs @@ -115,7 +115,7 @@ const buildConfigs = ([ ...nodeCommonOpts, entryPoints: [join(dirname(fileURLToPath(import.meta.url)), "../../src/main/index.ts")], outfile: "dist/desktop/patcher.js", - footer: { js: "//# sourceURL=VencordPatcher\n" + sourceMapFooter("patcher") }, + footer: { js: "//# sourceURL=file:///VencordPatcher\n" + sourceMapFooter("patcher") }, sourcemap, plugins: [ // @ts-ignore this is never undefined @@ -135,7 +135,7 @@ const buildConfigs = ([ outfile: "dist/desktop/renderer.js", format: "iife", target: ["esnext"], - footer: { js: "//# sourceURL=VencordRenderer\n" + sourceMapFooter("renderer") }, + footer: { js: "//# sourceURL=file:///VencordRenderer\n" + sourceMapFooter("renderer") }, globalName: "Vencord", sourcemap, plugins: [ @@ -153,7 +153,7 @@ const buildConfigs = ([ ...nodeCommonOpts, entryPoints: [join(dirname(fileURLToPath(import.meta.url)), "../../src/preload.ts")], outfile: "dist/desktop/preload.js", - footer: { js: "//# sourceURL=VencordPreload\n" + sourceMapFooter("preload") }, + footer: { js: "//# sourceURL=file:///VencordPreload\n" + sourceMapFooter("preload") }, sourcemap, define: { ...defines, @@ -168,7 +168,7 @@ const buildConfigs = ([ ...nodeCommonOpts, entryPoints: [join(dirname(fileURLToPath(import.meta.url)), "../../src/main/index.ts")], outfile: "dist/equibop/main.js", - footer: { js: "//# sourceURL=VencordMain\n" + sourceMapFooter("main") }, + footer: { js: "//# sourceURL=file:///VencordDesktopMain\n" + sourceMapFooter("main") }, sourcemap, plugins: [ ...nodeCommonOpts.plugins, @@ -187,7 +187,7 @@ const buildConfigs = ([ outfile: "dist/equibop/renderer.js", format: "iife", target: ["esnext"], - footer: { js: "//# sourceURL=VencordRenderer\n" + sourceMapFooter("renderer") }, + footer: { js: "//# sourceURL=file:///VencordDesktopRenderer\n" + sourceMapFooter("renderer") }, globalName: "Vencord", sourcemap, plugins: [ @@ -205,7 +205,7 @@ const buildConfigs = ([ ...nodeCommonOpts, entryPoints: [join(dirname(fileURLToPath(import.meta.url)), "../../src/preload.ts")], outfile: "dist/equibop/preload.js", - footer: { js: "//# sourceURL=VencordPreload\n" + sourceMapFooter("preload") }, + footer: { js: "//# sourceURL=file:///VencordPreload\n" + sourceMapFooter("preload") }, sourcemap, define: { ...defines, diff --git a/scripts/build/buildWeb.mjs b/scripts/build/buildWeb.mjs index 800f6bfd..dadaf294 100644 --- a/scripts/build/buildWeb.mjs +++ b/scripts/build/buildWeb.mjs @@ -92,7 +92,7 @@ const buildConfigs = [ { ...commonOptions, outfile: "dist/browser/browser.js", - footer: { js: "//# sourceURL=VencordWeb" } + footer: { js: "//# sourceURL=file:///VencordWeb" } }, { ...commonOptions, @@ -101,7 +101,7 @@ const buildConfigs = [ ...commonOptions.define, IS_EXTENSION: "true" }, - footer: { js: "//# sourceURL=VencordWeb" } + footer: { js: "//# sourceURL=file:///VencordWeb" } }, { ...commonOptions, diff --git a/scripts/build/common.mjs b/scripts/build/common.mjs index fd2ff6a7..ca3eab14 100644 --- a/scripts/build/common.mjs +++ b/scripts/build/common.mjs @@ -186,7 +186,7 @@ export const globPlugins = kind => ({ const mod = `p${i}`; code += `import ${mod} from "./${dir}/${fileName.replace(/\.tsx?$/, "")}";\n`; pluginsCode += `[${mod}.name]:${mod},\n`; - metaCode += `[${mod}.name]:${JSON.stringify({ folderName, userPlugin })},\n`; // TODO: add excluded plugins to display in the UI? + metaCode += `[${mod}.name]:${JSON.stringify({ folderName, userPlugin })},\n`; i++; } } diff --git a/scripts/generateReport.ts b/scripts/generateReport.ts index a709f7bc..68b3a044 100644 --- a/scripts/generateReport.ts +++ b/scripts/generateReport.ts @@ -261,7 +261,7 @@ page.on("console", async e => { const [, tag, message, otherMessage] = args as Array; switch (tag) { - case "WebpackInterceptor:": + case "WebpackPatcher:": const patchFailMatch = message.match(/Patch by (.+?) (had no effect|errored|found no module) \(Module id is (.+?)\): (.+)/); const patchSlowMatch = message.match(/Patch by (.+?) (took [\d.]+?ms) \(Module id is (.+?)\): (.+)/); const match = patchFailMatch ?? patchSlowMatch; @@ -315,11 +315,9 @@ page.on("console", async e => { report.badWebpackFinds.push(otherMessage); break; case "Finished test": + await browser.close(); await printReport(); - setTimeout(async () => { - await browser.close(); - process.exit(); - }, 10000); + process.exit(); } } } @@ -358,4 +356,4 @@ await page.evaluateOnNewDocument(` } `); -await page.goto(CANARY ? "https://canary.discord.com/login" : "https://discord.com/login"); +await page.goto(CANARY ? "https://canary.discord.com/login" : "https://discord.com/login", { timeout: 120000 }); diff --git a/src/Vencord.ts b/src/Vencord.ts index 586d732c..b43564a2 100644 --- a/src/Vencord.ts +++ b/src/Vencord.ts @@ -33,7 +33,7 @@ import { openUpdaterModal } from "@components/VencordSettings/UpdaterTab"; import { StartAt } from "@utils/types"; import { get as dsGet } from "./api/DataStore"; -import { showNotification } from "./api/Notifications"; +import { NotificationData, showNotification } from "./api/Notifications"; import { PlainSettings, Settings } from "./api/Settings"; import { patches, PMLogger, startAllPlugins } from "./plugins"; import { localStorage } from "./utils/localStorage"; @@ -105,6 +105,46 @@ async function syncSettings() { } } +let notifiedForUpdatesThisSession = false; + +async function runUpdateCheck() { + const notify = (data: NotificationData) => { + if (notifiedForUpdatesThisSession) return; + notifiedForUpdatesThisSession = true; + + setTimeout(() => showNotification({ + permanent: true, + noPersist: true, + ...data + }), 10_000); + }; + + try { + const isOutdated = await checkForUpdates(); + if (!isOutdated) return; + + if (Settings.autoUpdate) { + await update(); + if (Settings.autoUpdateNotification) { + notify({ + title: "Equicord has been updated!", + body: "Click here to restart", + onClick: relaunch + }); + } + return; + } + + notify({ + title: "A Equicord update is available!", + body: "Click here to view the update", + onClick: openUpdaterModal! + }); + } catch (err) { + UpdateLogger.error("Failed to check for updates", err); + } +} + async function init() { await onceReady; startAllPlugins(StartAt.WebpackReady); @@ -112,34 +152,8 @@ async function init() { syncSettings(); if (!IS_WEB && !IS_UPDATER_DISABLED) { - try { - const isOutdated = await checkForUpdates(); - if (!isOutdated) return; - - if (Settings.autoUpdate) { - await update(); - if (Settings.updateRelaunch) return relaunch; - if (Settings.autoUpdateNotification) - setTimeout(() => showNotification({ - title: "Equicord has been updated!", - body: "Click here to restart", - permanent: true, - noPersist: true, - onClick: relaunch - }), 10_000); - return; - } - - setTimeout(() => showNotification({ - title: "A Equicord update is available!", - body: "Click here to view the update", - permanent: true, - noPersist: true, - onClick: openUpdaterModal! - }), 10_000); - } catch (err) { - UpdateLogger.error("Failed to check for updates", err); - } + runUpdateCheck(); + setInterval(runUpdateCheck, 1000 * 60 * 30); // 30 minutes } if (IS_DEV) { @@ -149,7 +163,7 @@ async function init() { "Webpack has finished initialising, but some patches haven't been applied yet.", "This might be expected since some Modules are lazy loaded, but please verify", "that all plugins are working as intended.", - "You are seeing this warning because this is a Development build of Vencord.", + "You are seeing this warning because this is a Development build of Equicord.", "\nThe following patches have not been applied:", "\n\n" + pendingPatches.map(p => `${p.plugin}: ${p.find}`).join("\n") ); diff --git a/src/api/MemberListDecorators.tsx b/src/api/MemberListDecorators.tsx index ab5a618b..ada60776 100644 --- a/src/api/MemberListDecorators.tsx +++ b/src/api/MemberListDecorators.tsx @@ -21,25 +21,14 @@ import { Channel, User } from "discord-types/general/index.js"; import { JSX } from "react"; interface DecoratorProps { - activities: any[]; - channel: Channel; - /** - * Only for DM members - */ - channelName?: string; - /** - * Only for server members - */ - currentUser?: User; - guildId?: string; - isMobile: boolean; - isOwner?: boolean; - isTyping: boolean; - selected: boolean; - status: string; + type: "guild" | "dm"; user: User; - [key: string]: any; + /** only present when this is a DM list item */ + channel: Channel; + /** only present when this is a guild list item */ + isOwner: boolean; } + export type MemberListDecoratorFactory = (props: DecoratorProps) => JSX.Element | null; type OnlyIn = "guilds" | "dms"; @@ -53,18 +42,16 @@ export function removeMemberListDecorator(identifier: string) { decoratorsFactories.delete(identifier); } -export function __getDecorators(props: DecoratorProps): JSX.Element { - const isInGuild = !!(props.guildId); - +export function __getDecorators(props: DecoratorProps, type: "guild" | "dm"): JSX.Element { const decorators = Array.from( decoratorsFactories.entries(), ([key, { render: Decorator, onlyIn }]) => { - if ((onlyIn === "guilds" && !isInGuild) || (onlyIn === "dms" && isInGuild)) + if ((onlyIn === "guilds" && type !== "guild") || (onlyIn === "dms" && type !== "dm")) return null; return ( - + ); } diff --git a/src/api/NicknameIcons.tsx b/src/api/NicknameIcons.tsx new file mode 100644 index 00000000..8b0fbc20 --- /dev/null +++ b/src/api/NicknameIcons.tsx @@ -0,0 +1,40 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2025 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import ErrorBoundary from "@components/ErrorBoundary"; +import { Logger } from "@utils/Logger"; +import { ReactNode } from "react"; + +export interface NicknameIconProps { + userId: string; +} + +export type NicknameIconFactory = (props: NicknameIconProps) => ReactNode | Promise; + +export interface NicknameIcon { + priority: number; + factory: NicknameIconFactory; +} + +const nicknameIcons = new Map(); +const logger = new Logger("NicknameIcons"); + +export function addNicknameIcon(id: string, factory: NicknameIconFactory, priority = 0) { + return nicknameIcons.set(id, { + priority, + factory: ErrorBoundary.wrap(factory, { noop: true, onError: error => logger.error(`Failed to render ${id}`, error) }) + }); +} + +export function removeNicknameIcon(id: string) { + return nicknameIcons.delete(id); +} + +export function _renderIcons(props: NicknameIconProps) { + return Array.from(nicknameIcons) + .sort((a, b) => b[1].priority - a[1].priority) + .map(([id, { factory: NicknameIcon }]) => ); +} diff --git a/src/api/Notifications/Notifications.tsx b/src/api/Notifications/Notifications.tsx index 1350e5bd..96a3425b 100644 --- a/src/api/Notifications/Notifications.tsx +++ b/src/api/Notifications/Notifications.tsx @@ -18,7 +18,7 @@ import { Settings } from "@api/Settings"; import { Queue } from "@utils/Queue"; -import { ReactDOM } from "@webpack/common"; +import { createRoot } from "@webpack/common"; import type { ReactNode } from "react"; import type { Root } from "react-dom/client"; @@ -35,7 +35,7 @@ function getRoot() { const container = document.createElement("div"); container.id = "vc-notification-container"; document.body.append(container); - reactRoot = ReactDOM.createRoot(container); + reactRoot = createRoot(container); } return reactRoot; } diff --git a/src/api/Notifications/styles.css b/src/api/Notifications/styles.css index ad5c9cbc..98dff6df 100644 --- a/src/api/Notifications/styles.css +++ b/src/api/Notifications/styles.css @@ -11,10 +11,6 @@ width: 100%; } -.visual-refresh .vc-notification-root { - background-color: var(--bg-overlay-floating, var(--background-base-low)); -} - .vc-notification-root:not(.vc-notification-log-wrapper > .vc-notification-root) { position: absolute; z-index: 2147483647; diff --git a/src/api/Settings.ts b/src/api/Settings.ts index 25c4b3d9..e4dc21c5 100644 --- a/src/api/Settings.ts +++ b/src/api/Settings.ts @@ -39,7 +39,6 @@ export interface Settings { themeLinks: string[]; frameless: boolean; transparent: boolean; - updateRelaunch: boolean; winCtrlQ: boolean; macosVibrancyStyle: | "content" @@ -101,7 +100,6 @@ const DefaultSettings: Settings = { winCtrlQ: false, macosVibrancyStyle: undefined, disableMinSize: false, - updateRelaunch: false, winNativeTitleBar: false, plugins: {}, diff --git a/src/api/index.ts b/src/api/index.ts index f0538f52..8af6a49f 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -27,6 +27,7 @@ import * as $MessageDecorations from "./MessageDecorations"; import * as $MessageEventsAPI from "./MessageEvents"; import * as $MessagePopover from "./MessagePopover"; import * as $MessageUpdater from "./MessageUpdater"; +import * as $NicknameIcons from "./NicknameIcons"; import * as $Notices from "./Notices"; import * as $Notifications from "./Notifications"; import * as $ServerList from "./ServerList"; @@ -123,6 +124,11 @@ export const MessageUpdater = $MessageUpdater; */ export const UserSettings = $UserSettings; +/** + * An API allowing you to add icons to the nickname, in profiles + */ +export const NicknameIcons = $NicknameIcons; + /** * Just used to identify if user is on Equicord as Vencord doesnt have this */ diff --git a/src/components/DonateButton.tsx b/src/components/DonateButton.tsx index 4139dd31..814eba8e 100644 --- a/src/components/DonateButton.tsx +++ b/src/components/DonateButton.tsx @@ -22,7 +22,26 @@ import { ButtonProps } from "@webpack/types"; import { Heart } from "./Heart"; -export default function DonateButton({ +export function VCDonateButton({ + look = Button.Looks.LINK, + color = Button.Colors.TRANSPARENT, + ...props +}: Partial) { + return ( + + ); +} + +export function DonateButton({ look = Button.Looks.LINK, color = Button.Colors.TRANSPARENT, ...props diff --git a/src/components/ErrorBoundary.tsx b/src/components/ErrorBoundary.tsx index bb2df342..0ca20440 100644 --- a/src/components/ErrorBoundary.tsx +++ b/src/components/ErrorBoundary.tsx @@ -16,10 +16,10 @@ * along with this program. If not, see . */ +import { LazyComponent, LazyComponentWrapper } from "@utils/lazyReact"; import { Logger } from "@utils/Logger"; import { Margins } from "@utils/margins"; -import { LazyComponent } from "@utils/react"; -import { React } from "@webpack/common"; +import type { React } from "@webpack/common"; import { ErrorCard } from "./ErrorCard"; @@ -46,7 +46,9 @@ const NO_ERROR = {}; // We might want to import this in a place where React isn't ready yet. // Thus, wrap in a LazyComponent const ErrorBoundary = LazyComponent(() => { - return class ErrorBoundary extends React.PureComponent> { + // This component is used in a lot of files which end up importing other Webpack commons and causing circular imports. + // For this reason, use a non import access here. + return class ErrorBoundary extends Vencord.Webpack.Common.React.PureComponent> { state = { error: NO_ERROR as any, stack: "", @@ -107,9 +109,9 @@ const ErrorBoundary = LazyComponent(() => { } }; }) as - React.ComponentType> & { + LazyComponentWrapper> & { wrap(Component: React.ComponentType, errorBoundaryProps?: Omit, "wrappedProps">): React.FunctionComponent; - }; + }>; ErrorBoundary.wrap = (Component, errorBoundaryProps) => props => ( diff --git a/src/components/PluginSettings/PluginModal.tsx b/src/components/PluginSettings/PluginModal.tsx index 98f23fce..69a2273f 100644 --- a/src/components/PluginSettings/PluginModal.tsx +++ b/src/components/PluginSettings/PluginModal.tsx @@ -179,20 +179,18 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti } } - function renderMoreUsers(_label: string, count: number) { - const sliceCount = plugin.authors.length - count; - const sliceStart = plugin.authors.length - sliceCount; - const sliceEnd = sliceStart + plugin.authors.length - count; + function renderMoreUsers(_label: string) { + const remainingAuthors = plugin.authors.slice(6); return ( - u.name).join(", ")}> + u.name).join(", ")}> {({ onMouseEnter, onMouseLeave }) => (
- +{sliceCount} + +{remainingAuthors.length}
)}
@@ -250,7 +248,6 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti count={plugin.authors.length} guildId={undefined} renderIcon={false} - max={6} showDefaultAvatarsForNullUsers showUserPopout renderMoreUsers={renderMoreUsers} diff --git a/src/components/PluginSettings/styles.css b/src/components/PluginSettings/styles.css index 1eabaa1b..488fd212 100644 --- a/src/components/PluginSettings/styles.css +++ b/src/components/PluginSettings/styles.css @@ -252,6 +252,24 @@ } .visual-refresh .button-danger-background:hover { - background-color: var(--status-danger-background) !important; - color: var(--status-danger-text) !important; + background-color: var(--status-danger-background) !important; + color: var(--status-danger-text) !important; +} + +.visual-refresh .vc-plugins-info-card { + background-color: var(--card-primary-bg) !important; + border: 1px solid var(--border-subtle) !important; + + &:hover { + background-color: var(--card-primary-bg) !important; + } +} + +.visual-refresh .vc-plugin-stats { + background-color: var(--card-primary-bg) !important; + border: 1px solid var(--border-subtle) !important; + + &:hover { + background-color: var(--card-primary-bg) !important; + } } diff --git a/src/components/VencordSettings/PatchHelperTab.tsx b/src/components/VencordSettings/PatchHelperTab.tsx index 943f0299..1b1e965d 100644 --- a/src/components/VencordSettings/PatchHelperTab.tsx +++ b/src/components/VencordSettings/PatchHelperTab.tsx @@ -18,12 +18,13 @@ import { CodeBlock } from "@components/CodeBlock"; import { debounce } from "@shared/debounce"; +import { copyToClipboard } from "@utils/clipboard"; import { Margins } from "@utils/margins"; import { canonicalizeMatch, canonicalizeReplace } from "@utils/patches"; import { makeCodeblock } from "@utils/text"; import { Patch, ReplaceFn } from "@utils/types"; import { search } from "@webpack"; -import { Button, Clipboard, Forms, Parser, React, Switch, TextArea, TextInput } from "@webpack/common"; +import { Button, Forms, Parser, React, Switch, TextArea, TextInput } from "@webpack/common"; import { SettingsTab, wrapTab } from "./shared"; @@ -378,8 +379,8 @@ function PatchHelper() { <> Code - - + + )} diff --git a/src/components/VencordSettings/UpdaterTab.tsx b/src/components/VencordSettings/UpdaterTab.tsx index c03f5b93..f500b720 100644 --- a/src/components/VencordSettings/UpdaterTab.tsx +++ b/src/components/VencordSettings/UpdaterTab.tsx @@ -106,8 +106,6 @@ function Updatable(props: CommonProps) { const [updates, setUpdates] = React.useState(changes); const [isChecking, setIsChecking] = React.useState(false); const [isUpdating, setIsUpdating] = React.useState(false); - - const settings = useSettings(["updateRelaunch"]); const isOutdated = (updates?.length ?? 0) > 0; return ( @@ -119,7 +117,6 @@ function Updatable(props: CommonProps) { onClick={withDispatcher(setIsUpdating, async () => { if (await update()) { setUpdates([]); - if (settings.updateRelaunch) return relaunch(); return await new Promise(r => { Alerts.show({ title: "Update Success!", @@ -191,7 +188,7 @@ function Newer(props: CommonProps) { } function Updater() { - const settings = useSettings(["autoUpdate", "updateRelaunch", "autoUpdateNotification"]); + const settings = useSettings(["autoUpdate", "autoUpdateNotification"]); const [repo, err, repoPending] = useAwaiter(getRepo, { fallbackValue: "Loading..." }); @@ -217,30 +214,12 @@ function Updater() { { - settings.autoUpdateNotification = v; - if (settings.updateRelaunch) { - settings.updateRelaunch = !v; - } - }} + onChange={(v: boolean) => settings.autoUpdateNotification = v} note="Shows a notification when Equicord automatically updates" disabled={!settings.autoUpdate} > Get notified when an automatic update completes - { - settings.updateRelaunch = v; - if (settings.autoUpdateNotification) { - settings.autoUpdateNotification = !v; - } - }} - note="Relaunches the app after updating with no prompt" - disabled={!settings.autoUpdate} - > - Automatically relaunch after updating - Repo diff --git a/src/components/VencordSettings/VencordTab.tsx b/src/components/VencordSettings/VencordTab.tsx index 53100ea9..606986b7 100644 --- a/src/components/VencordSettings/VencordTab.tsx +++ b/src/components/VencordSettings/VencordTab.tsx @@ -9,7 +9,7 @@ import "./VencordTab.css"; import { openNotificationLogModal } from "@api/Notifications/notificationLog"; import { useSettings } from "@api/Settings"; import { classNameFactory } from "@api/Styles"; -import DonateButton, { InviteButton } from "@components/DonateButton"; +import { DonateButton, InviteButton } from "@components/DonateButton"; import { openContributorModal } from "@components/PluginSettings/ContributorModal"; import { openPluginModal } from "@components/PluginSettings/PluginModal"; import { gitRemote } from "@shared/vencordUserAgent"; @@ -82,7 +82,7 @@ function EquicordSettings() { (!IS_DISCORD_DESKTOP || !isWindows ? { key: "frameless", - title: "Disable the window frame", + title: "Disable the Window Frame", note: "Requires a full restart", warning: { enabled: false }, } @@ -95,7 +95,7 @@ function EquicordSettings() { }), !IS_WEB && { key: "transparent", - title: "Enable window transparency.", + title: "Enable Window Transparency", note: "You need a theme that supports transparency or this will do nothing. Requires a full restart!", warning: { enabled: isWindows, @@ -112,7 +112,7 @@ function EquicordSettings() { }, IS_DISCORD_DESKTOP && { key: "disableMinSize", - title: "Disable minimum window size", + title: "Disable Minimum Window Size", note: "Requires a full restart", warning: { enabled: false }, }, diff --git a/src/debug/runReporter.ts b/src/debug/runReporter.ts index f76f583e..041c0018 100644 --- a/src/debug/runReporter.ts +++ b/src/debug/runReporter.ts @@ -48,7 +48,7 @@ async function runReporter() { for (const patch of patches) { if (!patch.all) { - new Logger("WebpackInterceptor").warn(`Patch by ${patch.plugin} found no module (Module id is -): ${patch.find}`); + new Logger("WebpackPatcher").warn(`Patch by ${patch.plugin} found no module (Module id is -): ${patch.find}`); if (IS_COMPANION_TEST) reporterData.failedPatches.foundNoModule.push(patch); } @@ -56,7 +56,7 @@ async function runReporter() { for (const [plugin, moduleId, match, totalTime] of patchTimings) { if (totalTime > 5) { - new Logger("WebpackInterceptor").warn(`Patch by ${plugin} took ${Math.round(totalTime * 100) / 100}ms (Module id is ${String(moduleId)}): ${match}`); + new Logger("WebpackPatcher").warn(`Patch by ${plugin} took ${Math.round(totalTime * 100) / 100}ms (Module id is ${String(moduleId)}): ${match}`); } } @@ -92,7 +92,7 @@ async function runReporter() { result = Webpack[method](...args); } - if (result == null || (result.$$vencordInternal != null && result.$$vencordInternal() == null)) throw new Error("Webpack Find Fail"); + if (result == null || (result.$$vencordGetWrappedComponent != null && result.$$vencordGetWrappedComponent() == null)) throw new Error("Webpack Find Fail"); } catch (e) { let logMessage = searchType; if (method === "find" || method === "proxyLazyWebpack" || method === "LazyComponentWebpack") { diff --git a/src/equicordplugins/_core/equicordHelper.tsx b/src/equicordplugins/_core/equicordHelper.tsx index bfd67722..7b50c43a 100644 --- a/src/equicordplugins/_core/equicordHelper.tsx +++ b/src/equicordplugins/_core/equicordHelper.tsx @@ -18,10 +18,26 @@ import "@equicordplugins/_misc/styles.css"; +import { definePluginSettings } from "@api/Settings"; +import { disableStyle, enableStyle } from "@api/Styles"; import { EquicordDevs } from "@utils/constants"; -import definePlugin from "@utils/types"; +import definePlugin, { OptionType } from "@utils/types"; import { Forms } from "@webpack/common"; +import clanBadges from "../_misc/clanBadges.css?managed"; + +const settings = definePluginSettings({ + hideClanBadges: { + type: OptionType.BOOLEAN, + description: "Hide clan badges", + default: false, + onChange: value => { + if (value) enableStyle(clanBadges); + else disableStyle(clanBadges); + } + } +}); + export default definePlugin({ name: "EquicordHelper", description: "Fixes some misc issues with discord", @@ -31,6 +47,7 @@ export default definePlugin({ This Plugin is used for fixing misc issues with discord such as some crashes , + settings, required: true, patches: [ { @@ -45,13 +62,12 @@ export default definePlugin({ replace: "return $1;" } ] - }, - { - find: '"Slate: Unable to find syntax characters"', - replacement: { - match: /((let )(\i)=\i\.indexOf\(\i,(\i)\)),/, - replace: "$1;if ($3 === -1) {return $4;}$2" - } } - ] + ], + start() { + if (settings.store.hideClanBadges) enableStyle(clanBadges); + }, + stop() { + if (settings.store.hideClanBadges) disableStyle(clanBadges); + } }); diff --git a/src/equicordplugins/_misc/clanBadges.css b/src/equicordplugins/_misc/clanBadges.css new file mode 100644 index 00000000..6e577ba7 --- /dev/null +++ b/src/equicordplugins/_misc/clanBadges.css @@ -0,0 +1,3 @@ +[class*="chipletContainerInner_"]:has([src *="/clan-badges/"]) { + display: none; +} diff --git a/src/equicordplugins/autoJump/index.tsx b/src/equicordplugins/autoJump/index.tsx new file mode 100644 index 00000000..b05ba1e6 --- /dev/null +++ b/src/equicordplugins/autoJump/index.tsx @@ -0,0 +1,69 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { NavContextMenuPatchCallback } from "@api/ContextMenu"; +import { definePluginSettings } from "@api/Settings"; +import { EquicordDevs } from "@utils/constants"; +import definePlugin, { OptionType } from "@utils/types"; +import { ChannelStore, Menu, MessageActions, NavigationRouter } from "@webpack/common"; + +interface ChannelSelectEvent { + type: "CHANNEL_SELECT"; + channelId: string | null; + guildId: string | null; +} + +let lastChannelId = "0"; + +function autoJump({ guild_id, id: channelId }) { + const guildId = guild_id ?? "@me"; + + lastChannelId = channelId; + NavigationRouter.transitionTo(`/channels/${guildId}/${channelId}`); + MessageActions.jumpToPresent(channelId, { limit: null }); +} + +const MenuPatch: NavContextMenuPatchCallback = (children, { channel }) => { + children.push( + { + autoJump(channel); + }} + /> + ); +}; + +const settings = definePluginSettings({ + autoJumping: { + type: OptionType.BOOLEAN, + description: "Automatically jump to the last message in the channel when switching channels", + default: false + } +}); + +export default definePlugin({ + name: "AutoJump", + description: "Jumps to Last Message in Channel", + authors: [EquicordDevs.omaw], + settings, + contextMenus: { + "channel-context": MenuPatch, + "user-context": MenuPatch, + "thread-context": MenuPatch + }, + flux: { + async CHANNEL_SELECT({ guildId, channelId }: ChannelSelectEvent) { + if (!settings.store.autoJumping || !channelId) return; + + const channel = ChannelStore.getChannel(channelId); + if (!channel || channel.id === lastChannelId) return; + + autoJump({ guild_id: guildId, id: channelId }); + } + } +}); diff --git a/src/equicordplugins/bannersEverywhere/index.tsx b/src/equicordplugins/bannersEverywhere/index.tsx index ad804c89..f402dd3f 100644 --- a/src/equicordplugins/bannersEverywhere/index.tsx +++ b/src/equicordplugins/bannersEverywhere/index.tsx @@ -58,8 +58,8 @@ export default definePlugin({ replacement: [ { // We add the banner as a property while we can still access the user id - match: /(?<=nameplate:(\i).*?)verified:(\i).isVerifiedBot.*?name:null.*?(?=avatar:)/, - replace: "$&banner:$self.memberListBannerHook($2, $1),", + match: /user:(\i).{0,150}nameplate:(\i).*?name:null.*?(?=avatar:)/, + replace: "$&banner:$self.memberListBannerHook($1, $2),", }, { match: /(?<=\),nameplate:)(\i)/, @@ -112,7 +112,7 @@ export default definePlugin({ } return ( - + ); }, diff --git a/src/equicordplugins/betterActivities/index.tsx b/src/equicordplugins/betterActivities/index.tsx index b87f8ff3..6212eb33 100644 --- a/src/equicordplugins/betterActivities/index.tsx +++ b/src/equicordplugins/betterActivities/index.tsx @@ -35,7 +35,7 @@ export default definePlugin({ patches: [ { // Patch activity icons - find: '"activity-status-web"', + find: "isBlockedOrIgnored(null", replacement: { match: /(?<=hideTooltip:.{0,4}}=(\i).*?{}\))\]/, replace: ",$self.patchActivityList($1)]" @@ -44,9 +44,9 @@ export default definePlugin({ }, { // Show all activities in the user popout/sidebar - find: '"UserProfilePopoutBody"', + find: "hasAvatarForGuild(null", replacement: { - match: /(?<=(\i)\.id\)\}\)\),(\i).*?)\(0,.{0,100}\i\.activity\}\)/, + match: /(?<=(\i)\.id\)\}\)\),(\i).*?)\(0,.{0,100}\i\.id,onClose:\i\}\)/, replace: "$self.showAllActivitiesComponent({ activity: $2, user: $1 })" }, predicate: () => settings.store.userPopout diff --git a/src/equicordplugins/betterAudioPlayer/index.tsx b/src/equicordplugins/betterAudioPlayer/index.tsx index 0f942e90..6264db1f 100644 --- a/src/equicordplugins/betterAudioPlayer/index.tsx +++ b/src/equicordplugins/betterAudioPlayer/index.tsx @@ -66,7 +66,7 @@ async function addListeners(audioElement: HTMLAudioElement, url: string, parentB const madeURL = new URL(url); madeURL.searchParams.set("t", Date.now().toString()); - const corsProxyUrl = "https://corsproxy.io?" + encodeURIComponent(madeURL.href); + const corsProxyUrl = "https://corsproxy.io/?url=" + encodeURIComponent(madeURL.href); const response = await fetch(corsProxyUrl); const blob = await response.blob(); const blobUrl = URL.createObjectURL(blob); diff --git a/src/equicordplugins/betterBanReasons/index.tsx b/src/equicordplugins/betterBanReasons/index.tsx index 401235e1..3c01939c 100644 --- a/src/equicordplugins/betterBanReasons/index.tsx +++ b/src/equicordplugins/betterBanReasons/index.tsx @@ -4,53 +4,55 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import "./style.css"; +import "./styles.css"; import { definePluginSettings } from "@api/Settings"; +import { classNameFactory } from "@api/Styles"; +import { DeleteIcon, PlusIcon } from "@components/Icons"; import { Devs } from "@utils/constants"; import { getIntlMessage } from "@utils/discord"; import definePlugin, { OptionType } from "@utils/types"; import { Button, Forms, TextInput } from "@webpack/common"; +const cl = classNameFactory("vc-bbr-"); + function ReasonsComponent() { - const { reasons } = settings.use(["reasons"]); + const { reasons } = settings.store; return ( - {reasons.map((reason: string, index: number) => ( + {reasons.map((r, i) => (
{ - reasons[index] = v; - settings.store.reasons = [...reasons]; + reasons[i] = v; + settings.store.reasons = reasons; }} placeholder="Reason" />
))} - +
+ +
); } @@ -59,10 +61,10 @@ const settings = definePluginSettings({ reasons: { description: "Your custom reasons", type: OptionType.COMPONENT, - default: [""], + default: [] as string[], component: ReasonsComponent, }, - textInputDefault: { + isTextInputDefault: { type: OptionType.BOOLEAN, description: 'Shows a text input instead of a select menu by default. (Equivalent to clicking the "Other" option)' } @@ -74,9 +76,9 @@ export default definePlugin({ authors: [Devs.Inbestigator], patches: [ { - find: "#{intl::BAN_MULTIPLE_CONFIRM_TITLE}", + find: "#{intl::BAN_REASON_OPTION_SPAM_ACCOUNT}", replacement: [{ - match: /\[\{name:\i\.\i\.string\(\i\.\i#{intl::BAN_REASON_OPTION_SPAM_ACCOUNT}\).+?\}\]/, + match: /\[(\{((name|value):\i\.\i\.string\(\i\.\i\.\i\),?){2}\},?){3}\]/, replace: "$self.getReasons()" }, { @@ -86,17 +88,16 @@ export default definePlugin({ } ], getReasons() { - const reasons = settings.store.reasons.length - ? settings.store.reasons + const storedReasons = settings.store.reasons.filter((r: string) => r.trim()); + const reasons: string[] = storedReasons.length + ? storedReasons : [ getIntlMessage("BAN_REASON_OPTION_SPAM_ACCOUNT"), getIntlMessage("BAN_REASON_OPTION_HACKED_ACCOUNT"), - getIntlMessage("BAN_REASON_OPTION_BREAKING_RULES") + getIntlMessage("BAN_REASON_OPTION_BREAKING_RULES"), ]; return reasons.map(s => ({ name: s, value: s })); }, - getDefaultState() { - return settings.store.textInputDefault ? 1 : 0; - }, + getDefaultState: () => settings.store.isTextInputDefault ? 1 : 0, settings, }); diff --git a/src/equicordplugins/betterBanReasons/style.css b/src/equicordplugins/betterBanReasons/style.css deleted file mode 100644 index 02fb4bbf..00000000 --- a/src/equicordplugins/betterBanReasons/style.css +++ /dev/null @@ -1,11 +0,0 @@ -.vc-bbr-reason-wrapper { - display: grid; - padding: 0; - padding-bottom: 0.5rem; - gap: 0.5rem; - grid-template-columns: 6fr 1fr; -} - -.vc-bbr-remove-button { - height: 100%; -} diff --git a/src/equicordplugins/betterBanReasons/styles.css b/src/equicordplugins/betterBanReasons/styles.css new file mode 100644 index 00000000..a1ba64ea --- /dev/null +++ b/src/equicordplugins/betterBanReasons/styles.css @@ -0,0 +1,31 @@ +.vc-bbr-reason-wrapper { + display: grid; + padding-bottom: 12px; + gap: 4px 12px; + align-items: center; + grid-template-columns: 1fr 24px; +} + +.vc-bbr-remove-button { + color: var(--text-muted); +} + +.vc-bbr-remove-button:hover { + color: var(--button-danger-background-hover); +} + +.vc-bbr-add-button { + justify-content: start !important; + border: 0; + padding: 2px 12px; + color: var(--text-muted) !important; + font-size: 16px; +} + +.vc-bbr-add-button div { + display: flex; + justify-content: start; + align-items: center; + gap: 12px; + margin: 0 !important; +} diff --git a/src/equicordplugins/betterBlockedUsers/index.tsx b/src/equicordplugins/betterBlockedUsers/index.tsx index 258933d2..87876bbf 100644 --- a/src/equicordplugins/betterBlockedUsers/index.tsx +++ b/src/equicordplugins/betterBlockedUsers/index.tsx @@ -4,58 +4,23 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { definePluginSettings } from "@api/Settings"; -import { Devs, EquicordDevs } from "@utils/constants"; -import { openUserProfile } from "@utils/discord"; -import { openModal } from "@utils/modal"; -import definePlugin, { OptionType } from "@utils/types"; -import { findByCodeLazy, findByPropsLazy, findComponentByCodeLazy } from "@webpack"; -import { Button, FluxDispatcher, React, RelationshipStore, Text, TextInput, UserStore } from "@webpack/common"; -import { ButtonProps } from "@webpack/types"; -import { User } from "discord-types/general"; +import "./styles.css"; + +import { EquicordDevs } from "@utils/constants"; +import { getIntlMessage, openUserProfile } from "@utils/discord"; +import definePlugin from "@utils/types"; +import { findByPropsLazy } from "@webpack"; +import { Button, React, RelationshipStore, TextInput, UserStore } from "@webpack/common"; + +const ChannelActions = findByPropsLazy("openPrivateChannel"); let lastSearch = ""; let updateFunc = (v: any) => { }; -const ChannelActions = findByPropsLazy("openPrivateChannel"); -const ButtonComponent = findComponentByCodeLazy('submittingStartedLabel","submittingFinishedLabel"]);'); -const ConfirmationModal = findByCodeLazy('"ConfirmModal")', "useLayoutEffect"); - -const settings = definePluginSettings({ - addDmsButton: { - default: true, - type: OptionType.BOOLEAN, - description: "Adds a 'View DMs' button to the users in the blocked/ignored list.", - }, - hideBlockedWarning: { - default: false, - type: OptionType.BOOLEAN, - description: "Skip the warning about blocked/ignored users when opening any profile anywhere on discord outside of the blocklist.", - restartNeeded: true, - }, - showUnblockConfirmation: { - default: true, - type: OptionType.BOOLEAN, - description: "Show a warning before unblocking a user from the blocklist.", - }, - showUnblockConfirmationEverywhere: { - default: false, - type: OptionType.BOOLEAN, - description: "Show a warning before unblocking a user anywhere on discord.", - restartNeeded: true, - }, - unblockButtonDanger: { - default: false, - type: OptionType.BOOLEAN, - description: "Color the unblock button in the blocklist red instead of gray.", - }, -}); - export default definePlugin({ name: "BetterBlockedUsers", - description: "Allows you to search in blocked users list and makes names clickable in settings.", - authors: [EquicordDevs.TheArmagan, Devs.Elvyra], - settings, + description: "Allows you to search in blocked users list and makes names selectable in settings.", + authors: [EquicordDevs.TheArmagan], patches: [ { find: '"],{numberOfBlockedUsers:', @@ -65,12 +30,8 @@ export default definePlugin({ replace: ",$1.listType==='blocked'?$self.renderSearchInput():null" }, { - match: /(?<=className:\i.userInfo,)(?=children:.{0,20}user:(\i))/, - replace: "style:{cursor:'pointer'},onClick:()=>$self.openUserProfile($1)," - }, - { - match: /(?<=children:null!=(\i).globalName\?.+?}\),).*?(\{color:.{0,65}?string\((\i).+?"8wXU9P"]\)})\)/, - replace: "$self.generateButtons({user:$1, originalProps:$2, isBlocked:$3})", + match: /(?<=userId:(\i).*?\}\)\]\}\),)(\(.*?\)\}\))/, + replace: "$self.renderUser($1,$2),", }, { match: /(?<=\}=(\i).{0,10}(\i).useState\(.{0,1}\);)/, @@ -81,64 +42,6 @@ export default definePlugin({ replace: "$1(searchResults.length?searchResults:$2)" }, ] - }, - { - find: "UserProfileModalHeaderActionButtons", - replacement: [ - { - match: /(?<=return \i)\|\|(\i)===.*?.FRIEND/, - replace: (_, type) => `?null:${type} === 1|| ${type} === 2`, - }, - { - match: /(?<=\i.bot.{0,50}children:.*?onClose:)(\i)/, - replace: "() => {$1();$self.closeSettingsWindow()}", - } - ], - }, - { - find: ',["user"])', - replacement: { - match: /(?<=isIgnored:.*?,\[\i,\i]=\i.useState\()\i\|\|\i\|\|\i.*?]\);/, - replace: "false);" - }, - }, - - // If the users wishes to, they can disable the warning in all other places as well. - ...[ - "UserProfilePanelWrapper: currentUser cannot be undefined", - "UserProfilePopoutWrapper: currentUser cannot be undefined", - ].map(x => ({ - find: x, - replacement: { - match: /(?<=isIgnored:.*?,\[\i,\i]=\i.useState\()\i\|\|\i\|\|\i\)(?:;\i.useEffect.*?]\))?/, - replace: "false)", - }, - predicate: () => settings.store.hideBlockedWarning, - })), - - { - find: ".BLOCKED:return", - replacement: { - match: /(?<=\i.BLOCKED:return.{0,65}onClick:)\(\)=>\{(\i.\i.unblockUser\((\i).+?}\))/, - replace: "(event) => {$self.openConfirmationModal(event,()=>{$1}, $2)", - }, - predicate: () => settings.store.showUnblockConfirmationEverywhere, - }, - { - find: "#{intl::UNBLOCK}),", - replacement: { - match: /(?<=#{intl::UNBLOCK}.+?Click=)\(\)=>(\{.+?(\i.getRecipientId\(\))\)})/, - replace: "event => $self.openConfirmationModal(event, ()=>$1, $2)", - }, - predicate: () => settings.store.showUnblockConfirmationEverywhere, - }, - { - find: "#{intl::BLOCK}),action", - replacement: { - match: /(?<=id:"block".{0,100}action:\i\?)\(\)=>(\{.{0,25}unblockUser\((\i).{0,60}:void 0\)})/, - replace: "event => {$self.openConfirmationModal(event, ()=>$1,$2)}", - }, - predicate: () => settings.store.showUnblockConfirmationEverywhere, } ], renderSearchInput() { @@ -161,6 +64,19 @@ export default definePlugin({ }} value={value} >; }, + renderUser(userId: string, rest: any) { + return ( +
+ + {rest} +
+ ); + }, + getSearchResults() { + return !!lastSearch; + }, setUpdateFunc(e, setResults) { if (e.listType !== "blocked") return; updateFunc = setResults; @@ -173,72 +89,5 @@ export default definePlugin({ if (!user) return id === search; return id === search || user?.username?.toLowerCase()?.includes(search) || user?.globalName?.toLowerCase()?.includes(search); }) as string[]; - }, - closeSettingsWindow() { - FluxDispatcher.dispatch({ type: "LAYER_POP" }); - }, - openUserProfile(user: User) { - openUserProfile(user.id); - }, - generateButtons(props: { user: User, originalProps: ButtonProps, isBlocked: boolean; }) { - const { user, originalProps, isBlocked } = props; - - if (settings.store.unblockButtonDanger) originalProps.color = Button.Colors.RED; - - // TODO add extra unblock confirmation after the click + setting. - - if (settings.store.showUnblockConfirmation || settings.store.showUnblockConfirmationEverywhere) { - const originalOnClick = originalProps.onClick!; - originalProps.onClick = e => { - if (!isBlocked) return originalOnClick(e); - this.openConfirmationModal(e as unknown as MouseEvent, () => originalOnClick(e), user, true); - }; - } - - const unblockButton = ; - - if (!settings.store.addDmsButton) return unblockButton; - - const dmButton = this.openDMChannel(user)}>Show DMs; - - return
- {dmButton} - {unblockButton} -
; - }, - - openDMChannel(user: User) { - ChannelActions.openPrivateChannel(user.id); - this.closeSettingsWindow(); - return null; - }, - openConfirmationModal(event: MouseEvent, callback: () => any, user: User | string, isSettingsOrigin: boolean = false) { - if (event.shiftKey) return callback(); - - if (typeof user === "string") { - user = UserStore.getUser(user); - } - - return openModal(m => { - callback(); - }}> -
-
- {`Are you sure you want to unblock ${user?.username ?? "this user"}?`} - {`This will allow ${user?.username ?? "them"} to see your profile and message you again.`} -
- {"You can always block them again later."} - {isSettingsOrigin ?
- {"If you just want to read the chat logs instead, you can just click on their profile."} - {"Alternatively, you can enable a button to jump to DMs in the blocklist through the plugin settings."} -
: {"If you just want to read the chat logs, you can do this without unblocking them."}} -
-
); - }, + } }); diff --git a/src/equicordplugins/betterBlockedUsers/styles.css b/src/equicordplugins/betterBlockedUsers/styles.css new file mode 100644 index 00000000..810c6772 --- /dev/null +++ b/src/equicordplugins/betterBlockedUsers/styles.css @@ -0,0 +1,3 @@ +[class*="usersList_"] [class*="text_"] { + user-select: text !important; +} diff --git a/src/equicordplugins/bypassPinPrompt/index.ts b/src/equicordplugins/bypassPinPrompt/index.ts new file mode 100644 index 00000000..0d4ac7ed --- /dev/null +++ b/src/equicordplugins/bypassPinPrompt/index.ts @@ -0,0 +1,49 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2025 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { EquicordDevs } from "@utils/constants"; +import definePlugin from "@utils/types"; + +export default definePlugin({ + name: "BypassPinPrompt", + description: "Bypass the pin prompt when pinning messages", + authors: [EquicordDevs.thororen], + patches: [ + { + find: '"Channel Pins"', + replacement: { + match: /(?<=(\i\.\i\.unpinMessage\(\i,\i\.id\)):)\i\.\i\.confirmUnpin\(\i,\i\)/, + replace: "$1" + } + }, + { + find: 'source:"message-actions"', + replacement: [ + { + match: /(?<=(\i\.\i\.pinMessage\(\i,\i\.id\)):)\i\.\i\.confirmPin\(\i,\i\)/, + replace: "$1" + }, + { + match: /(?<=(\i\.\i\.unpinMessage\(\i,\i\.id\)):)\i\.\i\.confirmUnpin\(\i,\i\)/, + replace: "$1" + } + ] + }, + { + find: 'id:"pin"', + replacement: [ + { + match: /(?<=(\i\.\i\.pinMessage\(\i,\i\.id\)):)\i\.\i\.confirmPin\(\i,\i\)/, + replace: "$1" + }, + { + match: /(?<=(\i\.\i\.unpinMessage\(\i,\i\.id\)):)\i\.\i\.confirmUnpin\(\i,\i\)/, + replace: "$1" + } + ] + }, + ], +}); diff --git a/src/equicordplugins/channelTabs/components/ChannelTabsContainer.tsx b/src/equicordplugins/channelTabs/components/ChannelTabsContainer.tsx index 4fd26e76..20ad46e5 100644 --- a/src/equicordplugins/channelTabs/components/ChannelTabsContainer.tsx +++ b/src/equicordplugins/channelTabs/components/ChannelTabsContainer.tsx @@ -20,6 +20,8 @@ const PlusSmallIcon = findComponentByCodeLazy("0v-5h5a1"); const cl = classNameFactory("vc-channeltabs-"); +const isMac = navigator.platform.toLowerCase().startsWith("mac"); + export default function ChannelsTabsContainer(props: BasicChannelTabsProps) { const [userId, setUserId] = useState(""); const { showBookmarkBar, widerTabsAndBookmarks } = settings.use(["showBookmarkBar", "widerTabsAndBookmarks"]); @@ -66,6 +68,7 @@ export default function ChannelsTabsContainer(props: BasicChannelTabsProps) { className={cl("container")} ref={ref} onContextMenu={e => ContextMenuApi.openContextMenu(e, () => )} + style={{ marginTop: isMac ? "28px" : "0" }} >
{openedTabs.map((tab, i) => diff --git a/src/equicordplugins/channelTabs/index.tsx b/src/equicordplugins/channelTabs/index.tsx index f6895140..6c5aaf10 100644 --- a/src/equicordplugins/channelTabs/index.tsx +++ b/src/equicordplugins/channelTabs/index.tsx @@ -48,7 +48,7 @@ export default definePlugin({ { find: ".COLLECTIBLES_SHOP_FULLSCREEN))", replacement: { - match: /(\?void 0:(\i)\.channelId.{0,300}return)((.{0,15})"div",{.*?\])(\}\)\}\})/, + match: /(\?void 0:(\i)\.channelId.{0,500}return)((.{0,15})"div",{.*?\])(\}\)\}\})/, replace: "$1$4$self.render,{currentChannel:$2,children:$3})$5" } }, diff --git a/src/equicordplugins/clientSideBlock/index.tsx b/src/equicordplugins/clientSideBlock/index.tsx index e5993d72..279ef958 100644 --- a/src/equicordplugins/clientSideBlock/index.tsx +++ b/src/equicordplugins/clientSideBlock/index.tsx @@ -152,8 +152,8 @@ export default definePlugin({ { find: "._areActivitiesExperimentallyHidden=(", replacement: { - match: /BOOST_GEM_ICON\}\}\)\)\};/, - replace: "$&if($self.shouldHideUser(this.props.user.id, this.props.channel.id)) return null; " + match: /(?<=user:(\i),guildId:\i,channel:(\i).*?)BOOST_GEM_ICON.{0,10}\);/, + replace: "$&if($self.shouldHideUser($1.id, $2.id)) return null; " } }, // stop the role header from displaying if all users with that role are hidden (wip sorta) diff --git a/src/equicordplugins/clipsEnhancements/index.tsx b/src/equicordplugins/clipsEnhancements.discordDesktop/index.tsx similarity index 100% rename from src/equicordplugins/clipsEnhancements/index.tsx rename to src/equicordplugins/clipsEnhancements.discordDesktop/index.tsx diff --git a/src/equicordplugins/commandPalette/commands.tsx b/src/equicordplugins/commandPalette/commands.tsx index 3b78ec12..c324c90b 100644 --- a/src/equicordplugins/commandPalette/commands.tsx +++ b/src/equicordplugins/commandPalette/commands.tsx @@ -6,9 +6,10 @@ import { showNotification } from "@api/Notifications"; import { Settings } from "@api/Settings"; +import { copyToClipboard } from "@utils/clipboard"; import { relaunch, showItemInFolder } from "@utils/native"; import { checkForUpdates, getRepo } from "@utils/updater"; -import { Clipboard, GuildStore, NavigationRouter, SettingsRouter, Toasts } from "@webpack/common"; +import { GuildStore, NavigationRouter, SettingsRouter, Toasts } from "@webpack/common"; import gitHash from "~git-hash"; import gitRemote from "~git-remote"; @@ -89,7 +90,7 @@ export const actions: ButtonAction[] = [ const newUrl = url.replace(/(https?:\/\/)?([a-zA-Z0-9-]+)\.([a-zA-Z0-9-]+)/, "https://$2.$3"); const res = (await fetch(newUrl)); const text = await res.text(); - Clipboard.copy(text); + copyToClipboard(text); Toasts.show({ message: "Copied response to clipboard!", @@ -115,7 +116,7 @@ export const actions: ButtonAction[] = [ { id: "copyGitInfo", label: "Copy Git Info", callback: async () => { - Clipboard.copy(`gitHash: ${gitHash}\ngitRemote: ${gitRemote}`); + copyToClipboard(`gitHash: ${gitHash}\ngitRemote: ${gitRemote}`); Toasts.show({ message: "Copied git info to clipboard!", diff --git a/src/equicordplugins/copyProfileColors/index.tsx b/src/equicordplugins/copyProfileColors/index.tsx new file mode 100644 index 00000000..2a3ab604 --- /dev/null +++ b/src/equicordplugins/copyProfileColors/index.tsx @@ -0,0 +1,107 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2025 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { addContextMenuPatch, NavContextMenuPatchCallback, removeContextMenuPatch } from "@api/ContextMenu"; +import { copyToClipboard } from "@utils/clipboard"; +import { EquicordDevs } from "@utils/constants"; +import definePlugin from "@utils/types"; +import { Menu, Toasts, UserProfileStore } from "@webpack/common"; + +function getProfileColors(userId) { + try { + const profile = UserProfileStore.getUserProfile(userId); + + if (!profile || !profile.themeColors || profile.themeColors.length < 2) { + return null; + } + + const primaryColor = profile.themeColors[0].toString(16).padStart(6, "0"); + const secondaryColor = profile.themeColors[1].toString(16).padStart(6, "0"); + + return { primaryColor, secondaryColor }; + } catch (e) { + console.error("Failed to get profile colors:", e); + return null; + } +} + + +function copyProfileColors(userId) { + const colors = getProfileColors(userId); + + if (!colors) { + Toasts.show({ + type: Toasts.Type.FAILURE, + message: "No profile colors found!", + id: Toasts.genId() + }); + return; + } + + const { primaryColor, secondaryColor } = colors; + + // Formatting + const formattedColors = `Primary-color #${primaryColor}, Secondary-Color #${secondaryColor}`; + + try { + copyToClipboard(formattedColors); + Toasts.show({ + type: Toasts.Type.SUCCESS, + message: "Profile colors copied to clipboard!", + id: Toasts.genId() + }); + } catch (e) { + console.error("Failed to copy to clipboard:", e); + Toasts.show({ + type: Toasts.Type.FAILURE, + message: "Error copying profile colors!", + id: Toasts.genId() + }); + } +} + +export function ColorIcon() { + return ( + + + + ); +} +// spawn in the context menu +const userContextMenuPatch: NavContextMenuPatchCallback = (children, { user }) => { + if (!user) return; + children.push( + Copy Profile Colors} + action={() => copyProfileColors(user.id)} + /> + ); +}; + +export default definePlugin({ + name: "CopyProfileColors", + description: "A plugin to copy people's profile gradient colors to clipboard.", + authors: [EquicordDevs.Crxa, EquicordDevs.Cortex], // Cortex is here because he showed me how to add icons <3 + + start() { + addContextMenuPatch("user-context", userContextMenuPatch); + addContextMenuPatch("user-profile-actions", userContextMenuPatch); + }, + + stop() { + // bye bye menu options + removeContextMenuPatch("user-context", userContextMenuPatch); + removeContextMenuPatch("user-profile-actions", userContextMenuPatch); + } +}); diff --git a/src/equicordplugins/copyStickerLinks/index.tsx b/src/equicordplugins/copyStickerLinks/index.tsx new file mode 100644 index 00000000..d4d95841 --- /dev/null +++ b/src/equicordplugins/copyStickerLinks/index.tsx @@ -0,0 +1,135 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2025 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu"; +import { Devs } from "@utils/constants"; +import { copyWithToast } from "@utils/misc"; +import definePlugin from "@utils/types"; +import { findStoreLazy } from "@webpack"; +import { Menu, React } from "@webpack/common"; +import { Promisable } from "type-fest"; + +const StickersStore = findStoreLazy("StickersStore"); + +interface Sticker { + t: "Sticker"; + format_type: number; + id: string; + type: number; +} + +const StickerExt = ["png", "png", "json", "gif"] as const; + +function getUrl(data: Sticker) { + if (data.format_type === 4) + return `https:${window.GLOBAL_ENV.MEDIA_PROXY_ENDPOINT}/stickers/${data.id}.gif?size=4096&lossless=true`; + + return `https://${window.GLOBAL_ENV.CDN_HOST}/stickers/${data.id}.${StickerExt[data.format_type]}?size=4096&lossless=true`; +} + +function buildMenuItem(Sticker, fetchData: () => Promisable>) { + return ( + <> + + + { + const res = await fetchData(); + const data = { t: Sticker, ...res } as Sticker; + const url = getUrl(data[0]); + copyWithToast(url, "Link copied!"); + } + } + /> + + { + const res = await fetchData(); + const data = { t: Sticker, ...res } as Sticker; + const url = getUrl(data[0]); + VencordNative.native.openExternal(url); + } + } + /> + + ); +} + +function buildMenuExpression(Sticker, fetchData: () => Promisable>) { + return ( + <> + + { + const res = await fetchData(); + const data = { t: Sticker, ...res } as Sticker; + const url = getUrl(data); + copyWithToast(url, "Link copied!"); + } + } + /> + { + const res = await fetchData(); + const data = { t: Sticker, ...res } as Sticker; + const url = getUrl(data); + VencordNative.native.openExternal(url); + } + } + /> + + ); +} + +const messageContextMenuPatch: NavContextMenuPatchCallback = (children, props) => { + const { favoriteableId, favoriteableType } = props ?? {}; + if (!favoriteableId) return; + const menuItem = (() => { + const sticker = props.message.stickerItems.find(s => s.id === favoriteableId); + if (sticker?.format_type === 3) return; + switch (favoriteableType) { + case "sticker": + return buildMenuItem("Sticker", () => props.message.stickerItems); + } + })(); + + if (menuItem) + findGroupChildrenByChildId("devmode-copy-id", children, true)?.push(menuItem); +}; + +const expressionPickerPatch: NavContextMenuPatchCallback = (children, props: { target: HTMLElement; }) => { + const { id } = props?.target?.dataset ?? {}; + if (!id) return; + + if (!props.target.className?.includes("lottieCanvas")) { + const stickerCache = StickersStore.getStickerById(id); + if (stickerCache) { + children.push(buildMenuExpression("Sticker", () => stickerCache)); + } + } +}; + +export default definePlugin({ + name: "CopyStickerLinks", + description: "Adds the ability to copy and open sticker links to your browser", + authors: [Devs.Byeoon], + contextMenus: { + "message": messageContextMenuPatch, + "expression-picker": expressionPickerPatch + } +}); diff --git a/src/equicordplugins/copyUserMention/index.tsx b/src/equicordplugins/copyUserMention/index.tsx index 81423265..c8e03d8e 100644 --- a/src/equicordplugins/copyUserMention/index.tsx +++ b/src/equicordplugins/copyUserMention/index.tsx @@ -5,9 +5,10 @@ */ import { NavContextMenuPatchCallback } from "@api/ContextMenu"; +import { copyToClipboard } from "@utils/clipboard"; import { Devs, EquicordDevs } from "@utils/constants"; import definePlugin from "@utils/types"; -import { Clipboard, Menu } from "@webpack/common"; +import { Menu } from "@webpack/common"; import type { Channel, User } from "discord-types/general"; const MentionIcon = () => ( @@ -37,7 +38,7 @@ const UserContextMenuPatch: NavContextMenuPatchCallback = (children, { user }: U Clipboard.copy(`<@${user.id}>`)} + action={() => copyToClipboard(`<@${user.id}>`)} icon={MentionIcon} /> ); diff --git a/src/equicordplugins/customTimestamps/index.tsx b/src/equicordplugins/customTimestamps/index.tsx index 87930f6a..d90defe3 100644 --- a/src/equicordplugins/customTimestamps/index.tsx +++ b/src/equicordplugins/customTimestamps/index.tsx @@ -71,11 +71,11 @@ export default definePlugin({ find: "#{intl::MESSAGE_EDITED_TIMESTAMP_A11Y_LABEL}", replacement: [ { - match: /(?<=\i=null!=\i\?).{0,25}\((\i),"LT"\):\(0,\i\.\i\)\(\i,!0\)/, + match: /(?<=null!=\i\?).{0,25}\((\i),"LT"\):\(0,\i\.\i\)\(\i,!0\)/, replace: '$self.format($1,"compactFormat","[calendar]"):$self.format($1,"cozyFormat","LT")', }, { - match: /(?<=text:)\(0,\i.\i\)\((\i),"LLLL"\)(?=,)/, + match: /(?<=text:)\(\)=>\(0,\i.\i\)\((\i),"LLLL"\)(?=,)/, replace: '$self.format($1,"tooltipFormat","LLLL")', }, ] diff --git a/src/equicordplugins/customUserColors/index.tsx b/src/equicordplugins/customUserColors/index.tsx index 9898fe56..ad7e3567 100644 --- a/src/equicordplugins/customUserColors/index.tsx +++ b/src/equicordplugins/customUserColors/index.tsx @@ -62,12 +62,8 @@ const userContextMenuPatch: NavContextMenuPatchCallback = (children, { user }: { }; export function getCustomColorString(userId: string, withHash?: boolean): string | undefined { - if (!colors[userId] || !Settings.plugins.CustomUserColors.enabled) - return; - - if (withHash) - return `#${colors[userId]}`; - + if (!colors[userId] || !Settings.plugins.CustomUserColors.enabled) return; + if (withHash) return `#${colors[userId]}`; return colors[userId]; } @@ -98,21 +94,23 @@ export default definePlugin({ // this also affects name headers in chats outside of servers find: '="SYSTEM_TAG"', replacement: { - match: /\i.gradientClassName]\),style:/, - replace: "$&{color:$self.colorIfServer(arguments[0])},_style:" + // Override colorString with our custom color and disable gradients if applying the custom color. + match: /&&null!=\i\.secondaryColor,(?<=colorString:(\i).+?(\i)=.+?)/, + replace: (m, colorString, hasGradientColors) => `${m}` + + `vcCustomUserColorsDummy=[${colorString},${hasGradientColors}]=$self.getMessageColorsVariables(arguments[0],${hasGradientColors}),` }, predicate: () => !Settings.plugins.IrcColors.enabled }, { find: "PrivateChannel.renderAvatar", replacement: { - match: /(highlighted:\i,)/, + match: /(subText:\i\(\),)/, replace: "$1style:{color:`${$self.colorDMList(arguments[0])}`}," }, predicate: () => settings.store.dmList, }, { - find: "!1,wrapContent", + find: '"AvatarWithText"', replacement: [ { match: /(\}=\i)/, @@ -125,22 +123,40 @@ export default definePlugin({ ], predicate: () => settings.store.dmList, }, + { + find: '"Reply Chain Nudge")', + replacement: { + match: /(,color:)(\i),/, + replace: "$1$self.colorInReplyingTo(arguments[0]) ?? $2,", + }, + }, ], - colorDMList(a: any): string | undefined { - const userId = a?.user?.id; - if (!userId) return; - const colorString = getCustomColorString(userId, true); - if (colorString) return colorString; - return "inherit"; + getMessageColorsVariables(context: any, hasGradientColors: boolean) { + const colorString = this.colorIfServer(context); + const originalColorString = context?.author?.colorString; + + return [colorString, hasGradientColors && colorString === originalColorString]; }, - colorIfServer(a: any): string | undefined { - const roleColor = a.author?.colorString; + colorDMList(context: any): string | undefined { + const userId = context?.user?.id; + const colorString = getCustomColorString(userId, true); + return colorString ?? "inherit"; + }, - if (a?.channel?.guild_id && !settings.store.colorInServers) return roleColor; + colorIfServer(context: any): string | undefined { + const userId = context?.message?.author?.id; + const colorString = context?.author?.colorString; - const color = getCustomColorString(a.message.author.id, true); - return color ?? roleColor ?? undefined; - } + if (context?.channel?.guild_id && !settings.store.colorInServers) return colorString; + + const color = getCustomColorString(userId, true); + return color ?? colorString ?? undefined; + }, + + colorInReplyingTo(a: any) { + const { id } = a.reply.message.author; + return getCustomColorString(id, true); + }, }); diff --git a/src/equicordplugins/emojiDumper/index.tsx b/src/equicordplugins/emojiDumper/index.tsx deleted file mode 100644 index 9bc3f1c7..00000000 --- a/src/equicordplugins/emojiDumper/index.tsx +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Vencord, a Discord client mod - * Copyright (c) 2024 Vendicated and contributors - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu"; -import { Devs, EquicordDevs } from "@utils/constants"; -import definePlugin from "@utils/types"; -import { Menu } from "@webpack/common"; -import type { Guild } from "discord-types/general"; -import { zipSync } from "fflate"; - -const Patch: NavContextMenuPatchCallback = (children, { guild }: { guild: Guild; }) => { - // Assuming "privacy" is the correct ID for the group you want to modify. - const group = findGroupChildrenByChildId("privacy", children); - - if (group) { - group.push( - zipServerEmojis(guild)}> - ); - } -}; - -export default definePlugin({ - name: "EmojiDumper", - description: "Context menu to dump and download a server's emojis.", - authors: [EquicordDevs.Cortex, Devs.Samwich, EquicordDevs.Woosh], - contextMenus: { - "guild-context": Patch, - "guild-header-popout": Patch - } -}); - -async function zipServerEmojis(guild: Guild) { - - const emojis = Vencord.Webpack.Common.EmojiStore.getGuilds()[guild.id]?.emojis; - if (!emojis) { - return console.log("Server not found!"); - } - - const fetchEmojis = async e => { - const filename = e.id + (e.animated ? ".gif" : ".png"); - const emoji = await fetch("https://cdn.discordapp.com/emojis/" + filename + "?size=512&quality=lossless").then(res => res.blob()); - return { file: new Uint8Array(await emoji.arrayBuffer()), filename }; - }; - const emojiPromises = emojis.map(e => fetchEmojis(e)); - - Promise.all(emojiPromises) - .then(results => { - const emojis = zipSync(Object.fromEntries(results.map(({ file, filename }) => [filename, file]))); - const blob = new Blob([emojis], { type: "application/zip" }); - const link = document.createElement("a"); - link.href = URL.createObjectURL(blob); - link.download = `${guild.name}-emojis.zip`; - link.click(); - link.remove(); - }) - .catch(error => { - console.error(error); - }); -} diff --git a/src/equicordplugins/encryptcord/index.tsx b/src/equicordplugins/encryptcord/index.tsx index a125e777..1f260ca4 100644 --- a/src/equicordplugins/encryptcord/index.tsx +++ b/src/equicordplugins/encryptcord/index.tsx @@ -12,7 +12,7 @@ import { } from "@api/Commands"; import * as DataStore from "@api/DataStore"; import { addMessagePreSendListener, MessageSendListener, removeMessagePreSendListener } from "@api/MessageEvents"; -import { Devs } from "@utils/constants"; +import { Devs, EquicordDevs } from "@utils/constants"; import { sleep } from "@utils/misc"; import definePlugin from "@utils/types"; import { @@ -140,7 +140,7 @@ const ChatBarIcon: ChatBarButtonFactory = ({ isMainChat }) => { export default definePlugin({ name: "Encryptcord", description: "End-to-end encryption in Discord!", - authors: [Devs.Inbestigator], + authors: [Devs.Inbestigator, EquicordDevs.ItsAlex], patches: [ { find: "INTERACTION_APPLICATION_COMMAND_INVALID_VERSION", @@ -360,18 +360,28 @@ async function handleGroupData(groupData) { // Handle joining group async function handleJoin(senderId: string, senderKey: string, encryptcordGroupMembers: object) { - encryptcordGroupMembers[senderId] = { key: senderKey, parent: UserStore.getCurrentUser().id, child: null }; - encryptcordGroupMembers[UserStore.getCurrentUser().id].child = senderId; + const currentUserId = UserStore.getCurrentUser().id; + + if (!encryptcordGroupMembers[senderId]) { + encryptcordGroupMembers[senderId] = { key: senderKey, parent: currentUserId, child: null }; + } + + if (!encryptcordGroupMembers[currentUserId]) { + encryptcordGroupMembers[currentUserId] = { key: "", parent: null, child: null }; + } + encryptcordGroupMembers[currentUserId].child = senderId; + await DataStore.set("encryptcordGroupMembers", encryptcordGroupMembers); + const groupChannel = await DataStore.get("encryptcordChannelId"); + const newMember = await UserUtils.getUser(senderId).catch(() => null); if (!newMember) return; const membersData = {}; - Object.entries(encryptcordGroupMembers) - .forEach(([memberId, value]) => { - membersData[memberId] = value; - }); + Object.entries(encryptcordGroupMembers).forEach(([memberId, value]) => { + membersData[memberId] = value; + }); const membersDataString = JSON.stringify({ members: membersData, channel: groupChannel }); @@ -382,6 +392,7 @@ async function handleJoin(senderId: string, senderKey: string, encryptcordGroupM }); await Promise.all(dmPromises); + await MessageActions.receiveMessage(groupChannel, { ...await createMessage("", senderId, groupChannel, 7), components: [{ type: 1, diff --git a/src/equicordplugins/equicordCSS/css/betterauthapps.css b/src/equicordplugins/equicordCSS/css/betterauthapps.css deleted file mode 100644 index 8651840c..00000000 --- a/src/equicordplugins/equicordCSS/css/betterauthapps.css +++ /dev/null @@ -1 +0,0 @@ -@import url("https://dablulite.github.io/css-snippets/BetterAuthApps/import.css"); diff --git a/src/equicordplugins/equicordCSS/css/betterstatuspicker.css b/src/equicordplugins/equicordCSS/css/betterstatuspicker.css deleted file mode 100644 index f890b288..00000000 --- a/src/equicordplugins/equicordCSS/css/betterstatuspicker.css +++ /dev/null @@ -1 +0,0 @@ -@import url("https://dablulite.github.io/css-snippets/BetterStatusPicker/import.css"); diff --git a/src/equicordplugins/equicordCSS/css/discordicons.css b/src/equicordplugins/equicordCSS/css/discordicons.css deleted file mode 100644 index 3589a10d..00000000 --- a/src/equicordplugins/equicordCSS/css/discordicons.css +++ /dev/null @@ -1 +0,0 @@ -@import url("https://raw.githubusercontent.com/gold-me/DiscordIcons/master/DiscordIcons.theme.css"); diff --git a/src/equicordplugins/equicordCSS/css/gradientbuttons.css b/src/equicordplugins/equicordCSS/css/gradientbuttons.css deleted file mode 100644 index 900d64ea..00000000 --- a/src/equicordplugins/equicordCSS/css/gradientbuttons.css +++ /dev/null @@ -1,128 +0,0 @@ -/* stylelint-disable selector-class-pattern */ -/* stylelint-disable color-function-notation */ - -:root { - /* || Gradients */ - --gradient-special: 140deg, hsl(245deg, calc(var(--saturaton-factor, 1)*79%), 72%) 0%, hsl(287deg, calc(var(--saturaton-factor, 1)*80%), 70%) 100%; - --gradient-blurple: 140deg, hsl(235deg, calc(var(--saturation-factor, 1)*85%), 72%) 0%, hsl(235deg, calc(var(--saturation-factor, 1)*85%), 60%) 100%; - --gradient-green: 140deg, hsl(139deg, calc(var(--saturaton-factor, 1)*47%), 44%) 0%, hsl(139deg, calc(var(--saturaton-factor, 1)*66%), 24%) 100%; - --gradient-yellow: 140deg, hsl(38deg, calc(var(--saturaton-factor, 1)*96%), 54%) 0%, hsl(38deg, calc(var(--saturaton-factor, 1)*82%), 41%) 100%; - --gradient-red: 140deg, hsl(359deg, calc(var(--saturaton-factor, 1)*83%), 59%) 0%, hsl(359deg, calc(var(--saturaton-factor, 1)*54%), 37%) 100%; - --gradient-grey: 140deg, hsl(214deg, calc(var(--saturaton-factor, 1)*10%), 50%) 0%, hsl(216deg, calc(var(--saturaton-factor, 1)*11%), 26%) 100%; - - /* || Transitions */ - --button-transition: 0.1s linear; - --font-default: 500; - --font-hover: 525; - --fontsize-hover: 15px; - --transform-normal: scale(1); - --transform-hover: scale(1.15); - --button-transform-hover: scale(1.04); -} - -/* || Filled Buttons */ -.lookFilled-yCfaCM { - transform: var(--transform-normal); - transition: var(--button-transition); - background: var(--gradient); -} - -.lookFilled-yCfaCM:hover { - transform: var(--button-transform-hover); -} - -.lookFilled-yCfaCM[disabled] { - transform: none; -} - -.lookFilled-yCfaCM.colorBrand-I6CyqQ { - --gradient: linear-gradient(var(--gradient-blurple)); -} - -.lookFilled-yCfaCM.colorGreen-3y-Z79, -.lookFilled-yCfaCM.button_adcaac.buttonActive_adcaac { - --gradient: linear-gradient(var(--gradient-green)); -} - -.lookFilled-yCfaCM.colorYellow-Pgtmch { - --gradient: linear-gradient(var(--gradient-yellow)); -} - -.lookFilled-yCfaCM.colorRed-rQXKgM { - --gradient: linear-gradient(var(--gradient-red)); -} - -.lookFilled-yCfaCM.colorPrimary-2AuQVo, -.lookFilled-yCfaCM.colorGrey-2iAG-B, -.lookFilled-yCfaCM.buttonColor_adcaac { - --gradient: linear-gradient(var(--gradient-grey)); -} - -/* || Context Menus */ -.menu_d90b3d .item-1OdjEX:not(.hideInteraction-2jPGL_) { - font-weight: var(--font-default); - transition: var(--button-transition); -} - -.menu_d90b3d .item-1OdjEX:not(.hideInteraction-2jPGL_).focused-3qFvc8, -.menu_d90b3d .item-1OdjEX:not(.hideInteraction-2jPGL_):active { - font-size: var(--fontsize-hover); - font-weight: var(--font-hover); - background: var(--gradient); -} - -.menu_d90b3d .colorDefault-CDqZdO.focused-3qFvc8, -.menu_d90b3d .colorDefault-CDqZdO:active { - --gradient: linear-gradient(var(--gradient-blurple)); -} - -.menu_d90b3d .colorDanger-3n-KnP.focused-3qFvc8, -.menu_d90b3d .colorDanger-3n-KnP:active, -.menu_d90b3d #status-picker-dnd.focused-3qFvc8, -.menu_d90b3d #status-picker-dnd:active { - --gradient: linear-gradient(var(--gradient-red)); -} - -.menu_d90b3d .colorPremium-vwmYZQ.focused-3qFvc8, -.menu_d90b3d .colorPremium-vwmYZQ:active { - --gradient: linear-gradient(var(--gradient-special)); -} - -.menu_d90b3d #status-picker-online.focused-3qFvc8, -.menu_d90b3d #status-picker-online:active { - --gradient: linear-gradient(var(--gradient-green)); -} - -.menu_d90b3d #status-picker-idle.focused-3qFvc8, -.menu_d90b3d #status-picker-idle:active { - --gradient: linear-gradient(var(--gradient-yellow)); -} - -.menu_d90b3d #status-picker-invisible.focused-3qFvc8, -.menu_d90b3d #status-picker-invisible:active { - --gradient: linear-gradient(var(--gradient-grey)); -} - -/* || Message Actions */ -.wrapper_f7e168 .button_f7e168 { - background: var(--gradient); -} - -.wrapper_f7e168 .button_f7e168 img, -.wrapper_f7e168 .button_f7e168 svg { - transition: var(--button-transition); - transform: var(--transform-normal); -} - -.wrapper_f7e168 .button_f7e168:hover { - --gradient: linear-gradient(var(--gradient-blurple)); -} - -.wrapper_f7e168 .button_f7e168:hover svg { - transform: var(--transform-hover); - color: white; -} - -.wrapper_f7e168 .button_f7e168.dangerous_f7e168:hover { - --gradient: linear-gradient(var(--gradient-red)); -} diff --git a/src/equicordplugins/equicordCSS/css/main.min.css b/src/equicordplugins/equicordCSS/css/main.min.css deleted file mode 100644 index f52da4c2..00000000 --- a/src/equicordplugins/equicordCSS/css/main.min.css +++ /dev/null @@ -1,487 +0,0 @@ -/* stylelint-disable property-no-vendor-prefix */ -/* stylelint-disable selector-class-pattern */ - -:root { - --settingsicons: 1; - --si-size: 18px; - --si-gap: 14px; - --use-si: calc(var(--settingsicons, 1) / (var(--settingsicons, 1))); - --si-myaccount: url("https://minidiscordthemes.github.io/SettingsIcons/svg/myaccount.svg"); - --si-profilecustomization: url("https://minidiscordthemes.github.io/SettingsIcons/svg/profilecustomization.svg"); - --si-privacysafety: url("https://minidiscordthemes.github.io/SettingsIcons/svg/privacysafety.svg"); - --si-familycenter: url("https://minidiscordthemes.github.io/SettingsIcons/svg/familycenter.svg"); - --si-authorizedapps: url("https://minidiscordthemes.github.io/SettingsIcons/svg/authorizedapps.svg"); - --si-sessions: url("https://minidiscordthemes.github.io/SettingsIcons/svg/sessions.svg"); - --si-connections: url("https://minidiscordthemes.github.io/SettingsIcons/svg/connections.svg"); - --si-settingsclips: url("https://minidiscordthemes.github.io/SettingsIcons/svg/settingsclips.svg"); - --si-friendrequests: url("https://minidiscordthemes.github.io/SettingsIcons/svg/friendrequests.svg"); - --si-discordnitro: url("https://minidiscordthemes.github.io/SettingsIcons/svg/discordnitro.svg"); - --si-nitroserverboost: url("https://minidiscordthemes.github.io/SettingsIcons/svg/nitroserverboost.svg"); - --si-subscriptions: url("https://minidiscordthemes.github.io/SettingsIcons/svg/subscriptions.svg"); - --si-libraryinventory: url("https://minidiscordthemes.github.io/SettingsIcons/svg/libraryinventory.svg"); - --si-billing: url("https://minidiscordthemes.github.io/SettingsIcons/svg/billing.svg"); - --si-appearance: url("https://minidiscordthemes.github.io/SettingsIcons/svg/appearance.svg"); - --si-accessibility: url("https://minidiscordthemes.github.io/SettingsIcons/svg/accessibility.svg"); - --si-voicevideo: url("https://minidiscordthemes.github.io/SettingsIcons/svg/voicevideo.svg"); - --si-textimages: url("https://minidiscordthemes.github.io/SettingsIcons/svg/textimages.svg"); - --si-notifications: url("https://minidiscordthemes.github.io/SettingsIcons/svg/notifications.svg"); - --si-keybinds: url("https://minidiscordthemes.github.io/SettingsIcons/svg/keybinds.svg"); - --si-language: url("https://minidiscordthemes.github.io/SettingsIcons/svg/language.svg"); - --si-windows: url("https://minidiscordthemes.github.io/SettingsIcons/svg/windows.svg"); - --si-streamermode: url("https://minidiscordthemes.github.io/SettingsIcons/svg/streamermode.svg"); - --si-rtcspeedtest: url("https://minidiscordthemes.github.io/SettingsIcons/svg/rtcspeedtest.svg"); - --si-advanced: url("https://minidiscordthemes.github.io/SettingsIcons/svg/advanced.svg"); - --si-activityprivacy: url("https://minidiscordthemes.github.io/SettingsIcons/svg/activityprivacy.svg"); - --si-gameactivity: url("https://minidiscordthemes.github.io/SettingsIcons/svg/gameactivity.svg"); - --si-overlay: url("https://minidiscordthemes.github.io/SettingsIcons/svg/overlay.svg"); - --si-changelog: url("https://minidiscordthemes.github.io/SettingsIcons/svg/changelog.svg"); - --si-merchandise: url("https://minidiscordthemes.github.io/SettingsIcons/svg/merchandise.svg"); - --si-hypesquadonline: url("https://minidiscordthemes.github.io/SettingsIcons/svg/hypesquadonline.svg"); - --si-powermodesettings: url("https://minidiscordthemes.github.io/SettingsIcons/svg/powermodesettings.svg"); - --si-experiments: url("https://minidiscordthemes.github.io/SettingsIcons/svg/experiments.svg"); - --si-developeroptions: url("https://minidiscordthemes.github.io/SettingsIcons/svg/developeroptions.svg"); - --si-hotspotoptions: url("https://minidiscordthemes.github.io/SettingsIcons/svg/hotspotoptions.svg"); - --si-dismissiblecontentoptions: url("https://minidiscordthemes.github.io/SettingsIcons/svg/dismissiblecontentoptions.svg"); - --si-startuptimings: url("https://minidiscordthemes.github.io/SettingsIcons/svg/startuptimings.svg"); - --si-paymentflowmodals: url("https://minidiscordthemes.github.io/SettingsIcons/svg/paymentflowmodals.svg"); - --si-textplayground: url("https://minidiscordthemes.github.io/SettingsIcons/svg/textplayground.svg"); - --si-textcomponent: url("https://minidiscordthemes.github.io/SettingsIcons/svg/textcomponent.svg"); - --si-logout: url("https://minidiscordthemes.github.io/SettingsIcons/svg/logout.svg"); - --si-equicordsettings: url("https://minidiscordthemes.github.io/SettingsIcons/svg/vencordsettings.svg"); - --si-equicordplugins: url("https://minidiscordthemes.github.io/SettingsIcons/svg/vencordplugins.svg"); - --si-equicordthemes: url("https://minidiscordthemes.github.io/SettingsIcons/svg/vencordthemes.svg"); - --si-equicordupdater: url("https://minidiscordthemes.github.io/SettingsIcons/svg/vencordupdater.svg"); - --si-equicordcloud: url("https://minidiscordthemes.github.io/SettingsIcons/svg/vencordcloud.svg"); - --si-equicordsettingssync: url("https://minidiscordthemes.github.io/SettingsIcons/svg/vencordsettingssync.svg"); - --si-equicordpatchhelper: url("https://minidiscordthemes.github.io/SettingsIcons/svg/vencordpatchhelper.svg"); - --si-equibop: url("https://minidiscordthemes.github.io/SettingsIcons/svg/vesktop.svg"); - --si-vesktop: url("https://minidiscordthemes.github.io/SettingsIcons/svg/vesktop.svg"); - --si-overview: url("https://minidiscordthemes.github.io/SettingsIcons/svg/overview.svg"); - --si-roles: url("https://minidiscordthemes.github.io/SettingsIcons/svg/roles.svg"); - --si-emoji: url("https://minidiscordthemes.github.io/SettingsIcons/svg/emoji.svg"); - --si-stickers: url("https://minidiscordthemes.github.io/SettingsIcons/svg/stickers.svg"); - --si-soundboard: url("https://minidiscordthemes.github.io/SettingsIcons/svg/soundboard.svg"); - --si-widget: url("https://minidiscordthemes.github.io/SettingsIcons/svg/widget.svg"); - --si-guildtemplates: url("https://minidiscordthemes.github.io/SettingsIcons/svg/guildtemplates.svg"); - --si-vanityurl: url("https://minidiscordthemes.github.io/SettingsIcons/svg/vanityurl.svg"); - --si-integrations: url("https://minidiscordthemes.github.io/SettingsIcons/svg/integrations.svg"); - --si-appdirectory: url("https://minidiscordthemes.github.io/SettingsIcons/svg/appdirectory.svg"); - --si-safety: url("https://minidiscordthemes.github.io/SettingsIcons/svg/safety.svg"); - --si-auditlog: url("https://minidiscordthemes.github.io/SettingsIcons/svg/auditlog.svg"); - --si-bans: url("https://minidiscordthemes.github.io/SettingsIcons/svg/bans.svg"); - --si-community: url("https://minidiscordthemes.github.io/SettingsIcons/svg/community.svg"); - --si-onboarding: url("https://minidiscordthemes.github.io/SettingsIcons/svg/onboarding.svg"); - --si-analytics: url("https://minidiscordthemes.github.io/SettingsIcons/svg/analytics.svg"); - --si-partner: url("https://minidiscordthemes.github.io/SettingsIcons/svg/partner.svg"); - --si-discovery: url("https://minidiscordthemes.github.io/SettingsIcons/svg/discovery.svg"); - --si-rolesubscriptions: url("https://minidiscordthemes.github.io/SettingsIcons/svg/rolesubscriptions.svg"); - --si-guildpremium: url("https://minidiscordthemes.github.io/SettingsIcons/svg/guildpremium.svg"); - --si-members: url("https://minidiscordthemes.github.io/SettingsIcons/svg/members.svg"); - --si-instantinvites: url("https://minidiscordthemes.github.io/SettingsIcons/svg/instantinvites.svg"); - --si-delete: url("https://minidiscordthemes.github.io/SettingsIcons/svg/delete.svg"); - --si-permissions: url("https://minidiscordthemes.github.io/SettingsIcons/svg/permissions.svg"); - --si-default: url("https://minidiscordthemes.github.io/SettingsIcons/svg/default.svg"); -} - -.sidebarRegion_c25c6d { - flex-basis: calc(218px + var(--use-si)*(var(--si-size) + var(--si-gap))) !important -} - -.sidebar_c25c6d { - width: calc(218px + var(--use-si)*(var(--si-size) + var(--si-gap))) !important -} - -.sidebar_c25c6d :is(.item_a0 .icon_f7189e, .premiumLabel_ae3c77>svg, .premiumLabel_ae3c77 img, .tabBarItemContainer_e7c031>svg, .tabBarItemContainer_e7c031 img) { - transform: scaleX(calc(1 - var(--use-si))) -} - -.sidebar_c25c6d .side_a0 .item_a0 { - display: flex; - align-items: center -} - -.sidebar_c25c6d .side_a0 .item_a0::before { - content: ""; - flex: 0 0 auto; - width: calc(var(--use-si)*var(--si-size)); - height: calc(var(--use-si)*var(--si-size)); - margin-right: calc(var(--use-si)*var(--si-size)/2); - background: currentcolor; - z-index: 2; - -webkit-mask: var(--si-default) center/contain no-repeat; - mask: var(--si-default) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="My Account"]::before { - -webkit-mask: var(--si-myaccount) center/contain no-repeat; - mask: var(--si-myaccount) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Profile Customization"]::before { - -webkit-mask: var(--si-profilecustomization) center/contain no-repeat; - mask: var(--si-profilecustomization) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Privacy & Safety"]::before { - -webkit-mask: var(--si-privacysafety) center/contain no-repeat; - mask: var(--si-privacysafety) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Family Center"]::before { - -webkit-mask: var(--si-familycenter) center/contain no-repeat; - mask: var(--si-familycenter) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Authorized Apps"]::before { - -webkit-mask: var(--si-authorizedapps) center/contain no-repeat; - mask: var(--si-authorizedapps) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Sessions"]::before { - -webkit-mask: var(--si-sessions) center/contain no-repeat; - mask: var(--si-sessions) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Connections"]::before { - -webkit-mask: var(--si-connections) center/contain no-repeat; - mask: var(--si-connections) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Settings Clips"]::before { - -webkit-mask: var(--si-settingsclips) center/contain no-repeat; - mask: var(--si-settingsclips) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Friend Requests"]::before { - -webkit-mask: var(--si-friendrequests) center/contain no-repeat; - mask: var(--si-friendrequests) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Discord Nitro"]::before { - -webkit-mask: var(--si-discordnitro) center/contain no-repeat; - mask: var(--si-discordnitro) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Nitro Server Boost"]::before { - -webkit-mask: var(--si-nitroserverboost) center/contain no-repeat; - mask: var(--si-nitroserverboost) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Subscriptions"]::before { - -webkit-mask: var(--si-subscriptions) center/contain no-repeat; - mask: var(--si-subscriptions) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Library Inventory"]::before { - -webkit-mask: var(--si-libraryinventory) center/contain no-repeat; - mask: var(--si-libraryinventory) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Billing"]::before { - -webkit-mask: var(--si-billing) center/contain no-repeat; - mask: var(--si-billing) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Appearance"]::before { - -webkit-mask: var(--si-appearance) center/contain no-repeat; - mask: var(--si-appearance) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Accessibility"]::before { - -webkit-mask: var(--si-accessibility) center/contain no-repeat; - mask: var(--si-accessibility) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Voice & Video"]::before { - -webkit-mask: var(--si-voicevideo) center/contain no-repeat; - mask: var(--si-voicevideo) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Text & Images"]::before { - -webkit-mask: var(--si-textimages) center/contain no-repeat; - mask: var(--si-textimages) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Notifications"]::before { - -webkit-mask: var(--si-notifications) center/contain no-repeat; - mask: var(--si-notifications) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Keybinds"]::before { - -webkit-mask: var(--si-keybinds) center/contain no-repeat; - mask: var(--si-keybinds) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Language"]::before { - -webkit-mask: var(--si-language) center/contain no-repeat; - mask: var(--si-language) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Windows"]::before { - -webkit-mask: var(--si-windows) center/contain no-repeat; - mask: var(--si-windows) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Streamer Mode"]::before { - -webkit-mask: var(--si-streamermode) center/contain no-repeat; - mask: var(--si-streamermode) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="RTC Speed Test"]::before { - -webkit-mask: var(--si-rtcspeedtest) center/contain no-repeat; - mask: var(--si-rtcspeedtest) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Advanced"]::before { - -webkit-mask: var(--si-advanced) center/contain no-repeat; - mask: var(--si-advanced) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Activity Privacy"]::before { - -webkit-mask: var(--si-activityprivacy) center/contain no-repeat; - mask: var(--si-activityprivacy) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Game Activity"]::before { - -webkit-mask: var(--si-gameactivity) center/contain no-repeat; - mask: var(--si-gameactivity) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Overlay"]::before { - -webkit-mask: var(--si-overlay) center/contain no-repeat; - mask: var(--si-overlay) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="changelog"]::before { - -webkit-mask: var(--si-changelog) center/contain no-repeat; - mask: var(--si-changelog) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="merchandise"]::before { - -webkit-mask: var(--si-merchandise) center/contain no-repeat; - mask: var(--si-merchandise) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Hypesquad Online"]::before { - -webkit-mask: var(--si-hypesquadonline) center/contain no-repeat; - mask: var(--si-hypesquadonline) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Powermode Settings"]::before { - -webkit-mask: var(--si-powermodesettings) center/contain no-repeat; - mask: var(--si-powermodesettings) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Experiments"]::before { - -webkit-mask: var(--si-experiments) center/contain no-repeat; - mask: var(--si-experiments) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Developer Options"]::before { - -webkit-mask: var(--si-developeroptions) center/contain no-repeat; - mask: var(--si-developeroptions) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Hotspot Options"]::before { - -webkit-mask: var(--si-hotspotoptions) center/contain no-repeat; - mask: var(--si-hotspotoptions) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Dismissible Content Options"]::before { - -webkit-mask: var(--si-dismissiblecontentoptions) center/contain no-repeat; - mask: var(--si-dismissiblecontentoptions) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="StartupTimings"]::before { - -webkit-mask: var(--si-startuptimings) center/contain no-repeat; - mask: var(--si-startuptimings) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Payment Flow Modals"]::before { - -webkit-mask: var(--si-paymentflowmodals) center/contain no-repeat; - mask: var(--si-paymentflowmodals) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Text Playground"]::before { - -webkit-mask: var(--si-textplayground) center/contain no-repeat; - mask: var(--si-textplayground) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Text Component"]::before { - -webkit-mask: var(--si-textcomponent) center/contain no-repeat; - mask: var(--si-textcomponent) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="logout"]::before { - -webkit-mask: var(--si-logout) center/contain no-repeat; - mask: var(--si-logout) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="EquicordSettings"]::before { - -webkit-mask: var(--si-equicordsettings) center/contain no-repeat; - mask: var(--si-equicordsettings) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="EquicordPlugins"]::before { - -webkit-mask: var(--si-equicordplugins) center/contain no-repeat; - mask: var(--si-equicordplugins) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="EquicordThemes"]::before { - -webkit-mask: var(--si-equicordthemes) center/contain no-repeat; - mask: var(--si-equicordthemes) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="EquicordUpdater"]::before { - -webkit-mask: var(--si-equicordupdater) center/contain no-repeat; - mask: var(--si-equicordupdater) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="EquicordCloud"]::before { - -webkit-mask: var(--si-equicordcloud) center/contain no-repeat; - mask: var(--si-equicordcloud) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="EquicordSettingsSync"]::before { - -webkit-mask: var(--si-equicordsettingssync) center/contain no-repeat; - mask: var(--si-equicordsettingssync) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="EquicordPatchHelper"]::before { - -webkit-mask: var(--si-equicordpatchhelper) center/contain no-repeat; - mask: var(--si-equicordpatchhelper) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Equibop"]::before { - -webkit-mask: var(--si-equibop) center/contain no-repeat; - mask: var(--si-equibop) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="Vesktop"]::before { - -webkit-mask: var(--si-vesktop) center/contain no-repeat; - mask: var(--si-vesktop) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="OVERVIEW"]::before { - -webkit-mask: var(--si-overview) center/contain no-repeat; - mask: var(--si-overview) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="ROLES"]::before { - -webkit-mask: var(--si-roles) center/contain no-repeat; - mask: var(--si-roles) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="EMOJI"]::before { - -webkit-mask: var(--si-emoji) center/contain no-repeat; - mask: var(--si-emoji) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="STICKERS"]::before { - -webkit-mask: var(--si-stickers) center/contain no-repeat; - mask: var(--si-stickers) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="SOUNDBOARD"]::before { - -webkit-mask: var(--si-soundboard) center/contain no-repeat; - mask: var(--si-soundboard) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="WIDGET"]::before { - -webkit-mask: var(--si-widget) center/contain no-repeat; - mask: var(--si-widget) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="GUILD_TEMPLATES"]::before { - -webkit-mask: var(--si-guildtemplates) center/contain no-repeat; - mask: var(--si-guildtemplates) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="VANITY_URL"]::before { - -webkit-mask: var(--si-vanityurl) center/contain no-repeat; - mask: var(--si-vanityurl) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="INTEGRATIONS"]::before { - -webkit-mask: var(--si-integrations) center/contain no-repeat; - mask: var(--si-integrations) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="APP_DIRECTORY"]::before { - -webkit-mask: var(--si-appdirectory) center/contain no-repeat; - mask: var(--si-appdirectory) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="SAFETY"]::before { - -webkit-mask: var(--si-safety) center/contain no-repeat; - mask: var(--si-safety) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="AUDIT_LOG"]::before { - -webkit-mask: var(--si-auditlog) center/contain no-repeat; - mask: var(--si-auditlog) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="BANS"]::before { - -webkit-mask: var(--si-bans) center/contain no-repeat; - mask: var(--si-bans) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="COMMUNITY"]::before { - -webkit-mask: var(--si-community) center/contain no-repeat; - mask: var(--si-community) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="ONBOARDING"]::before { - -webkit-mask: var(--si-onboarding) center/contain no-repeat; - mask: var(--si-onboarding) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="ANALYTICS"]::before { - -webkit-mask: var(--si-analytics) center/contain no-repeat; - mask: var(--si-analytics) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="PARTNER"]::before { - -webkit-mask: var(--si-partner) center/contain no-repeat; - mask: var(--si-partner) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="DISCOVERY"]::before { - -webkit-mask: var(--si-discovery) center/contain no-repeat; - mask: var(--si-discovery) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="ROLE_SUBSCRIPTIONS"]::before { - -webkit-mask: var(--si-rolesubscriptions) center/contain no-repeat; - mask: var(--si-rolesubscriptions) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="GUILD_PREMIUM"]::before { - -webkit-mask: var(--si-guildpremium) center/contain no-repeat; - mask: var(--si-guildpremium) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="MEMBERS"]::before { - -webkit-mask: var(--si-members) center/contain no-repeat; - mask: var(--si-members) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="INSTANT_INVITES"]::before { - -webkit-mask: var(--si-instantinvites) center/contain no-repeat; - mask: var(--si-instantinvites) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="DELETE"]::before { - -webkit-mask: var(--si-delete) center/contain no-repeat; - mask: var(--si-delete) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0[data-tab-id="PERMISSIONS"]::before { - -webkit-mask: var(--si-permissions) center/contain no-repeat; - mask: var(--si-permissions) center/contain no-repeat -} - -.sidebar_c25c6d .side_a0 .item_a0>div { - flex: 1 1 auto -} diff --git a/src/equicordplugins/equicordCSS/css/nitrothemesfix.css b/src/equicordplugins/equicordCSS/css/nitrothemesfix.css deleted file mode 100644 index 87cd5810..00000000 --- a/src/equicordplugins/equicordCSS/css/nitrothemesfix.css +++ /dev/null @@ -1 +0,0 @@ -@import url("https://raw.githubusercontent.com/coolesding/snippets/main/import/fixnitrothemes.css"); diff --git a/src/equicordplugins/equicordCSS/css/settingsicons.css b/src/equicordplugins/equicordCSS/css/settingsicons.css deleted file mode 100644 index 5e0a8b7d..00000000 --- a/src/equicordplugins/equicordCSS/css/settingsicons.css +++ /dev/null @@ -1,9 +0,0 @@ -@import url("https://raw.githubusercontent.com/Equicord/Equicord/main/src/equicordplugins/equicordCSS/css/main.min.css"); - -/* https://github.com/MiniDiscordThemes/SettingsIcons#customisation */ - -:root { - --settingsicons: 1; - --si-size: 18px; - --si-gap: 14px; -} diff --git a/src/equicordplugins/equicordCSS/css/userreimagined.css b/src/equicordplugins/equicordCSS/css/userreimagined.css deleted file mode 100644 index fcfd98d1..00000000 --- a/src/equicordplugins/equicordCSS/css/userreimagined.css +++ /dev/null @@ -1 +0,0 @@ -@import url("https://dablulite.github.io/css-snippets/UserReimagined/import.css"); diff --git a/src/equicordplugins/equicordCSS/index.ts b/src/equicordplugins/equicordCSS/index.ts deleted file mode 100644 index 15dd4a7b..00000000 --- a/src/equicordplugins/equicordCSS/index.ts +++ /dev/null @@ -1,124 +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 . -*/ - -// Import required modules and components -import { definePluginSettings } from "@api/Settings"; -import { disableStyle, enableStyle } from "@api/Styles"; -import { EquicordDevs } from "@utils/constants"; -import definePlugin, { OptionType } from "@utils/types"; - -// Importing the style managed fixes on and off switch -import betterauthapps from "./css/betterauthapps.css?managed"; -import betterstatuspicker from "./css/betterstatuspicker.css?managed"; -import discordicons from "./css/discordicons.css?managed"; -import gradientbuttons from "./css/gradientbuttons.css?managed"; -import nitrothemesfix from "./css/nitrothemesfix.css?managed"; -import settingsicons from "./css/settingsicons.css?managed"; -import userreimagined from "./css/userreimagined.css?managed"; - -// Forcing restartNeeded: true to not overcomplicate the live update of the settings using FluxDispatcher and making it complex -const settings = definePluginSettings({ - betterAuthApps: { - type: OptionType.BOOLEAN, - description: "Enable Better Auth Apps CSS", - restartNeeded: true, - default: false - }, - betterStatusPicker: { - type: OptionType.BOOLEAN, - description: "Enable Better Status Picker CSS", - restartNeeded: true, - default: false - }, - discordicons: { - type: OptionType.BOOLEAN, - description: "Enable Discord Icons CSS", - restartNeeded: true, - default: false - }, - gradientButtons: { - type: OptionType.BOOLEAN, - description: "Enable Gradient Buttons CSS", - restartNeeded: true, - default: false - }, - nitroThemesFix: { - type: OptionType.BOOLEAN, - description: "Enable Fix Nitro Themes CSS", - restartNeeded: true, - default: false - }, - settingsIcons: { - type: OptionType.BOOLEAN, - description: "Enable Settings Icons CSS", - restartNeeded: true, - default: false - }, - userReimagined: { - type: OptionType.BOOLEAN, - description: "Enable User Reimagined CSS", - restartNeeded: true, - default: false - } -}); - -let settingsArray: Array = []; -let cssArray: Array = []; - -export default definePlugin({ - name: "EquicordCSS", - description: "CSS for Equicord users. You will need to look at the settings.", - authors: [EquicordDevs.thororen, EquicordDevs.Panniku], - dependencies: ["ThemeAttributes"], - settings, - start() { - - // Push variables to array to iterate on start() and stop() - settingsArray.push( - settings.store.betterAuthApps, - settings.store.betterStatusPicker, - settings.store.discordicons, - settings.store.gradientButtons, - settings.store.nitroThemesFix, - settings.store.settingsIcons, - settings.store.userReimagined - ); - cssArray.push( - betterauthapps, - betterstatuspicker, - discordicons, - gradientbuttons, - nitrothemesfix, - settingsicons, - userreimagined - ); - - settingsArray.forEach((s, i) => { - if (s) enableStyle(cssArray[i]); - }); - }, - stop() { - - settingsArray.forEach((s, i) => { - if (s) disableStyle(cssArray[i]); - }); - - settingsArray = []; - cssArray = []; - } -}); diff --git a/src/equicordplugins/equicordToolbox/index.tsx b/src/equicordplugins/equicordToolbox/index.tsx index 69ec427e..9326c189 100644 --- a/src/equicordplugins/equicordToolbox/index.tsx +++ b/src/equicordplugins/equicordToolbox/index.tsx @@ -24,7 +24,7 @@ import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import definePlugin from "@utils/types"; import { findComponentByCodeLazy } from "@webpack"; -import { Menu, Popout, useState } from "@webpack/common"; +import { Menu, Popout, useRef, useState } from "@webpack/common"; import type { ReactNode } from "react"; const HeaderBarIcon = findComponentByCodeLazy(".HEADER_BAR_BADGE_TOP:", '.iconBadge,"top"'); @@ -116,6 +116,7 @@ function VencordPopoutIcon() { } function VencordPopoutButton() { + const buttonRef = useRef(null); const [show, setShow] = useState(false); return ( @@ -126,10 +127,12 @@ function VencordPopoutButton() { animation={Popout.Animation.NONE} shouldShow={show} onRequestClose={() => setShow(false)} + targetElementRef={buttonRef} renderPopout={() => VencordPopout(() => setShow(false))} > {(_, { isShown }) => ( setShow(v => !v)} tooltip={isShown ? null : "Equicord Toolbox"} diff --git a/src/equicordplugins/exportContacts/index.tsx b/src/equicordplugins/exportContacts/index.tsx index a454e432..6a5df39f 100644 --- a/src/equicordplugins/exportContacts/index.tsx +++ b/src/equicordplugins/exportContacts/index.tsx @@ -19,9 +19,10 @@ import "./styles.css"; import ErrorBoundary from "@components/ErrorBoundary"; +import { copyToClipboard } from "@utils/clipboard"; import { EquicordDevs } from "@utils/constants"; import definePlugin from "@utils/types"; -import { Clipboard, Toasts } from "@webpack/common"; +import { Toasts } from "@webpack/common"; interface User { id: string; @@ -97,7 +98,7 @@ export default definePlugin({ copyContactToClipboard() { if (this.contactList) { - Clipboard.copy(JSON.stringify(this.contactList)); + copyToClipboard(JSON.stringify(this.contactList)); Toasts.show({ message: "Contacts copied to clipboard successfully.", type: Toasts.Type.SUCCESS, diff --git a/src/equicordplugins/fakeProfileThemesAndEffects/components/BuilderButton.tsx b/src/equicordplugins/fakeProfileThemesAndEffects/components/BuilderButton.tsx index 9da8049b..45ffe141 100644 --- a/src/equicordplugins/fakeProfileThemesAndEffects/components/BuilderButton.tsx +++ b/src/equicordplugins/fakeProfileThemesAndEffects/components/BuilderButton.tsx @@ -5,16 +5,17 @@ */ import { Text, Tooltip } from "@webpack/common"; -import type { ComponentProps } from "react"; +import type { ComponentProps, RefObject } from "react"; export interface BuilderButtonProps { label?: string | undefined; tooltip?: string | undefined; selectedStyle?: ComponentProps<"div">["style"]; buttonProps?: ComponentProps<"div"> | undefined; + buttonRef?: RefObject; } -export const BuilderButton = ({ label, tooltip, selectedStyle, buttonProps }: BuilderButtonProps) => ( +export const BuilderButton = ({ buttonRef, label, tooltip, selectedStyle, buttonProps }: BuilderButtonProps) => ( {tooltipProps => (
@@ -32,6 +33,7 @@ export const BuilderButton = ({ label, tooltip, selectedStyle, buttonProps }: Bu borderRadius: "4px", cursor: "pointer" }} + ref={buttonRef} > {!selectedStyle && ( void; } -export const BuilderColorButton = ({ label, color, setColor, suggestedColors }: BuilderColorButtonProps) => ( - ( - - )} - > - {popoutProps => { - const hexColor = color ? "#" + color.toString(16).padStart(6, "0") : undefined; - - return ( - ( + - ); - }} - -); + )} + > + {popoutProps => { + const hexColor = color ? "#" + color.toString(16).padStart(6, "0") : undefined; + + return ( + + ); + }} + + ); +} diff --git a/src/equicordplugins/fastDeleteChannels/index.tsx b/src/equicordplugins/fastDeleteChannels/index.tsx new file mode 100644 index 00000000..1facee66 --- /dev/null +++ b/src/equicordplugins/fastDeleteChannels/index.tsx @@ -0,0 +1,83 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2025 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { EquicordDevs } from "@utils/constants"; +import definePlugin from "@utils/types"; +import { Constants, PermissionsBits, PermissionStore, React, RestAPI, useCallback, useEffect, useState } from "@webpack/common"; + +const showIcon = () => { + const [show, setShow] = useState(false); + + const handleKeys = useCallback(e => { + const keysHeld = e.ctrlKey && e.altKey; + setShow(keysHeld); + }, []); + + useEffect(() => { + window.addEventListener("keydown", handleKeys); + window.addEventListener("keyup", handleKeys); + + return () => { + window.removeEventListener("keydown", handleKeys); + window.removeEventListener("keyup", handleKeys); + }; + }, [handleKeys]); + + return show; +}; + +export default definePlugin({ + name: "FastDeleteChannels", + description: "Adds a trash icon to delete channels when holding ctrl + alt", + authors: [EquicordDevs.thororen], + patches: [ + // TY TypingIndicator + { + find: "UNREAD_IMPORTANT:", + replacement: { + match: /\.name,{.{0,140}\.children.+?:null(?<=,channel:(\i).+?)/, + replace: "$&,$self.TrashIcon($1)" + } + }, + { + find: "M11 9H4C2.89543 9 2 8.10457 2 7V1C2 0.447715 1.55228 0 1 0C0.447715 0 0 0.447715 0 1V7C0 9.20914 1.79086 11 4 11H11C11.5523 11 12 10.5523 12 10C12 9.44771 11.5523 9 11 9Z", + replacement: { + match: /mentionsCount:\i.+?null(?<=channel:(\i).+?)/, + replace: "$&,$self.TrashIcon($1)" + } + } + ], + TrashIcon: channel => { + const show = showIcon(); + + if (!show || !PermissionStore.can(PermissionsBits.MANAGE_CHANNELS, channel)) return null; + + return ( + RestAPI.del({ url: Constants.Endpoints.CHANNEL(channel.id) })} + > + + + + + + ); + } +}); diff --git a/src/equicordplugins/findReply/index.tsx b/src/equicordplugins/findReply/index.tsx index 8f7e88c4..9ce27e2c 100644 --- a/src/equicordplugins/findReply/index.tsx +++ b/src/equicordplugins/findReply/index.tsx @@ -21,7 +21,7 @@ import { disableStyle, enableStyle } from "@api/Styles"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; import { findByPropsLazy } from "@webpack"; -import { ChannelStore, MessageStore, ReactDOM, Toasts } from "@webpack/common"; +import { ChannelStore, createRoot, MessageStore, Toasts } from "@webpack/common"; import Message from "discord-types/general/Message"; import { Root } from "react-dom/client"; @@ -101,7 +101,7 @@ export default definePlugin({ madeComponent = true; element = document.createElement("div"); document.querySelector("[class^=base_]")!.appendChild(element); - root = ReactDOM.createRoot(element); + root = createRoot(element); } root!.render(); } diff --git a/src/equicordplugins/fixFileExtensions/index.tsx b/src/equicordplugins/fixFileExtensions/index.tsx index adeef42b..9e3fd34d 100644 --- a/src/equicordplugins/fixFileExtensions/index.tsx +++ b/src/equicordplugins/fixFileExtensions/index.tsx @@ -34,11 +34,10 @@ export default definePlugin({ patches: [ // Taken from AnonymiseFileNames { - find: "instantBatchUpload:", + find: 'type:"UPLOAD_START"', replacement: { - match: /uploadFiles:(\i),/, - replace: - "uploadFiles:(...args)=>(args[0].uploads.forEach(f=>f.filename=$self.fixExt(f)),$1(...args)),", + match: /await \i\.uploadFiles\((\i),/, + replace: "$1.forEach($self.fixExt),$&" }, predicate: () => !Settings.plugins.AnonymiseFileNames.enabled, }, diff --git a/src/equicordplugins/forwardAnywhere/index.ts b/src/equicordplugins/forwardAnywhere/index.ts new file mode 100644 index 00000000..c2ca185f --- /dev/null +++ b/src/equicordplugins/forwardAnywhere/index.ts @@ -0,0 +1,53 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2025 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { definePluginSettings } from "@api/Settings"; +import { EquicordDevs } from "@utils/constants"; +import { sendMessage } from "@utils/discord"; +import definePlugin, { OptionType } from "@utils/types"; +import { Message } from "discord-types/general"; + +// Taken From Signature :) +const settings = definePluginSettings({ + forwardPreface: { + description: "What should forwarded from be prefaced with", + type: OptionType.SELECT, + options: [ + { label: ">", value: ">", default: true }, + { label: "-#", value: "-#" } + ] + } +}); + +export default definePlugin({ + name: "ForwardAnywhere", + description: "If a forward fails send it as a normal message also allows nsfw forwards", + authors: [EquicordDevs.thororen], + settings, + patches: [ + { + find: "#{intl::MESSAGE_FORWARDING_NSFW_NOT_ALLOWED}", + replacement: { + match: /if\((\i)\.isNSFW\(\)&&.{0,25}\)\)\)/, + replace: "if(false)", + } + }, + { + find: "#{intl::MESSAGE_ACTION_FORWARD_TO}", + replacement: { + match: /(?<=let (\i)=.{0,25}rejected.{0,25}\);)(?=.{0,25}message:(\i))/, + replace: "if ($1) return $self.sendForward($1,$2);", + } + }, + ], + sendForward(channels: any, message: Message) { + for (const c of channels) { + sendMessage(c.id, { + content: `${message.content}\n${settings.store.forwardPreface} Forwarded from <#${message.channel_id}>` + }); + } + } +}); diff --git a/src/equicordplugins/friendCodes/FriendCodesPanel.tsx b/src/equicordplugins/friendCodes/FriendCodesPanel.tsx index b1832579..10e071ab 100644 --- a/src/equicordplugins/friendCodes/FriendCodesPanel.tsx +++ b/src/equicordplugins/friendCodes/FriendCodesPanel.tsx @@ -6,8 +6,9 @@ import "./styles.css"; +import { copyToClipboard } from "@utils/clipboard"; import { findByPropsLazy } from "@webpack"; -import { Button, Clipboard, Flex, Forms, Parser, Text, useEffect, useState } from "@webpack/common"; +import { Button, Flex, Forms, Parser, Text, useEffect, useState } from "@webpack/common"; import { FriendInvite } from "./types"; @@ -51,7 +52,7 @@ function FriendInviteCard({ invite }: { invite: FriendInvite; }) { Clipboard.copy(`https://discord.gg/${invite.code}`)} + onClick={() => copyToClipboard(`https://discord.gg/${invite.code}`)} /> diff --git a/src/equicordplugins/friendCodes/index.tsx b/src/equicordplugins/friendCodes/index.tsx index 2b9c3300..b2d74c22 100644 --- a/src/equicordplugins/friendCodes/index.tsx +++ b/src/equicordplugins/friendCodes/index.tsx @@ -17,9 +17,10 @@ export default definePlugin({ { find: "#{intl::ADD_FRIEND})}),(", replacement: { - match: /\.Fragment[^]*?children:\[[^]*?}\)/, + match: /header,children:\[.*?\{\}\)/, replace: "$&,$self.FriendCodesPanel" - } + }, + noWarn: true, } ], diff --git a/src/equicordplugins/gifCollections/index.tsx b/src/equicordplugins/gifCollections/index.tsx index 33fff6ac..c6a09822 100644 --- a/src/equicordplugins/gifCollections/index.tsx +++ b/src/equicordplugins/gifCollections/index.tsx @@ -10,10 +10,11 @@ import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/Co import { DataStore } from "@api/index"; import { definePluginSettings } from "@api/Settings"; import { Flex } from "@components/Flex"; +import { copyToClipboard } from "@utils/clipboard"; import { Devs, EquicordDevs } from "@utils/constants"; import { ModalContent, ModalFooter, ModalHeader, ModalRoot, ModalSize, openModal } from "@utils/modal"; import definePlugin, { OptionType } from "@utils/types"; -import { Alerts, Button, Clipboard, ContextMenuApi, FluxDispatcher, Forms, Menu, React, showToast, TextInput, Toasts, useCallback, useState } from "@webpack/common"; +import { Alerts, Button, ContextMenuApi, FluxDispatcher, Forms, Menu, React, showToast, TextInput, Toasts, useCallback, useState } from "@webpack/common"; import { addToCollection, cache_collections, createCollection, DATA_COLLECTION_NAME, deleteCollection, fixPrefix, getCollections, getGifById, getItemCollectionNameFromId, moveGifToCollection, refreshCacheCollection, removeFromCollection, renameCollection } from "./utils/collectionManager"; import { getFormat } from "./utils/getFormat"; @@ -526,7 +527,7 @@ const RemoveItemContextMenu = ({ type, nameOrId, instance }) => ( action={() => { const gifInfo = getGifById(nameOrId); if (!gifInfo) return; - Clipboard.copy(gifInfo.url); + copyToClipboard(gifInfo.url); showToast("URL copied to clipboard", Toasts.Type.SUCCESS); }} /> diff --git a/src/equicordplugins/githubRepos/index.tsx b/src/equicordplugins/githubRepos/index.tsx index 7faa03c9..65eccb64 100644 --- a/src/equicordplugins/githubRepos/index.tsx +++ b/src/equicordplugins/githubRepos/index.tsx @@ -76,7 +76,7 @@ export default definePlugin({ } }, { - find: "action:\"PRESS_APP_CONNECTION\"", + find: "#{intl::CONNECTIONS}),scrollIntoView", replacement: { match: /(?<=user:(\i).{0,15}displayProfile:(\i).*?CONNECTIONS.{0,100}\}\)\}\))/, replace: ",$self.ProfilePopoutComponent({ user: $1, displayProfile: $2 })" diff --git a/src/equicordplugins/glide/index.tsx b/src/equicordplugins/glide/index.tsx index 5547133a..70ca5d4c 100644 --- a/src/equicordplugins/glide/index.tsx +++ b/src/equicordplugins/glide/index.tsx @@ -5,10 +5,11 @@ */ import { definePluginSettings, Settings } from "@api/Settings"; +import { copyToClipboard } from "@utils/clipboard"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType, StartAt } from "@utils/types"; import { findComponentByCodeLazy } from "@webpack"; -import { Button, Clipboard, Forms, TextInput, Toasts, useState } from "@webpack/common"; +import { Button, Forms, TextInput, Toasts, useState } from "@webpack/common"; import { darkenColorHex, generateRandomColorHex, saturateColorHex } from "./generateTheme"; import { themes } from "./themeDefinitions"; @@ -58,10 +59,7 @@ function copyPreset(name: string) { name: "${name}" } `; - if (Clipboard.SUPPORTS_COPY) { - Clipboard.copy(template); - } - + copyToClipboard(template); } function CopyPresetComponent() { @@ -229,9 +227,7 @@ export function ColorPick({ propertyname }: { propertyname: string; }) { function copyCSS() { - if (Clipboard.SUPPORTS_COPY) { - Clipboard.copy(getCSS(parseFontContent())); - } + copyToClipboard(getCSS(parseFontContent())); } function parseFontContent() { diff --git a/src/equicordplugins/guildPickerDumper/index.tsx b/src/equicordplugins/guildPickerDumper/index.tsx new file mode 100644 index 00000000..754ba808 --- /dev/null +++ b/src/equicordplugins/guildPickerDumper/index.tsx @@ -0,0 +1,75 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu"; +import { migratePluginSettings } from "@api/Settings"; +import { Devs, EquicordDevs } from "@utils/constants"; +import definePlugin from "@utils/types"; +import { EmojiStore, Menu, StickersStore } from "@webpack/common"; +import type { Guild } from "discord-types/general"; +import { zipSync } from "fflate"; + +const Patch: NavContextMenuPatchCallback = (children, { guild }: { guild: Guild; }) => { + // Assuming "privacy" is the correct ID for the group you want to modify. + const group = findGroupChildrenByChildId("privacy", children); + + if (group) { + group.push( + <> + zipGuildAssets(guild, "emojis")}> + zipGuildAssets(guild, "stickers")}> + + ); + } +}; + +async function zipGuildAssets(guild: Guild, type: "emojis" | "stickers") { + const isEmojis = type === "emojis"; + const items = isEmojis + ? EmojiStore.getGuilds()[guild.id]?.emojis + : StickersStore.getStickersByGuildId(guild.id); + + if (!items) { + return console.log("Server not found!"); + } + + const fetchAsset = async e => { + const ext = e.animated ? ".gif" : ".png"; + const filename = e.id + ext; + const url = isEmojis + ? `https://${window.GLOBAL_ENV.MEDIA_PROXY_ENDPOINT}/emojis/${filename}?size=512&quality=lossless` + : `https://${window.GLOBAL_ENV.MEDIA_PROXY_ENDPOINT}/stickers/${filename}?size=4096&lossless=true`; + + const response = await fetch(url); + const blob = await response.blob(); + return { file: new Uint8Array(await blob.arrayBuffer()), filename }; + }; + + const assetPromises = items.map(e => fetchAsset(e)); + + Promise.all(assetPromises) + .then(results => { + const zipped = zipSync(Object.fromEntries(results.map(({ file, filename }) => [filename, file]))); + const blob = new Blob([zipped], { type: "application/zip" }); + const link = document.createElement("a"); + link.href = URL.createObjectURL(blob); + link.download = `${guild.name}-${type}.zip`; + link.click(); + link.remove(); + }) + .catch(console.error); +} + +migratePluginSettings("GuildPickerDumper", "EmojiDumper"); +export default definePlugin({ + name: "GuildPickerDumper", + description: "Context menu to dump and download a server's emojis and stickers.", + authors: [EquicordDevs.Cortex, Devs.Samwich, EquicordDevs.Synth, EquicordDevs.thororen], + contextMenus: { + "guild-context": Patch, + "guild-header-popout": Patch + } +}); diff --git a/src/equicordplugins/hideServers/HiddenServersStore.ts b/src/equicordplugins/hideServers/HiddenServersStore.ts index bdad64fd..2f7a4f93 100644 --- a/src/equicordplugins/hideServers/HiddenServersStore.ts +++ b/src/equicordplugins/hideServers/HiddenServersStore.ts @@ -24,7 +24,7 @@ export const HiddenServersStore = proxyLazyWebpack(() => { // id try to use .initialize() but i dont know how it works public async load() { const data = await DataStore.get(DB_KEY); - if (data) { + if (data && data instanceof Set) { this._hiddenGuilds = data; } } diff --git a/src/equicordplugins/hideServers/index.tsx b/src/equicordplugins/hideServers/index.tsx index 0d8aded7..8c44ee7e 100644 --- a/src/equicordplugins/hideServers/index.tsx +++ b/src/equicordplugins/hideServers/index.tsx @@ -77,8 +77,8 @@ export default definePlugin({ find: '("guildsnav")', replacement: [ { - match: /(?<=#{intl::SERVERS}\),children:)(\i)(\)?\.map\(\i\))/g, - replace: "$self.useFilteredGuilds($1)$2", + match: /(?<=#{intl::SERVERS}\),gap:"xs",children:.{0,100}?)(\i)(\.map\(.{5,30}\}\))/, + replace: "$self.useFilteredGuilds($1)$2" }, // despite my best efforts, the above doesnt trigger a rerender { diff --git a/src/equicordplugins/holyNotes/components/modals/NoteBookTab.tsx b/src/equicordplugins/holyNotes/components/modals/NoteBookTab.tsx index f1c62dad..4dbdee51 100644 --- a/src/equicordplugins/holyNotes/components/modals/NoteBookTab.tsx +++ b/src/equicordplugins/holyNotes/components/modals/NoteBookTab.tsx @@ -6,7 +6,7 @@ import { classes } from "@utils/misc"; import { findByCode } from "@webpack"; -import { Button, Clickable, Menu, Popout, React } from "@webpack/common"; +import { Button, Clickable, Menu, Popout, React, useRef } from "@webpack/common"; import { SvgOverFlowIcon } from "../icons/overFlowIcon"; @@ -101,6 +101,8 @@ export function NoteBookTabs({ tabs, selectedTabId, onSelectTab }: { tabs: strin ); }, [tabs, selectedTabId, onSelectTab, overflowedTabs]); + const buttonRef = useRef(null); + return (
{props => ( + + ); }, moreStickersComponent({ diff --git a/src/equicordplugins/moreStickers/style.css b/src/equicordplugins/moreStickers/style.css index 6392044a..16101bf4 100644 --- a/src/equicordplugins/moreStickers/style.css +++ b/src/equicordplugins/moreStickers/style.css @@ -116,7 +116,7 @@ .vc-more-stickers-category-scroller, .vc-more-stickers-picker-content-scroller { - scrollbar-width: thin; + scrollbar-width: none; scrollbar-color: var(--scrollbar-thin-thumb) var(--scrollbar-thin-track); } diff --git a/src/equicordplugins/moreStickers/upload.ts b/src/equicordplugins/moreStickers/upload.ts index 82b5da1e..59ad2d09 100644 --- a/src/equicordplugins/moreStickers/upload.ts +++ b/src/equicordplugins/moreStickers/upload.ts @@ -11,8 +11,7 @@ import { ChannelStore, UploadHandler } from "@webpack/common"; import { FFmpegState, Sticker } from "./types"; - -const MessageUpload = findByPropsLazy("instantBatchUpload"); +const MessageUpload = findByPropsLazy("uploadFiles"); const CloudUpload = findLazy(m => m.prototype?.trackUploadFinished); const PendingReplyStore = findByPropsLazy("getPendingReply"); const MessageUtils = findByPropsLazy("sendMessage"); diff --git a/src/equicordplugins/moreStickers/utils.tsx b/src/equicordplugins/moreStickers/utils.tsx index f73a5e4c..0af5de35 100644 --- a/src/equicordplugins/moreStickers/utils.tsx +++ b/src/equicordplugins/moreStickers/utils.tsx @@ -14,7 +14,7 @@ import { FFmpegState } from "./types"; export const cl = classNameFactory("vc-more-stickers-"); export const clPicker = (className: string, ...args: any[]) => cl("picker-" + className, ...args); -const CORS_PROXY = "https://corsproxy.io?"; +const CORS_PROXY = "https://corsproxy.io/?url="; function corsUrl(url: string | URL) { return CORS_PROXY + encodeURIComponent(url.toString()); diff --git a/src/equicordplugins/moreUserTags/consts.ts b/src/equicordplugins/moreUserTags/consts.ts new file mode 100644 index 00000000..14ea3dd9 --- /dev/null +++ b/src/equicordplugins/moreUserTags/consts.ts @@ -0,0 +1,63 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2025 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { findByCodeLazy, findLazy } from "@webpack"; +import { GuildStore } from "@webpack/common"; +import { RC } from "@webpack/types"; +import { Channel, Guild, Message, User } from "discord-types/general"; + +import type { ITag } from "./types"; + +export const isWebhook = (message: Message, user: User) => !!message?.webhookId && user.isNonUserBot(); +export const tags = [ + { + name: "WEBHOOK", + displayName: "Webhook", + description: "Messages sent by webhooks", + condition: isWebhook + }, { + name: "OWNER", + displayName: "Owner", + description: "Owns the server", + condition: (_, user, channel) => GuildStore.getGuild(channel?.guild_id)?.ownerId === user.id + }, { + name: "ADMINISTRATOR", + displayName: "Admin", + description: "Has the administrator permission", + permissions: ["ADMINISTRATOR"] + }, { + name: "MODERATOR_STAFF", + displayName: "Staff", + description: "Can manage the server, channels or roles", + permissions: ["MANAGE_GUILD", "MANAGE_CHANNELS", "MANAGE_ROLES"] + }, { + name: "MODERATOR", + displayName: "Mod", + description: "Can manage messages or kick/ban people", + permissions: ["MANAGE_MESSAGES", "KICK_MEMBERS", "BAN_MEMBERS"] + }, { + name: "VOICE_MODERATOR", + displayName: "VC Mod", + description: "Can manage voice chats", + permissions: ["MOVE_MEMBERS", "MUTE_MEMBERS", "DEAFEN_MEMBERS"] + }, { + name: "CHAT_MODERATOR", + displayName: "Chat Mod", + description: "Can timeout people", + permissions: ["MODERATE_MEMBERS"] + } +] as const satisfies ITag[]; + +export const Tag = findLazy(m => m.Types?.[0] === "BOT") as RC<{ type?: number | null, className?: string, useRemSizes?: boolean; }> & { Types: Record; }; + +// PermissionStore.computePermissions will not work here since it only gets permissions for the current user +export const computePermissions: (options: { + user?: { id: string; } | string | null; + context?: Guild | Channel | null; + overwrites?: Channel["permissionOverwrites"] | null; + checkElevated?: boolean /* = true */; + excludeGuildPermissions?: boolean /* = false */; +}) => bigint = findByCodeLazy(".getCurrentUser()", ".computeLurkerPermissionsAllowList()"); diff --git a/src/equicordplugins/moreUserTags/index.tsx b/src/equicordplugins/moreUserTags/index.tsx new file mode 100644 index 00000000..b45dac04 --- /dev/null +++ b/src/equicordplugins/moreUserTags/index.tsx @@ -0,0 +1,185 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2025 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import "./styles.css"; + +import { classNameFactory } from "@api/Styles"; +import { Devs, EquicordDevs } from "@utils/constants"; +import { getCurrentChannel, getIntlMessage } from "@utils/discord"; +import definePlugin from "@utils/types"; +import { ChannelStore, GuildStore, PermissionsBits, SelectedChannelStore, UserStore } from "@webpack/common"; +import { Channel, Message, User } from "discord-types/general"; + +import { computePermissions, Tag, tags } from "./consts"; +import { settings } from "./settings"; +import { TagSettings } from "./types"; + +const cl = classNameFactory("vc-mut-"); + +const genTagTypes = () => { + let i = 100; + const obj = {}; + + for (const { name } of tags) { + obj[name] = ++i; + obj[i] = name; + } + + return obj; +}; + +export default definePlugin({ + name: "MoreUserTags", + description: "Adds tags for webhooks and moderative roles (owner, admin, etc.)", + authors: [Devs.Cyn, Devs.TheSun, Devs.RyanCaoDev, Devs.LordElias, Devs.AutumnVN, EquicordDevs.Hen], + dependencies: ["MemberListDecoratorsAPI", "NicknameIconsAPI", "MessageDecorationsAPI"], + settings, + patches: [ + // Make discord actually use our tags + { + find: ".STAFF_ONLY_DM:", + replacement: [ + { + match: /(?<=type:(\i).{10,1000}.REMIX.{10,100})default:(\i)=/, + replace: "default:$2=$self.getTagText($self.localTags[$1]);", + }, + { + match: /(?<=type:(\i).{10,1000}.REMIX.{10,100})\.BOT:(?=default:)/, + replace: "$&return null;", + predicate: () => settings.store.dontShowBotTag + }, + ], + } + ], + start() { + const tagSettings = settings.store.tagSettings || {} as TagSettings; + for (const tag of Object.values(tags)) { + tagSettings[tag.name] ??= { + showInChat: true, + showInNotChat: true, + text: tag.displayName + }; + } + + settings.store.tagSettings = tagSettings; + }, + localTags: genTagTypes(), + getChannelId() { + return SelectedChannelStore.getChannelId(); + }, + renderNicknameIcon(props) { + const tagId = this.getTag({ + user: UserStore.getUser(props.userId), + channel: ChannelStore.getChannel(this.getChannelId()), + channelId: this.getChannelId(), + isChat: false + }); + + return tagId && + ; + + }, + renderMessageDecoration(props) { + const tagId = this.getTag({ + message: props.message, + user: UserStore.getUser(props.message.author.id), + channelId: props.message.channel_id, + isChat: false + }); + + return tagId && + ; + }, + renderMemberListDecorator(props) { + const tagId = this.getTag({ + user: props.user, + channel: getCurrentChannel(), + channelId: this.getChannelId(), + isChat: false + }); + + return tagId && + ; + }, + + getTagText(tagName: string) { + if (!tagName) return getIntlMessage("APP_TAG"); + const tag = tags.find(({ name }) => tagName === name); + if (!tag) return tagName || getIntlMessage("APP_TAG"); + + return settings.store.tagSettings?.[tag.name]?.text || tag.displayName; + }, + + getTag({ + message, user, channelId, isChat, channel + }: { + message?: Message, + user?: User & { isClyde(): boolean; }, + channel?: Channel & { isForumPost(): boolean; isMediaPost(): boolean; }, + channelId?: string; + isChat?: boolean; + }): number | null { + const settings = this.settings.store; + + if (!user) return null; + if (isChat && user.id === "1") return null; + if (user.isClyde()) return null; + if (user.bot && settings.dontShowForBots) return null; + + channel ??= ChannelStore.getChannel(channelId!) as any; + if (!channel) return null; + + const perms = this.getPermissions(user, channel); + + for (const tag of tags) { + if (isChat && !settings.tagSettings[tag.name].showInChat) + continue; + if (!isChat && !settings.tagSettings[tag.name].showInNotChat) + continue; + + // If the owner tag is disabled, and the user is the owner of the guild, + // avoid adding other tags because the owner will always match the condition for them + if ( + (tag.name !== "OWNER" && + GuildStore.getGuild(channel?.guild_id)?.ownerId === + user.id && + isChat && + !settings.tagSettings.OWNER.showInChat) || + (!isChat && + !settings.tagSettings.OWNER.showInNotChat) + ) + continue; + + if ("permissions" in tag ? + tag.permissions.some(perm => perms.includes(perm)) : + tag.condition(message!, user, channel)) { + + return this.localTags[tag.name]; + } + } + + return null; + }, + getPermissions(user: User, channel: Channel): string[] { + const guild = GuildStore.getGuild(channel?.guild_id); + if (!guild) return []; + + const permissions = computePermissions({ user, context: guild, overwrites: channel.permissionOverwrites }); + return Object.entries(PermissionsBits) + .map(([perm, permInt]) => + permissions & permInt ? perm : "" + ) + .filter(Boolean); + }, +}); diff --git a/src/equicordplugins/moreUserTags/settings.tsx b/src/equicordplugins/moreUserTags/settings.tsx new file mode 100644 index 00000000..e3853571 --- /dev/null +++ b/src/equicordplugins/moreUserTags/settings.tsx @@ -0,0 +1,104 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2025 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { definePluginSettings } from "@api/Settings"; +import { Margins } from "@utils/margins"; +import { OptionType } from "@utils/types"; +import { Card, Flex, Forms, Switch, TextInput, Tooltip } from "@webpack/common"; + +import { Tag, tags } from "./consts"; +import { TagSettings } from "./types"; + +function SettingsComponent() { + const tagSettings = settings.store.tagSettings as TagSettings; + const { localTags } = Vencord.Plugins.plugins.MoreUserTags as any; + + return ( + +
+ {tags.map(t => ( + + + + {({ onMouseEnter, onMouseLeave }) => ( +
+ {t.displayName} Tag +
+ )} +
+
+ +
+ + Example: + + +
+ + tagSettings[t.name].text = v} + className={Margins.bottom16} + /> + + tagSettings[t.name].showInChat = v} + hideBorder + > + Show in messages + + + tagSettings[t.name].showInNotChat = v} + hideBorder + > + Show in member list and profiles + +
+ ))} +
+
+ ); +} + +export const settings = definePluginSettings({ + dontShowForBots: { + description: "Don't show extra tags for bots (excluding webhooks)", + type: OptionType.BOOLEAN, + default: false + }, + dontShowBotTag: { + description: "Only show extra tags for bots / Hide [APP] text", + type: OptionType.BOOLEAN, + default: false, + restartNeeded: true + }, + tagSettings: { + type: OptionType.COMPONENT, + component: SettingsComponent, + description: "fill me" + } +}); diff --git a/src/equicordplugins/moreUserTags/styles.css b/src/equicordplugins/moreUserTags/styles.css new file mode 100644 index 00000000..bf6b34d4 --- /dev/null +++ b/src/equicordplugins/moreUserTags/styles.css @@ -0,0 +1,27 @@ +.vc-message-decorations-wrapper .vc-mut-message-tag { + margin-bottom: 1px; +} + +/* stylelint-disable-next-line no-descending-specificity */ +.vc-mut-message-tag { + /* Remove default margin from tags in messages */ + margin-top: unset !important; + + /* Align with Discord default tags in messages */ + /* stylelint-disable-next-line length-zero-no-unit */ + bottom: 0px; + top: -2px; + margin-right: 3px; +} + +.vc-mut-message-verified { + height: 1rem !important; +} + +span[class*="botTagCozy"][data-moreTags-darkFg="true"]>svg>path { + fill: #000; +} + +span[class*="botTagCozy"][data-moreTags-darkFg="false"]>svg>path { + fill: #fff; +} diff --git a/src/equicordplugins/moreUserTags/types.ts b/src/equicordplugins/moreUserTags/types.ts new file mode 100644 index 00000000..40d8ac7d --- /dev/null +++ b/src/equicordplugins/moreUserTags/types.ts @@ -0,0 +1,32 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2025 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import type { Permissions } from "@webpack/types"; +import type { Channel, Message, User } from "discord-types/general"; + +import { tags } from "./consts"; + +export type ITag = { + // name used for identifying, must be alphanumeric + underscores + name: string; + // name shown on the tag itself, can be anything probably; automatically uppercase'd + displayName: string; + description: string; +} & ({ + permissions: Permissions[]; +} | { + condition?(message: Message | null, user: User, channel: Channel): boolean; +}); + +export interface TagSetting { + text: string; + showInChat: boolean; + showInNotChat: boolean; +} + +export type TagSettings = { + [k in typeof tags[number]["name"]]: TagSetting; +}; diff --git a/src/equicordplugins/morse/index.ts b/src/equicordplugins/morse/index.ts new file mode 100644 index 00000000..1bb7d75e --- /dev/null +++ b/src/equicordplugins/morse/index.ts @@ -0,0 +1,73 @@ +/* + * 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 { ApplicationCommandInputType, ApplicationCommandOptionType } from "@api/Commands"; +import { EquicordDevs } from "@utils/constants"; +import definePlugin from "@utils/types"; + +const morseMap = { + A: ".-", B: "-...", C: "-.-.", D: "-..", E: ".", F: "..-.", + G: "--.", H: "....", I: "..", J: ".---", K: "-.-", L: ".-..", + M: "--", N: "-.", O: "---", P: ".--.", Q: "--.-", R: ".-.", + S: "...", T: "-", U: "..-", V: "...-", W: ".--", X: "-..-", + Y: "-.--", Z: "--..", + 0: "-----", 1: ".----", 2: "..---", 3: "...--", 4: "....-", + 5: ".....", 6: "-....", 7: "--...", 8: "---..", 9: "----.", + " ": "/" +}; + +const toMorse = (text: string) => { + return text.toUpperCase().split("").map(char => morseMap[char] ?? "").join(" "); +}; + +const fromMorse = (text: string) => { + const reversedMap = Object.fromEntries(Object.entries(morseMap).map(([k, v]) => [v, k])); + const raw = text.split(" ").map(code => reversedMap[code] ?? "").join("").toLowerCase(); + return raw.charAt(0).toUpperCase() + raw.slice(1); +}; + +// boo regex +const isMorse = (text: string) => /^[.\-/ ]+$/.test(text); + +export default definePlugin({ + name: "Morse", + description: "A slash command to translate to/from morse code.", + authors: [EquicordDevs.zyqunix], + commands: [ + { + inputType: ApplicationCommandInputType.BUILT_IN_TEXT, + name: "morse", + description: "Translate to or from Morse code", + options: [ + { + name: "text", + description: "Text to convert", + type: ApplicationCommandOptionType.STRING, + required: true + } + ], + execute: opts => { + const input = opts.find(o => o.name === "text")?.value as string; + const output = isMorse(input) ? fromMorse(input) : toMorse(input); + return { + content: `${output}` + }; + }, + } + ] +}); diff --git a/src/equicordplugins/noOnboarding/index.tsx b/src/equicordplugins/noOnboarding/index.tsx new file mode 100644 index 00000000..f01c7127 --- /dev/null +++ b/src/equicordplugins/noOnboarding/index.tsx @@ -0,0 +1,62 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2025 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { Devs, EquicordDevs } from "@utils/constants"; +import definePlugin from "@utils/types"; +import { RestAPI } from "@webpack/common"; + +export default definePlugin({ + name: "NoOnboarding", + description: "Bypasses Discord's onboarding process for quicker server entry.", + authors: [EquicordDevs.omaw, Devs.Glitch], + patches: [ + { + find: ",acceptInvite(", + replacement: { + match: /INVITE_ACCEPT_SUCCESS.+?,(\i)=null!=.+?;/, + replace: (m, guildId) => `${m}$self.bypassOnboard(${guildId});` + } + }, + { + find: "{joinGuild:", + replacement: { + match: /guildId:(\i),lurker:(\i).{0,20}}\)\);/, + replace: (m, guildId, lurker) => `${m}if(!${lurker})$self.bypassOnboard(${guildId});` + } + } + ], + bypassOnboard(guild_id: string) { + RestAPI.get({ url: `/guilds/${guild_id}/onboarding` }).then(res => { + const data = res.body; + if (!data?.prompts?.length) return; + + const now = Math.floor(Date.now() / 1000); + const prompts_seen: Record = {}; + const responses_seen: Record = {}; + const responses: string[] = []; + + for (const prompt of data.prompts) { + const options = prompt.options || []; + if (!options.length) continue; + prompts_seen[prompt.id] = now; + for (const opt of options) responses_seen[opt.id] = now; + responses.push(options[options.length - 1].id); + } + + const payload = { + onboarding_responses: responses, + onboarding_prompts_seen: prompts_seen, + onboarding_responses_seen: responses_seen, + }; + + RestAPI.post({ + url: `/guilds/${guild_id}/onboarding-responses`, + body: payload + }).catch(() => { }); + }).catch(() => { }); + } +}); + diff --git a/src/equicordplugins/pinIcon/index.tsx b/src/equicordplugins/pinIcon/index.tsx index 8ae671df..f97869f4 100644 --- a/src/equicordplugins/pinIcon/index.tsx +++ b/src/equicordplugins/pinIcon/index.tsx @@ -28,9 +28,9 @@ export default definePlugin({ authors: [EquicordDevs.iamme], patches: [ { - find: "#{intl::MESSAGE_EDITED}),", + find: "isUnsupported})", replacement: { - match: /#{intl::MESSAGE_EDITED}\),(?:[^}]*[}]){3}\)/, + match: /WITH_CONTENT\}\)/, replace: "$&,$self.PinnedIcon(arguments[0].message)" } } diff --git a/src/equicordplugins/purgeMessages/index.tsx b/src/equicordplugins/purgeMessages/index.tsx index 425754fb..03269bf8 100644 --- a/src/equicordplugins/purgeMessages/index.tsx +++ b/src/equicordplugins/purgeMessages/index.tsx @@ -21,12 +21,9 @@ import "@equicordplugins/_misc/styles.css"; import { ApplicationCommandInputType, ApplicationCommandOptionType, findOption, sendBotMessage } from "@api/Commands"; import { Devs, EquicordDevs } from "@utils/constants"; import definePlugin from "@utils/types"; -import { findByPropsLazy } from "@webpack"; -import { Forms, MessageStore, UserStore } from "@webpack/common"; +import { Forms, MessageActions, MessageStore, UserStore } from "@webpack/common"; import { Channel, Message } from "discord-types/general"; -const MessageActions = findByPropsLazy("deleteMessage", "startEditMessage"); - async function deleteMessages(amount: number, channel: Channel, delay: number = 1500): Promise { let deleted = 0; const userId = UserStore.getCurrentUser().id; diff --git a/src/equicordplugins/questCompleter/index.tsx b/src/equicordplugins/questCompleter.discordDesktop/index.tsx similarity index 71% rename from src/equicordplugins/questCompleter/index.tsx rename to src/equicordplugins/questCompleter.discordDesktop/index.tsx index 15a91eba..19a13533 100644 --- a/src/equicordplugins/questCompleter/index.tsx +++ b/src/equicordplugins/questCompleter.discordDesktop/index.tsx @@ -17,38 +17,16 @@ */ import "@equicordplugins/_misc/styles.css"; - -import { showNotification } from "@api/Notifications"; -import ErrorBoundary from "@components/ErrorBoundary"; -import { Devs } from "@utils/constants"; -import { getTheme, Theme } from "@utils/discord"; -import definePlugin, { OptionType } from "@utils/types"; -import { findByProps, findComponentByCodeLazy } from "@webpack"; -import { Button, FluxDispatcher, Forms, NavigationRouter, RestAPI, Tooltip, UserStore } from "@webpack/common"; - -const HeaderBarIcon = findComponentByCodeLazy(".HEADER_BAR_BADGE_TOP:", '.iconBadge,"top"'); -const isApp = navigator.userAgent.includes("Electron/"); - import "./style.css"; -import { definePluginSettings } from "@api/Settings"; +import { showNotification } from "@api/Notifications"; +import { Devs } from "@utils/constants"; +import { getTheme, Theme } from "@utils/discord"; +import definePlugin from "@utils/types"; +import { findByProps, findComponentByCodeLazy } from "@webpack"; +import { Button, ChannelStore, FluxDispatcher, GuildChannelStore, NavigationRouter, RestAPI, Tooltip, UserStore } from "@webpack/common"; -const ToolBarQuestsIcon = findComponentByCodeLazy("1 0 1 1.73Z"); - -function ToolBarHeader() { - return ( - - - - - ); -} +const QuestIcon = findComponentByCodeLazy("10.47a.76.76"); async function openCompleteQuestUI() { const ApplicationStreamingStore = findByProps("getStreamerActiveStreamMetadata"); @@ -72,7 +50,7 @@ async function openCompleteQuestUI() { const applicationId = quest.config.application.id; const applicationName = quest.config.application.name; - const taskName = ["WATCH_VIDEO", "PLAY_ON_DESKTOP", "STREAM_ON_DESKTOP"].find(x => quest.config.taskConfig.tasks[x] != null); + const taskName = ["WATCH_VIDEO", "PLAY_ON_DESKTOP", "STREAM_ON_DESKTOP", "PLAY_ACTIVITY"].find(x => quest.config.taskConfig.tasks[x] != null); // @ts-ignore const secondsNeeded = quest.config.taskConfig.tasks[taskName].target; // @ts-ignore @@ -108,18 +86,9 @@ async function openCompleteQuestUI() { }); console.log(`Spoofing video for ${applicationName}.`); } else if (taskName === "PLAY_ON_DESKTOP") { - if (!isApp) { - showNotification({ - title: `${applicationName} - Quest Completer`, - body: `${applicationName}'s quest requires the desktop app.`, - icon: icon, - }); - } RestAPI.get({ url: `/applications/public?application_ids=${applicationId}` }).then(res => { const appData = res.body[0]; const exeName = appData.executables.find(x => x.os === "win32").name.replace(">", ""); - - const games = RunningGameStore.getRunningGames(); const fakeGame = { cmdLine: `C:\\Program Files\\${appData.name}\\${exeName}`, exeName, @@ -133,8 +102,15 @@ async function openCompleteQuestUI() { processName: appData.name, start: Date.now(), }; - games.push(fakeGame); - FluxDispatcher.dispatch({ type: "RUNNING_GAMES_CHANGE", removed: [], added: [fakeGame], games: games }); + const realGames = RunningGameStore.getRunningGames(); + const fakeGames = [fakeGame]; + const realGetRunningGames = RunningGameStore.getRunningGames; + const realGetGameForPID = RunningGameStore.getGameForPID; + RunningGameStore.getRunningGames = () => fakeGames; + RunningGameStore.getGameForPID = pid => fakeGames.find(x => x.pid === pid); + FluxDispatcher.dispatch({ + type: "RUNNING_GAMES_CHANGE", removed: realGames, added: [fakeGame], games: fakeGames + }); const fn = data => { const progress = quest.config.configVersion === 1 ? data.userStatus.streamProgressSeconds : Math.floor(data.userStatus.progress.PLAY_ON_DESKTOP.value); @@ -151,11 +127,9 @@ async function openCompleteQuestUI() { icon: icon, }); - const idx = games.indexOf(fakeGame); - if (idx > -1) { - games.splice(idx, 1); - FluxDispatcher.dispatch({ type: "RUNNING_GAMES_CHANGE", removed: [fakeGame], added: [], games: [] }); - } + RunningGameStore.getRunningGames = realGetRunningGames; + RunningGameStore.getGameForPID = realGetGameForPID; + FluxDispatcher.dispatch({ type: "RUNNING_GAMES_CHANGE", removed: [fakeGame], added: [], games: [] }); FluxDispatcher.unsubscribe("QUESTS_SEND_HEARTBEAT_SUCCESS", fn); } }; @@ -163,13 +137,6 @@ async function openCompleteQuestUI() { console.log(`Spoofed your game to ${applicationName}.`); }); } else if (taskName === "STREAM_ON_DESKTOP") { - if (!isApp) { - showNotification({ - title: `${applicationName} - Quest Completer`, - body: `${applicationName}'s quest requires the desktop app.`, - icon: icon, - }); - } const stream = ApplicationStreamingStore.getAnyStreamForUser(UserStore.getCurrentUser()?.id); if (!stream) { showNotification({ @@ -211,84 +178,68 @@ async function openCompleteQuestUI() { }; FluxDispatcher.subscribe("QUESTS_SEND_HEARTBEAT_SUCCESS", fn); console.log(`Spoofed your stream to ${applicationName}.`); + } else if (taskName === "PLAY_ACTIVITY") { + const channelId = ChannelStore.getSortedPrivateChannels()[0]?.id ?? Object.values(GuildChannelStore.getAllGuilds() as any[]).find(x => x != null && x.VOCAL.length > 0).VOCAL[0].channel.id; + const streamKey = `call:${channelId}:1`; + + const fn = async () => { + + while (true) { + const res = await RestAPI.post({ url: `/quests/${quest.id}/heartbeat`, body: { stream_key: streamKey, terminal: false } }); + const progress = res.body.progress.PLAY_ACTIVITY.value; + showNotification({ + title: `${applicationName} - Quest Completer`, + body: `Current progress: ${progress}/${secondsNeeded} seconds.`, + icon: icon, + }); + + await new Promise(resolve => setTimeout(resolve, 20 * 1000)); + + if (progress >= secondsNeeded) { + await RestAPI.post({ url: `/quests/${quest.id}/heartbeat`, body: { stream_key: streamKey, terminal: true } }); + break; + } + } + + showNotification({ + title: `${applicationName} - Quest Completer`, + body: "Quest Completed.", + icon: icon, + }); + }; + fn(); } return; } } -const settings = definePluginSettings({ - clickableQuestDiscovery: { - type: OptionType.BOOLEAN, - description: "Makes the quest button in discovery clickable", - restartNeeded: true, - default: false - } -}); - export default definePlugin({ name: "QuestCompleter", description: "A plugin to complete quests without having the game installed.", authors: [Devs.amia], - settingsAboutComponent: () => <> - - Game Quests do not work on Equibop/Web Platforms. Only Video Quests do. - - , - settings, patches: [ { - find: "\"invite-button\"", + find: "AppTitleBar", replacement: { - match: /\i&&(\i\i\.push).{0,50}"current-speaker"/, - replace: "$1($self.renderQuestButton()),$&" + match: /(?<=trailing:.{0,70}\(\i\.Fragment,{children:\[)/, + replace: "$self.renderQuestButton()," } - }, - { - find: "toolbar:function", - replacement: { - match: /(function \i\(\i\){)(.{1,500}toolbar.{1,500}mobileToolbar)/, - replace: "$1$self.toolbarAction(arguments[0]);$2" - } - }, - { - find: "M7.5 21.7a8.95 8.95 0 0 1 9 0 1 1 0 0 0 1-1.73c", - replacement: { - match: /(?<=className:\i\}\))/, - replace: ",onClick:()=>$self.openCompleteQuestUI()" - }, - predicate: () => settings.store.clickableQuestDiscovery } ], renderQuestButton() { return ( {tooltipProps => ( - )} ); }, - openCompleteQuestUI, - toolbarAction(e) { - if (Array.isArray(e.toolbar)) - return e.toolbar.push( - - - - ); - - e.toolbar = [ - - - , - e.toolbar, - ]; - } }); diff --git a/src/equicordplugins/questCompleter.discordDesktop/style.css b/src/equicordplugins/questCompleter.discordDesktop/style.css new file mode 100644 index 00000000..cccdaa87 --- /dev/null +++ b/src/equicordplugins/questCompleter.discordDesktop/style.css @@ -0,0 +1,12 @@ +.vc-quest-completer-icon { + bottom: -2px; + padding: 0; + width: var(--space-32); + min-width: 0; + height: var(--space-32); + color: var(--interactive-normal) !important; + + &:hover{ + filter: brightness(2); + } +} diff --git a/src/equicordplugins/questCompleter/style.css b/src/equicordplugins/questCompleter/style.css deleted file mode 100644 index 5e4f7b7b..00000000 --- a/src/equicordplugins/questCompleter/style.css +++ /dev/null @@ -1,3 +0,0 @@ -.vc-quest-completer-icon:hover{ - filter: brightness(10); -} diff --git a/src/equicordplugins/quoter/index.tsx b/src/equicordplugins/quoter/index.tsx index d2120bfb..3c2770f6 100644 --- a/src/equicordplugins/quoter/index.tsx +++ b/src/equicordplugins/quoter/index.tsx @@ -10,7 +10,7 @@ import { Devs } from "@utils/constants"; import { getCurrentChannel } from "@utils/discord"; import { ModalCloseButton, ModalContent, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal"; import definePlugin, { OptionType } from "@utils/types"; -import { Button, Menu, Select, Switch, Text, TextInput, UploadHandler, useEffect, UserStore, useState } from "@webpack/common"; +import { Button, Menu, Select, Switch, Text, UploadHandler, useEffect, useState } from "@webpack/common"; import { Message } from "discord-types/general"; import { QuoteIcon } from "./components"; @@ -49,7 +49,6 @@ let recentmessage: Message; let grayscale; let setStyle: ImageStyle = ImageStyle.inspirational; let customMessage: string = ""; -let isUserCustomCapable = false; enum userIDOptions { displayName, @@ -89,14 +88,7 @@ const preparingSentence: string[] = []; const lines: string[] = []; async function createQuoteImage(avatarUrl: string, quoteOld: string, grayScale: boolean): Promise { - let quote; - - if (isUserCustomCapable && customMessage.length > 0) { - quote = FixUpQuote(customMessage); - } - else { - quote = FixUpQuote(quoteOld); - } + const quote = FixUpQuote(quoteOld); const canvas = document.createElement("canvas"); const ctx = canvas.getContext("2d"); @@ -141,7 +133,7 @@ async function createQuoteImage(avatarUrl: string, quoteOld: string, grayScale: ctx.fillRect(0, 0, canvas.width, canvas.height); const avatarBlob = await fetchImageAsBlob(avatarUrl); - const fadeBlob = await fetchImageAsBlob("https://github.com/Equicord/Equibored/raw/main/misc/quoter.png"); + const fadeBlob = await fetchImageAsBlob("https://raw.githubusercontent.com/Equicord/Equibored/refs/heads/main/icons/quoter/quoter.png"); const avatar = new Image(); const fade = new Image(); @@ -194,13 +186,7 @@ function registerStyleChange(style) { GeneratePreview(); } -async function setIsUserCustomCapable() { - const allowList: string[] = await fetch("https://equicord.org/quoter").then(e => e.json()); // Override for memes - IF THIS IS ABUSED WILL WE TAKEN AWAY - isUserCustomCapable = allowList.includes(UserStore.getCurrentUser().id); -} - function QuoteModal(props: ModalProps) { - setIsUserCustomCapable(); const [gray, setGray] = useState(true); useEffect(() => { grayscale = gray; @@ -226,13 +212,6 @@ function QuoteModal(props: ModalProps) {



- {isUserCustomCapable && - ( - <> - -
- - )} Grayscale