Compare commits

...

38 Commits

Author SHA1 Message Date
V
a8b6aea26f bump to 1.2.1 2023-05-11 02:39:59 +02:00
V
e50c2fafa5 Settings > Plugins: Hide API plugins 2023-05-11 02:38:59 +02:00
V
6e3cafce42 Translate: Only add button in chat box 2023-05-11 02:35:06 +02:00
V
4d0a064425 WebContextMenus: Fix slate menu 2023-05-11 02:35:05 +02:00
V
d1ad6c47a7 Fix ServerListAPI 2023-05-11 02:35:05 +02:00
V
d5c35055f3 experiments: Add a warning card to experiments page (#1097) 2023-05-11 02:34:39 +02:00
V
cb385d1b28 New Plugin: Translate (#1089)
Co-authored-by: Nuckyz <61953774+Nuckyz@users.noreply.github.com>
2023-05-10 23:14:04 +02:00
V
195f1a032f ShowConnections: Add verified & copy/link icons in tooltip (#1092) 2023-05-10 23:13:47 +02:00
V
dfda9e7f84 New plugin: ValidUser (#1081) 2023-05-10 23:13:28 +02:00
Vendicated
0d5e2d0696 [skip ci] Refactor utils 2023-05-06 01:36:00 +02:00
Vendicated
2834bed518 MemberCount: Do not count offline group lol 2023-05-06 01:06:48 +02:00
Vendicated
0b2c3c834a MemberCount: Fix not properly updating on channel switch 2023-05-05 18:41:04 +02:00
AutumnVN
3a54a24c70 VencordToolbox: Change img icon to svg (#1059)
Co-authored-by: V <vendicated@riseup.net>
2023-05-05 16:41:01 +00:00
Vendicated
244d10dc9e [skip ci] Fix handleComponentFailed spam 2023-05-05 16:57:58 +02:00
Đỗ Văn Hoài Tuân
c25bc0ff4b USRBG: Add VoiceBackground (#1038)
Co-authored-by: V <vendicated@riseup.net>
2023-05-05 02:54:33 +02:00
Kode
22334663cf TextReplace: Test rules section (#1039) 2023-05-05 02:51:01 +02:00
Đỗ Văn Hoài Tuân
8813f81bde ShowConnections: Remove "(click to copy/open)" from tooltip (#1041) 2023-05-05 02:47:59 +02:00
Đỗ Văn Hoài Tuân
84371ed456 TextReplace: Fix Linebreak (#1057) 2023-05-05 02:47:31 +02:00
Nuckyz
8f61119b99 ShowAllMessageButtons plugin (#1029) 2023-05-05 02:47:26 +02:00
JSEverything
0a89d09727 style(Settings): Make the text about raw theme links bold (#1052) 2023-05-05 02:47:18 +02:00
Nuckyz
474932161f Fix FakeNitro Stickers (#1048) 2023-05-05 02:47:08 +02:00
Vendicated
bd95a25f4c TextReplace: Do not apply rules in #textreplace-rules channel 2023-05-02 04:39:27 +02:00
Vendicated
6a57ecc22b Delete FxTwitter & NoCanaryMessageLinks: Use TextReplace 2023-05-02 04:37:16 +02:00
Vendicated
0d665b7e0b Badge duplication glitch was too good to be true 2023-05-02 03:11:40 +02:00
Vendicated
d94b28fb8e Fix VencordToolbox 2023-05-02 02:57:25 +02:00
V
bc1d8694d4 new plugin: VencordToolbox (#998) 2023-05-02 02:55:38 +02:00
V
7bc1362cbd MessageEvents: Support sendMessage with attachments (#1023) 2023-05-02 02:55:21 +02:00
Vendicated
4dce836ff7 RelationshipNotifier: Fix false positives when using AccountSwitcher 2023-05-02 02:52:09 +02:00
Vendicated
9f534c0685 SupportHelper: Add OpenAsar info 2023-05-02 02:52:03 +02:00
V
c62d05e1b3 Refactor ipc to be strongly typed and hide impl details (#1018) 2023-05-02 02:50:51 +02:00
Nuckyz
6a1cb133cd Fix blank errors on plugin reports (#1021) 2023-05-01 07:26:27 +00:00
Vendicated
c6196dff81 [skip ci] PlatformIndicator: Fix 1px misalignment 2023-04-30 21:47:33 +02:00
Vendicated
acdb390302 [skip ci] Bump deps 2023-04-30 17:11:38 +02:00
Nuckyz
08d88b326d Fix Plugin Reporting false dependency errors and other stuff (#1017) 2023-04-30 13:34:38 +00:00
LordElias
a73858d131 UserVoiceShow: Fix lack of bottom padding for popout on current user and broken modal patch (#873) 2023-04-30 10:29:45 -03:00
Đỗ Văn Hoài Tuân
b0caa6f4db feat(plugin): TextReplace (#994)
Co-authored-by: Vendicated <vendicated@riseup.net>
2023-04-30 01:19:08 +00:00
Vendicated
168d4b4cd9 PlatformIndicators: Fix layout reflows - The trilogy 2023-04-30 02:07:57 +02:00
Vendicated
06cee75a56 PlatformIndicators: Fix layout reflows 2: Electric Boogaloo 2023-04-30 01:56:12 +02:00
151 changed files with 2727 additions and 1082 deletions

View File

@ -62,7 +62,7 @@
"indent": ["error", 4, { "SwitchCase": 1 }],
"arrow-parens": ["error", "as-needed"],
"eol-last": ["error", "always"],
"func-call-spacing": ["error", "never"],
"@typescript-eslint/func-call-spacing": ["error", "never"],
"no-multi-spaces": "error",
"no-trailing-spaces": "error",
"no-whitespace-before-property": "error",

View File

@ -16,51 +16,70 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
/// <reference path="../src/modules.d.ts" />
/// <reference path="../src/globals.d.ts" />
import monacoHtml from "~fileContent/../src/components/monacoWin.html";
import * as DataStore from "../src/api/DataStore";
import IpcEvents from "../src/utils/IpcEvents";
import { debounce } from "../src/utils";
import { getTheme, Theme } from "../src/utils/discord";
// Discord deletes this so need to store in variable
const { localStorage } = window;
// listeners for ipc.on
const listeners = {} as Record<string, Set<Function>>;
const cssListeners = new Set<(css: string) => void>();
const NOOP = () => { };
const NOOP_ASYNC = async () => { };
const handlers = {
[IpcEvents.GET_REPO]: () => "https://github.com/Vendicated/Vencord", // shrug
[IpcEvents.GET_SETTINGS_DIR]: () => "LocalStorage",
[IpcEvents.GET_QUICK_CSS]: () => DataStore.get("VencordQuickCss").then(s => s ?? ""),
[IpcEvents.SET_QUICK_CSS]: (css: string) => {
DataStore.set("VencordQuickCss", css);
listeners[IpcEvents.QUICK_CSS_UPDATE]?.forEach(l => l(null, css));
},
[IpcEvents.GET_SETTINGS]: () => localStorage.getItem("VencordSettings") || "{}",
[IpcEvents.SET_SETTINGS]: (s: string) => localStorage.setItem("VencordSettings", s),
[IpcEvents.GET_UPDATES]: () => ({ ok: true, value: [] }),
[IpcEvents.OPEN_EXTERNAL]: (url: string) => open(url, "_blank"),
};
function onEvent(event: string, ...args: any[]) {
const handler = handlers[event];
if (!handler) throw new Error(`Event ${event} not implemented.`);
return handler(...args);
}
const setCssDebounced = debounce((css: string) => VencordNative.quickCss.set(css));
// probably should make this less cursed at some point
window.VencordNative = {
getVersions: () => ({}),
ipc: {
send: (event: string, ...args: any[]) => void onEvent(event, ...args),
sendSync: onEvent,
on(event: string, listener: () => {}) {
(listeners[event] ??= new Set()).add(listener);
},
off(event: string, listener: () => {}) {
return listeners[event]?.delete(listener);
},
invoke: (event: string, ...args: any[]) => Promise.resolve(onEvent(event, ...args))
native: {
getVersions: () => ({}),
openExternal: async (url) => void open(url, "_blank")
},
updater: {
getRepo: async () => ({ ok: true, value: "https://github.com/Vendicated/Vencord" }),
getUpdates: async () => ({ ok: true, value: [] }),
update: async () => ({ ok: true, value: false }),
rebuild: async () => ({ ok: true, value: true }),
},
quickCss: {
get: () => DataStore.get("VencordQuickCss").then(s => s ?? ""),
set: async (css: string) => {
await DataStore.set("VencordQuickCss", css);
cssListeners.forEach(l => l(css));
},
addChangeListener(cb) {
cssListeners.add(cb);
},
openFile: NOOP_ASYNC,
async openEditor() {
const features = `popup,width=${Math.min(window.innerWidth, 1000)},height=${Math.min(window.innerHeight, 1000)}`;
const win = open("about:blank", "VencordQuickCss", features);
if (!win) {
alert("Failed to open QuickCSS popup. Make sure to allow popups!");
return;
}
win.setCss = setCssDebounced;
win.getCurrentCss = () => VencordNative.quickCss.get();
win.getTheme = () =>
getTheme() === Theme.Light
? "vs-light"
: "vs-dark";
win.document.write(monacoHtml);
},
},
settings: {
get: () => localStorage.getItem("VencordSettings") || "{}",
set: async (s: string) => localStorage.setItem("VencordSettings", s),
getSettingsDir: async () => "LocalStorage"
}
};

View File

@ -26,10 +26,6 @@ export default definePlugin({
name: "Your Name",
},
],
// Delete `patches` if you are not using code patches, as it will make
// your plugin require restarts, and your stop() method will not be
// invoked at all. The presence of the key in the object alone is
// enough to trigger this behavior, even if the value is an empty array.
patches: [],
// Delete these two below if you are only using code patches
start() {},

View File

@ -1,7 +1,7 @@
{
"name": "vencord",
"private": "true",
"version": "1.2.0",
"version": "1.2.1",
"description": "The cutest Discord client mod",
"homepage": "https://github.com/Vendicated/Vencord#readme",
"bugs": {
@ -32,20 +32,20 @@
},
"dependencies": {
"@vap/core": "0.0.12",
"@vap/shiki": "0.10.3",
"@vap/shiki": "0.10.5",
"fflate": "^0.7.4",
"nanoid": "^4.0.2",
"virtual-merge": "^1.0.1"
},
"devDependencies": {
"@types/diff": "^5.0.2",
"@types/lodash": "^4.14.191",
"@types/node": "^18.11.18",
"@types/react": "^18.0.27",
"@types/react-dom": "^18.0.10",
"@types/diff": "^5.0.3",
"@types/lodash": "^4.14.194",
"@types/node": "^18.16.3",
"@types/react": "^18.2.0",
"@types/react-dom": "^18.2.1",
"@types/yazl": "^2.4.2",
"@typescript-eslint/eslint-plugin": "^5.49.0",
"@typescript-eslint/parser": "^5.49.0",
"@typescript-eslint/eslint-plugin": "^5.59.1",
"@typescript-eslint/parser": "^5.59.1",
"diff": "^5.1.0",
"discord-types": "^1.3.26",
"esbuild": "^0.15.18",
@ -53,17 +53,17 @@
"eslint-import-resolver-alias": "^1.1.2",
"eslint-plugin-header": "^3.1.1",
"eslint-plugin-path-alias": "^1.0.0",
"eslint-plugin-simple-import-sort": "^8.0.0",
"eslint-plugin-simple-import-sort": "^10.0.0",
"eslint-plugin-unused-imports": "^2.0.0",
"highlight.js": "10.6.0",
"moment": "^2.29.4",
"puppeteer-core": "^19.6.0",
"puppeteer-core": "^19.11.1",
"standalone-electron-types": "^1.0.0",
"stylelint": "^14.16.1",
"stylelint-config-standard": "^29.0.0",
"tsx": "^3.12.6",
"type-fest": "^3.5.3",
"typescript": "^4.9.4"
"stylelint": "^15.6.0",
"stylelint-config-standard": "^33.0.0",
"tsx": "^3.12.7",
"type-fest": "^3.9.0",
"typescript": "^5.0.4"
},
"packageManager": "pnpm@8.1.1",
"pnpm": {

734
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -130,7 +130,7 @@ async function printReport() {
},
{
title: "Discord Errors",
description: toCodeBlock(report.otherErrors.join("\n")),
description: report.otherErrors.length ? toCodeBlock(report.otherErrors.join("\n")) : "None",
color: report.otherErrors.length ? 0xff0000 : 0x00ff00
}
]
@ -194,9 +194,10 @@ page.on("console", async e => {
return a.toString();
}
})
).then(a => a.join(" "));
).then(a => a.join(" ").trim());
if (!text.startsWith("Failed to load resource: the server responded with a status of")) {
if (text.length && !text.startsWith("Failed to load resource: the server responded with a status of")) {
console.error("Got unexpected error", text);
report.otherErrors.push(text);
}
@ -233,7 +234,7 @@ function runTime(token: string) {
// Needs native server to run
if (p.name === "WebRichPresence (arRPC)") return;
p.required = true;
Vencord.Settings.plugins[p.name].enabled = true;
p.patches?.forEach(patch => {
patch.plugin = p.name;
delete patch.predicate;

View File

@ -28,12 +28,12 @@ import "./utils/quickCss";
import "./webpack/patchWebpack";
import { showNotification } from "./api/Notifications";
import { PlainSettings, Settings } from "./api/settings";
import { PlainSettings, Settings } from "./api/Settings";
import { patches, PMLogger, startAllPlugins } from "./plugins";
import { localStorage } from "./utils/localStorage";
import { relaunch } from "./utils/native";
import { getCloudSettings, putCloudSettings } from "./utils/settingsSync";
import { checkForUpdates, rebuild, update, UpdateLogger } from "./utils/updater";
import { checkForUpdates, update, UpdateLogger } from "./utils/updater";
import { onceReady } from "./webpack";
import { SettingsRouter } from "./webpack/common";
@ -76,7 +76,6 @@ async function init() {
if (Settings.autoUpdate) {
await update();
await rebuild();
if (Settings.autoUpdateNotification)
setTimeout(() => showNotification({
title: "Vencord has been updated!",

View File

@ -16,34 +16,46 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import IPC_EVENTS from "@utils/IpcEvents";
import { IpcRenderer, ipcRenderer } from "electron";
import { IpcEvents } from "@utils/IpcEvents";
import { IpcRes } from "@utils/types";
import { ipcRenderer } from "electron";
function assertEventAllowed(event: string) {
if (!(event in IPC_EVENTS)) throw new Error(`Event ${event} not allowed.`);
function invoke<T = any>(event: IpcEvents, ...args: any[]) {
return ipcRenderer.invoke(event, ...args) as Promise<T>;
}
export function sendSync<T = any>(event: IpcEvents, ...args: any[]) {
return ipcRenderer.sendSync(event, ...args) as T;
}
export default {
getVersions: () => process.versions,
ipc: {
send(event: string, ...args: any[]) {
assertEventAllowed(event);
ipcRenderer.send(event, ...args);
updater: {
getUpdates: () => invoke<IpcRes<Record<"hash" | "author" | "message", string>[]>>(IpcEvents.GET_UPDATES),
update: () => invoke<IpcRes<boolean>>(IpcEvents.UPDATE),
rebuild: () => invoke<IpcRes<boolean>>(IpcEvents.BUILD),
getRepo: () => invoke<IpcRes<string>>(IpcEvents.GET_REPO),
},
settings: {
get: () => sendSync<string>(IpcEvents.GET_SETTINGS),
set: (settings: string) => invoke<void>(IpcEvents.SET_SETTINGS, settings),
getSettingsDir: () => invoke<string>(IpcEvents.GET_SETTINGS_DIR),
},
quickCss: {
get: () => invoke<string>(IpcEvents.GET_QUICK_CSS),
set: (css: string) => invoke<void>(IpcEvents.SET_QUICK_CSS, css),
addChangeListener(cb: (newCss: string) => void) {
ipcRenderer.on(IpcEvents.QUICK_CSS_UPDATE, (_, css) => cb(css));
},
sendSync<T = any>(event: string, ...args: any[]): T {
assertEventAllowed(event);
return ipcRenderer.sendSync(event, ...args);
},
on(event: string, listener: Parameters<IpcRenderer["on"]>[1]) {
assertEventAllowed(event);
ipcRenderer.on(event, listener);
},
off(event: string, listener: Parameters<IpcRenderer["off"]>[1]) {
assertEventAllowed(event);
ipcRenderer.off(event, listener);
},
invoke<T = any>(event: string, ...args: any[]): Promise<T> {
assertEventAllowed(event);
return ipcRenderer.invoke(event, ...args);
}
}
openFile: () => invoke<void>(IpcEvents.OPEN_QUICKCSS),
openEditor: () => invoke<void>(IpcEvents.OPEN_MONACO_EDITOR),
},
native: {
getVersions: () => process.versions as Partial<NodeJS.ProcessVersions>,
openExternal: (url: string) => invoke<void>(IpcEvents.OPEN_EXTERNAL, url)
},
};

View File

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { makeCodeblock } from "@utils/misc";
import { makeCodeblock } from "@utils/text";
import { sendBotMessage } from "./commandHelpers";
import { ApplicationCommandInputType, ApplicationCommandOptionType, ApplicationCommandType, Argument, Command, CommandContext, Option } from "./types";

View File

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import Logger from "@utils/Logger";
import { Logger } from "@utils/Logger";
import type { ReactElement } from "react";
type ContextMenuPatchCallbackReturn = (() => void) | void;

View File

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import Logger from "@utils/Logger";
import { Logger } from "@utils/Logger";
import { MessageStore } from "@webpack/common";
import type { Channel, Message } from "discord-types/general";
import type { Promisable } from "type-fest";
@ -36,10 +36,50 @@ export interface Emoji {
export interface MessageObject {
content: string,
validNonShortcutEmojis: Emoji[];
invalidEmojis: any[];
tts: boolean;
}
export interface Upload {
classification: string;
currentSize: number;
description: string | null;
filename: string;
id: string;
isImage: boolean;
isVideo: boolean;
item: {
file: File;
platform: number;
};
loaded: number;
mimeType: string;
preCompressionSize: number;
responseUrl: string;
sensitive: boolean;
showLargeMessageDialog: boolean;
spoiler: boolean;
status: "NOT_STARTED" | "STARTED" | "UPLOADING" | "ERROR" | "COMPLETED" | "CANCELLED";
uniqueId: string;
uploadedFilename: string;
}
export interface MessageReplyOptions {
messageReference: Message["messageReference"];
allowedMentions?: {
parse: Array<string>;
repliedUser: boolean;
};
}
export interface MessageExtra {
stickerIds?: string[];
stickers?: string[];
uploads?: Upload[];
replyOptions: MessageReplyOptions;
content: string;
channel: Channel;
type?: any;
openWarningPopout: (props: any) => any;
}
export type SendListener = (channelId: string, messageObj: MessageObject, extra: MessageExtra) => Promisable<void | { cancel: boolean; }>;
@ -48,7 +88,8 @@ export type EditListener = (channelId: string, messageId: string, messageObj: Me
const sendListeners = new Set<SendListener>();
const editListeners = new Set<EditListener>();
export async function _handlePreSend(channelId: string, messageObj: MessageObject, extra: MessageExtra) {
export async function _handlePreSend(channelId: string, messageObj: MessageObject, extra: MessageExtra, replyOptions: MessageReplyOptions) {
extra.replyOptions = replyOptions;
for (const listener of sendListeners) {
try {
const result = await listener(channelId, messageObj, extra);

View File

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import Logger from "@utils/Logger";
import { Logger } from "@utils/Logger";
import { Channel, Message } from "discord-types/general";
import type { MouseEventHandler } from "react";

View File

@ -18,7 +18,7 @@
import "./styles.css";
import { useSettings } from "@api/settings";
import { useSettings } from "@api/Settings";
import ErrorBoundary from "@components/ErrorBoundary";
import { classes } from "@utils/misc";
import { React, useEffect, useMemo, useState, useStateFromStores, WindowStore } from "@webpack/common";

View File

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Settings } from "@api/settings";
import { Settings } from "@api/Settings";
import { Queue } from "@utils/Queue";
import { ReactDOM } from "@webpack/common";
import type { ReactNode } from "react";

View File

@ -17,10 +17,10 @@
*/
import * as DataStore from "@api/DataStore";
import { Settings } from "@api/settings";
import { Settings } from "@api/Settings";
import { classNameFactory } from "@api/Styles";
import { useAwaiter } from "@utils/misc";
import { closeModal, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
import { useAwaiter } from "@utils/react";
import { Alerts, Button, Forms, moment, React, Text, Timestamp, useEffect, useReducer, useState } from "@webpack/common";
import { nanoid } from "nanoid";
import type { DispatchWithoutAction } from "react";

View File

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import Logger from "@utils/Logger";
import { Logger } from "@utils/Logger";
const logger = new Logger("ServerListAPI");

View File

@ -17,9 +17,8 @@
*/
import { debounce } from "@utils/debounce";
import IpcEvents from "@utils/IpcEvents";
import { localStorage } from "@utils/localStorage";
import Logger from "@utils/Logger";
import { Logger } from "@utils/Logger";
import { mergeDefaults } from "@utils/misc";
import { putCloudSettings } from "@utils/settingsSync";
import { DefinedSettings, OptionType, SettingsChecks, SettingsDefinition } from "@utils/types";
@ -94,7 +93,7 @@ const DefaultSettings: Settings = {
};
try {
var settings = JSON.parse(VencordNative.ipc.sendSync(IpcEvents.GET_SETTINGS)) as Settings;
var settings = JSON.parse(VencordNative.settings.get()) as Settings;
mergeDefaults(settings, DefaultSettings);
} catch (err) {
var settings = mergeDefaults({} as Settings, DefaultSettings);
@ -173,7 +172,7 @@ function makeProxy(settings: any, root = settings, path = ""): Settings {
PlainSettings.cloud.settingsSyncVersion = Date.now();
localStorage.Vencord_settingsDirty = true;
saveSettingsOnFrequentAction();
VencordNative.ipc.invoke(IpcEvents.SET_SETTINGS, JSON.stringify(root, null, 4));
VencordNative.settings.set(JSON.stringify(root, null, 4));
return true;
}
});
@ -249,10 +248,7 @@ export function migratePluginSettings(name: string, ...oldNames: string[]) {
logger.info(`Migrating settings from old name ${oldName} to ${name}`);
plugins[name] = plugins[oldName];
delete plugins[oldName];
VencordNative.ipc.invoke(
IpcEvents.SET_SETTINGS,
JSON.stringify(settings, null, 4)
);
VencordNative.settings.set(JSON.stringify(settings, null, 4));
break;
}
}

View File

@ -16,11 +16,11 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import Logger from "@utils/Logger";
import { proxyLazy } from "@utils/proxyLazy";
import { proxyLazy } from "@utils/lazy";
import { Logger } from "@utils/Logger";
import { findModuleId, wreq } from "@webpack";
import { Settings } from "./settings";
import { Settings } from "./Settings";
interface Setting<T> {
/**

View File

@ -16,7 +16,6 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import IpcEvents from "@utils/IpcEvents";
import { Button } from "@webpack/common";
import { Heart } from "./Heart";
@ -27,9 +26,7 @@ export default function DonateButton(props: any) {
{...props}
look={Button.Looks.LINK}
color={Button.Colors.TRANSPARENT}
onClick={() =>
VencordNative.ipc.invoke(IpcEvents.OPEN_EXTERNAL, "https://github.com/sponsors/Vendicated")
}
onClick={() => VencordNative.native.openExternal("https://github.com/sponsors/Vendicated")}
>
<Heart />
Donate

View File

@ -16,9 +16,9 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import Logger from "@utils/Logger";
import { Logger } from "@utils/Logger";
import { Margins } from "@utils/margins";
import { LazyComponent } from "@utils/misc";
import { LazyComponent } from "@utils/react";
import { React } from "@webpack/common";
import { ErrorCard } from "./ErrorCard";

83
src/components/Icons.tsx Normal file
View File

@ -0,0 +1,83 @@
/*
* Vencord, a modification for Discord's desktop app
* Copyright (c) 2023 Vendicated and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { classes } from "@utils/misc";
import type { PropsWithChildren } from "react";
interface BaseIconProps extends IconProps {
viewBox: string;
}
interface IconProps {
className?: string;
height?: number;
width?: number;
}
function Icon({ height = 24, width = 24, className, children, viewBox }: PropsWithChildren<BaseIconProps>) {
return (
<svg
className={classes(className, "vc-icon")}
aria-hidden="true"
role="img"
width={width}
height={height}
viewBox={viewBox}
>
{children}
</svg>
);
}
/**
* Discord's link icon, as seen in the Message context menu "Copy Message Link" option
*/
export function LinkIcon({ height = 24, width = 24, className }: IconProps) {
return (
<Icon
height={height}
width={width}
className={classes(className, "vc-link-icon")}
viewBox="0 0 24 24"
>
<g fill="none" fill-rule="evenodd">
<path fill="currentColor" d="M10.59 13.41c.41.39.41 1.03 0 1.42-.39.39-1.03.39-1.42 0a5.003 5.003 0 0 1 0-7.07l3.54-3.54a5.003 5.003 0 0 1 7.07 0 5.003 5.003 0 0 1 0 7.07l-1.49 1.49c.01-.82-.12-1.64-.4-2.42l.47-.48a2.982 2.982 0 0 0 0-4.24 2.982 2.982 0 0 0-4.24 0l-3.53 3.53a2.982 2.982 0 0 0 0 4.24zm2.82-4.24c.39-.39 1.03-.39 1.42 0a5.003 5.003 0 0 1 0 7.07l-3.54 3.54a5.003 5.003 0 0 1-7.07 0 5.003 5.003 0 0 1 0-7.07l1.49-1.49c-.01.82.12 1.64.4 2.43l-.47.47a2.982 2.982 0 0 0 0 4.24 2.982 2.982 0 0 0 4.24 0l3.53-3.53a2.982 2.982 0 0 0 0-4.24.973.973 0 0 1 0-1.42z" />
<rect width={width} height={height} />
</g>
</Icon>
);
}
/**
* Discord's copy icon, as seen in the user popout right of the username when clicking
* your own username in the bottom left user panel
*/
export function CopyIcon(props: IconProps) {
return (
<Icon
{...props}
className={classes(props.className, "vc-copy-icon")}
viewBox="0 0 24 24"
>
<g fill="currentColor">
<path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1z" />
<path d="M15 5H8c-1.1 0-1.99.9-1.99 2L6 21c0 1.1.89 2 1.99 2H19c1.1 0 2-.9 2-2V11l-6-6zM8 21V7h6v5h5v9H8z" />
</g>
</Icon>
);
}

View File

@ -1,51 +0,0 @@
/*
* Vencord, a modification for Discord's desktop app
* Copyright (c) 2022 Vendicated and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { debounce } from "@utils/debounce";
import IpcEvents from "@utils/IpcEvents";
import { Queue } from "@utils/Queue";
import { find } from "@webpack";
import monacoHtml from "~fileContent/monacoWin.html";
const queue = new Queue();
const setCss = debounce((css: string) => {
queue.push(() => VencordNative.ipc.invoke(IpcEvents.SET_QUICK_CSS, css));
});
export async function launchMonacoEditor() {
const features = `popup,width=${Math.min(window.innerWidth, 1000)},height=${Math.min(window.innerHeight, 1000)}`;
const win = open("about:blank", "VencordQuickCss", features);
if (!win) {
alert("Failed to open QuickCSS popup. Make sure to allow popups!");
return;
}
win.setCss = setCss;
win.getCurrentCss = () => VencordNative.ipc.invoke(IpcEvents.GET_QUICK_CSS);
win.getTheme = () =>
find(m =>
m.ProtoClass?.typeName.endsWith("PreloadedUserSettings")
)?.getCurrentValue()?.appearance?.theme === 2
? "vs-light"
: "vs-dark";
win.document.write(monacoHtml);
window.__VENCORD_MONACO_WIN__ = new WeakRef(win);
}

View File

@ -18,8 +18,8 @@
import { debounce } from "@utils/debounce";
import { Margins } from "@utils/margins";
import { makeCodeblock } from "@utils/misc";
import { canonicalizeMatch, canonicalizeReplace } from "@utils/patches";
import { makeCodeblock } from "@utils/text";
import { ReplaceFn } from "@utils/types";
import { search } from "@webpack";
import { Button, Clipboard, Forms, Parser, React, Switch, Text, TextInput } from "@webpack/common";

View File

@ -17,13 +17,14 @@
*/
import { generateId } from "@api/Commands";
import { useSettings } from "@api/settings";
import { useSettings } from "@api/Settings";
import ErrorBoundary from "@components/ErrorBoundary";
import { Flex } from "@components/Flex";
import { proxyLazy } from "@utils/lazy";
import { Margins } from "@utils/margins";
import { classes, LazyComponent } from "@utils/misc";
import { classes } from "@utils/misc";
import { ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize } from "@utils/modal";
import { proxyLazy } from "@utils/proxyLazy";
import { LazyComponent } from "@utils/react";
import { OptionType, Plugin } from "@utils/types";
import { findByCode, findByPropsLazy } from "@webpack";
import { Button, FluxDispatcher, Forms, React, Text, Tooltip, UserStore, UserUtils } from "@webpack/common";
@ -128,6 +129,8 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti
return <Forms.FormText>There are no settings for this plugin.</Forms.FormText>;
} else {
const options = Object.entries(plugin.options).map(([key, setting]) => {
if (setting.hidden) return null;
function onChange(newValue: any) {
setTempSettings(s => ({ ...s, [key]: newValue }));
}

View File

@ -20,7 +20,7 @@ import "./styles.css";
import * as DataStore from "@api/DataStore";
import { showNotice } from "@api/Notices";
import { useSettings } from "@api/settings";
import { useSettings } from "@api/Settings";
import { classNameFactory } from "@api/Styles";
import ErrorBoundary from "@components/ErrorBoundary";
import { Flex } from "@components/Flex";
@ -29,10 +29,12 @@ import { Badge } from "@components/PluginSettings/components";
import PluginModal from "@components/PluginSettings/PluginModal";
import { Switch } from "@components/Switch";
import { ChangeList } from "@utils/ChangeList";
import Logger from "@utils/Logger";
import { Logger } from "@utils/Logger";
import { Margins } from "@utils/margins";
import { classes, LazyComponent, useAwaiter } from "@utils/misc";
import { classes } from "@utils/misc";
import { openModalLazy } from "@utils/modal";
import { onlyOnce } from "@utils/onlyOnce";
import { LazyComponent, useAwaiter } from "@utils/react";
import { Plugin } from "@utils/types";
import { findByCode, findByPropsLazy } from "@webpack";
import { Alerts, Button, Card, Forms, Parser, React, Select, Text, TextInput, Toasts, Tooltip } from "@webpack/common";
@ -125,7 +127,7 @@ function PluginCard({ plugin, disabled, onRestartNeeded, onMouseEnter, onMouseLe
}
// if the plugin has patches, dont use stopPlugin/startPlugin. Wait for restart to apply changes.
if (plugin.patches) {
if (plugin.patches?.length) {
settings.enabled = !wasEnabled;
onRestartNeeded(plugin.name);
return;
@ -258,6 +260,9 @@ export default ErrorBoundary.wrap(function PluginSettings() {
requiredPlugins = [];
for (const p of sortedPlugins) {
if (!p.options && p.name.endsWith("API") && searchValue.value !== "API")
continue;
if (!pluginFilter(p)) continue;
const isRequired = p.required || depMap[p.name]?.some(d => settings.plugins[d].enabled);
@ -341,7 +346,7 @@ export default ErrorBoundary.wrap(function PluginSettings() {
);
}, {
message: "Failed to render the Plugin Settings. If this persists, try using the installer to reinstall!",
onError: handleComponentFailed,
onError: onlyOnce(handleComponentFailed),
});
function makeDependencyList(deps: string[]) {

View File

@ -17,7 +17,7 @@
*/
import { showNotification } from "@api/Notifications";
import { Settings, useSettings } from "@api/settings";
import { Settings, useSettings } from "@api/Settings";
import { CheckedTextInput } from "@components/CheckedTextInput";
import ErrorBoundary from "@components/ErrorBoundary";
import { Link } from "@components/Link";

View File

@ -16,11 +16,11 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { useSettings } from "@api/settings";
import { useSettings } from "@api/Settings";
import ErrorBoundary from "@components/ErrorBoundary";
import { Link } from "@components/Link";
import { Margins } from "@utils/margins";
import { useAwaiter } from "@utils/misc";
import { useAwaiter } from "@utils/react";
import { findLazy } from "@webpack";
import { Card, Forms, React, TextArea } from "@webpack/common";
@ -93,7 +93,7 @@ export default ErrorBoundary.wrap(function () {
<Card className="vc-settings-card vc-text-selectable">
<Forms.FormTitle tag="h5">Paste links to .theme.css files here</Forms.FormTitle>
<Forms.FormText>One link per line</Forms.FormText>
<Forms.FormText>Make sure to use the raw links or github.io links!</Forms.FormText>
<Forms.FormText><strong>Make sure to use the raw links or github.io links!</strong></Forms.FormText>
<Forms.FormDivider className={Margins.top8 + " " + Margins.bottom8} />
<Forms.FormTitle tag="h5">Find Themes:</Forms.FormTitle>
<div style={{ marginBottom: ".5em" }}>

View File

@ -16,16 +16,18 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { useSettings } from "@api/settings";
import { useSettings } from "@api/Settings";
import ErrorBoundary from "@components/ErrorBoundary";
import { ErrorCard } from "@components/ErrorCard";
import { Flex } from "@components/Flex";
import { handleComponentFailed } from "@components/handleComponentFailed";
import { Link } from "@components/Link";
import { Margins } from "@utils/margins";
import { classes, useAwaiter } from "@utils/misc";
import { classes } from "@utils/misc";
import { relaunch } from "@utils/native";
import { changes, checkForUpdates, getRepo, isNewer, rebuild, update, updateError, UpdateLogger } from "@utils/updater";
import { onlyOnce } from "@utils/onlyOnce";
import { useAwaiter } from "@utils/react";
import { changes, checkForUpdates, getRepo, isNewer, update, updateError, UpdateLogger } from "@utils/updater";
import { Alerts, Button, Card, Forms, Parser, React, Switch, Toasts } from "@webpack/common";
import gitHash from "~git-hash";
@ -125,7 +127,6 @@ function Updatable(props: CommonProps) {
onClick={withDispatcher(setIsUpdating, async () => {
if (await update()) {
setUpdates([]);
await rebuild();
await new Promise<void>(r => {
Alerts.show({
title: "Update Success!",
@ -251,5 +252,5 @@ function Updater() {
export default IS_WEB ? null : ErrorBoundary.wrap(Updater, {
message: "Failed to render the Updater. If this persists, try using the installer to reinstall!",
onError: handleComponentFailed,
onError: onlyOnce(handleComponentFailed),
});

View File

@ -18,15 +18,15 @@
import { openNotificationLogModal } from "@api/Notifications/notificationLog";
import { Settings, useSettings } from "@api/settings";
import { Settings, useSettings } from "@api/Settings";
import { classNameFactory } from "@api/Styles";
import DonateButton from "@components/DonateButton";
import ErrorBoundary from "@components/ErrorBoundary";
import { ErrorCard } from "@components/ErrorCard";
import IpcEvents from "@utils/IpcEvents";
import { Margins } from "@utils/margins";
import { identity, useAwaiter } from "@utils/misc";
import { identity } from "@utils/misc";
import { relaunch, showItemInFolder } from "@utils/native";
import { useAwaiter } from "@utils/react";
import { Button, Card, Forms, React, Select, Slider, Switch } from "@webpack/common";
const cl = classNameFactory("vc-settings-");
@ -39,7 +39,7 @@ type KeysOfType<Object, Type> = {
}[keyof Object];
function VencordSettings() {
const [settingsDir, , settingsDirPending] = useAwaiter(() => VencordNative.ipc.invoke<string>(IpcEvents.GET_SETTINGS_DIR), {
const [settingsDir, , settingsDirPending] = useAwaiter(VencordNative.settings.getSettingsDir, {
fallbackValue: "Loading..."
});
const settings = useSettings();
@ -101,40 +101,35 @@ function VencordSettings() {
<DonateCard image={donateImage} />
<Forms.FormSection title="Quick Actions">
<Card className={cl("quick-actions-card")}>
{IS_WEB ? (
<Button
onClick={() => require("../Monaco").launchMonacoEditor()}
size={Button.Sizes.SMALL}
disabled={settingsDir === "Loading..."}>
Open QuickCSS File
</Button>
) : (
<React.Fragment>
<React.Fragment>
{!IS_WEB && (
<Button
onClick={relaunch}
size={Button.Sizes.SMALL}>
Restart Client
</Button>
<Button
onClick={() => VencordNative.ipc.invoke(IpcEvents.OPEN_MONACO_EDITOR)}
size={Button.Sizes.SMALL}
disabled={settingsDir === "Loading..."}>
Open QuickCSS File
</Button>
)}
<Button
onClick={() => VencordNative.quickCss.openEditor()}
size={Button.Sizes.SMALL}
disabled={settingsDir === "Loading..."}>
Open QuickCSS File
</Button>
{!IS_WEB && (
<Button
onClick={() => showItemInFolder(settingsDir)}
size={Button.Sizes.SMALL}
disabled={settingsDirPending}>
Open Settings Folder
</Button>
<Button
onClick={() => VencordNative.ipc.invoke(IpcEvents.OPEN_EXTERNAL, "https://github.com/Vendicated/Vencord")}
size={Button.Sizes.SMALL}
disabled={settingsDirPending}>
Open in GitHub
</Button>
</React.Fragment>
)}
)}
<Button
onClick={() => VencordNative.native.openExternal("https://github.com/Vendicated/Vencord")}
size={Button.Sizes.SMALL}
disabled={settingsDirPending}>
Open in GitHub
</Button>
</React.Fragment>
</Card>
</Forms.FormSection>

View File

@ -22,6 +22,7 @@ import { classNameFactory } from "@api/Styles";
import ErrorBoundary from "@components/ErrorBoundary";
import { handleComponentFailed } from "@components/handleComponentFailed";
import { isMobile } from "@utils/misc";
import { onlyOnce } from "@utils/onlyOnce";
import { Forms, SettingsRouter, TabBar, Text } from "@webpack/common";
import BackupRestoreTab from "./BackupRestoreTab";
@ -86,8 +87,10 @@ function Settings(props: SettingsProps) {
</Forms.FormSection >;
}
const onError = onlyOnce(handleComponentFailed);
export default function (props: SettingsProps) {
return <ErrorBoundary onError={handleComponentFailed}>
return <ErrorBoundary onError={onError}>
<Settings tab={props.tab} />
</ErrorBoundary>;
}

View File

@ -15,7 +15,7 @@
display: flex;
gap: 1em;
align-items: center;
justify-content: space-between;
justify-content: space-evenly;
flex-grow: 1;
flex-flow: row wrap;
margin-bottom: 1em;
@ -59,7 +59,7 @@
}
.vc-text-selectable,
.vc-text-selectable :not(a, button) {
.vc-text-selectable :not(a, button, a *, button *) {
/* make text selectable, silly discord makes the entirety of settings not selectable */
user-select: text;

View File

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import Logger from "@utils/Logger";
import { Logger } from "@utils/Logger";
if (IS_DEV) {
var traces = {} as Record<string, [number, any[]]>;

View File

@ -19,7 +19,7 @@
import "./updater";
import { debounce } from "@utils/debounce";
import IpcEvents from "@utils/IpcEvents";
import { IpcEvents } from "@utils/IpcEvents";
import { Queue } from "@utils/Queue";
import { BrowserWindow, ipcMain, shell } from "electron";
import { mkdirSync, readFileSync, watch } from "fs";
@ -44,7 +44,7 @@ export function readSettings() {
}
}
export function getSettings(): typeof import("@api/settings").Settings {
export function getSettings(): typeof import("@api/Settings").Settings {
try {
return JSON.parse(readSettings());
} catch {

View File

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import IpcEvents from "@utils/IpcEvents";
import { IpcEvents } from "@utils/IpcEvents";
import { execFile as cpExecFile } from "child_process";
import { ipcMain } from "electron";
import { join } from "path";

View File

@ -17,7 +17,7 @@
*/
import { VENCORD_USER_AGENT } from "@utils/constants";
import IpcEvents from "@utils/IpcEvents";
import { IpcEvents } from "@utils/IpcEvents";
import { ipcMain } from "electron";
import { writeFile } from "fs/promises";
import { join } from "path";

View File

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Settings } from "@api/settings";
import { Settings } from "@api/Settings";
import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types";

View File

@ -22,12 +22,11 @@ import ErrorBoundary from "@components/ErrorBoundary";
import { Flex } from "@components/Flex";
import { Heart } from "@components/Heart";
import { Devs } from "@utils/constants";
import IpcEvents from "@utils/IpcEvents";
import Logger from "@utils/Logger";
import { Logger } from "@utils/Logger";
import { Margins } from "@utils/margins";
import { closeModal, Modals, openModal } from "@utils/modal";
import definePlugin from "@utils/types";
import { Forms } from "@webpack/common";
import { Forms, Toasts } from "@webpack/common";
const CONTRIBUTOR_BADGE = "https://cdn.discordapp.com/attachments/1033680203433660458/1092089947126780035/favicon.png";
@ -48,7 +47,29 @@ const ContributorBadge: ProfileBadge = {
link: "https://github.com/Vendicated/Vencord"
};
const DonorBadges = {} as Record<string, Pick<ProfileBadge, "image" | "description">[]>;
let DonorBadges = {} as Record<string, Pick<ProfileBadge, "image" | "description">[]>;
async function loadBadges(noCache = false) {
DonorBadges = {};
const init = {} as RequestInit;
if (noCache)
init.cache = "no-cache";
const badges = await fetch("https://gist.githubusercontent.com/Vendicated/51a3dd775f6920429ec6e9b735ca7f01/raw/badges.csv", init)
.then(r => r.text());
const lines = badges.trim().split("\n");
if (lines.shift() !== "id,tooltip,image") {
new Logger("BadgeAPI").error("Invalid badges.csv file!");
return;
}
for (const line of lines) {
const [id, description, image] = line.split(",");
(DonorBadges[id] ??= []).push({ image, description });
}
}
export default definePlugin({
name: "BadgeAPI",
@ -82,24 +103,27 @@ export default definePlugin({
}
],
toolboxActions: {
async "Refetch Badges"() {
await loadBadges(true);
Toasts.show({
id: Toasts.genId(),
message: "Successfully refetched badges!",
type: Toasts.Type.SUCCESS
});
}
},
async start() {
Vencord.Api.Badges.addBadge(ContributorBadge);
await loadBadges();
},
renderBadgeComponent: ErrorBoundary.wrap((badge: ProfileBadge & BadgeUserArgs) => {
const Component = badge.component!;
return <Component {...badge} />;
}, { noop: true }),
async start() {
Vencord.Api.Badges.addBadge(ContributorBadge);
const badges = await fetch("https://gist.githubusercontent.com/Vendicated/51a3dd775f6920429ec6e9b735ca7f01/raw/badges.csv").then(r => r.text());
const lines = badges.trim().split("\n");
if (lines.shift() !== "id,tooltip,image") {
new Logger("BadgeAPI").error("Invalid badges.csv file!");
return;
}
for (const line of lines) {
const [id, description, image] = line.split(",");
(DonorBadges[id] ??= []).push({ image, description });
}
},
getDonorBadges(userId: string) {
return DonorBadges[userId]?.map(badge => ({
@ -115,7 +139,7 @@ export default definePlugin({
const modalKey = openModal(props => (
<ErrorBoundary noop onError={() => {
closeModal(modalKey);
VencordNative.ipc.invoke(IpcEvents.OPEN_EXTERNAL, "https://github.com/sponsors/Vendicated");
VencordNative.native.openExternal("https://github.com/sponsors/Vendicated");
}}>
<Modals.ModalRoot {...props}>
<Modals.ModalHeader>

View File

@ -22,17 +22,29 @@ import definePlugin from "@utils/types";
export default definePlugin({
name: "MessageEventsAPI",
description: "Api required by anything using message events.",
authors: [Devs.Arjix, Devs.hunt],
authors: [Devs.Arjix, Devs.hunt, Devs.Ven],
patches: [
{
find: '"MessageActionCreators"',
replacement: [{
match: /_sendMessage:(function\([^)]+\)){/,
replace: "_sendMessage:async $1{if(await Vencord.Api.MessageEvents._handlePreSend(...arguments))return;"
}, {
match: /\beditMessage:(function\([^)]+\)){/,
replacement: {
// editMessage: function (...) {
match: /\beditMessage:(function\(.+?\))\{/,
// editMessage: async function (...) { await handlePreEdit(...); ...
replace: "editMessage:async $1{await Vencord.Api.MessageEvents._handlePreEdit(...arguments);"
}]
}
},
{
find: ".handleSendMessage=",
replacement: {
// props.chatInputType...then((function(isMessageValid)... var parsedMessage = b.c.parse(channel,... var replyOptions = f.g.getSendMessageOptionsForReply(pendingReply);
// Lookbehind: validateMessage)({openWarningPopout:..., type: i.props.chatInputType, content: t, stickers: r, ...}).then((function(isMessageValid)
match: /(props\.chatInputType.+?\.then\(\()(function.+?var (\i)=\i\.\i\.parse\((\i),.+?var (\i)=\i\.\i\.getSendMessageOptionsForReply\(\i\);)(?<=\)\(({.+?})\)\.then.+?)/,
// props.chatInputType...then((async function(isMessageValid)... var replyOptions = f.g.getSendMessageOptionsForReply(pendingReply); if(await Vencord.api...) return { shoudClear:true, shouldRefocus:true };
replace: (_, rest1, rest2, parsedMessage, channel, replyOptions, extra) => "" +
`${rest1}async ${rest2}` +
`if(await Vencord.Api.MessageEvents._handlePreSend(${channel}.id,${parsedMessage},${extra},${replyOptions}))` +
"return{shoudClear:true,shouldRefocus:true};"
}
},
{
find: '("interactionUsernameProfile',

View File

@ -32,7 +32,7 @@ export default definePlugin({
}
},
{
find: "Messages.SERVERS",
find: "Messages.SERVERS,children",
replacement: {
match: /(Messages\.SERVERS,children:)(.+?default:return null\}\}\)\))/,
replace: "$1Vencord.Api.ServerList.renderAll(Vencord.Api.ServerList.ServerListRenderPosition.In).concat($2)"

View File

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Settings } from "@api/settings";
import { Settings } from "@api/Settings";
import { classNameFactory } from "@api/Styles";
import ErrorBoundary from "@components/ErrorBoundary";
import { findByPropsLazy, findStoreLazy } from "@webpack";

View File

@ -18,7 +18,7 @@
import "./betterFolders.css";
import { definePluginSettings } from "@api/settings";
import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types";
import { findByPropsLazy, findLazy, findStoreLazy } from "@webpack";

View File

@ -16,9 +16,8 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Settings } from "@api/settings";
import { Settings } from "@api/Settings";
import { Devs } from "@utils/constants";
import { makeLazy } from "@utils/misc";
import definePlugin, { OptionType } from "@utils/types";
export default definePlugin({
@ -30,7 +29,7 @@ export default definePlugin({
{
find: "hideNote:",
all: true,
predicate: makeLazy(() => Vencord.Settings.plugins.BetterNotesBox.hide),
predicate: () => Vencord.Settings.plugins.BetterNotesBox.hide,
replacement: {
match: /hideNote:.+?(?=[,}])/g,
replace: "hideNote:true",

View File

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Settings } from "@api/settings";
import { Settings } from "@api/Settings";
import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types";
import { Clipboard, Toasts } from "@webpack/common";

View File

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Settings } from "@api/settings";
import { Settings } from "@api/Settings";
import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types";

View File

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Settings } from "@api/settings";
import { Settings } from "@api/Settings";
import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types";

View File

@ -17,9 +17,9 @@
*/
import { showNotification } from "@api/Notifications";
import { definePluginSettings } from "@api/settings";
import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants";
import Logger from "@utils/Logger";
import { Logger } from "@utils/Logger";
import { closeAllModals } from "@utils/modal";
import definePlugin, { OptionType } from "@utils/types";
import { maybePromptToUpdate } from "@utils/updater";

View File

@ -16,11 +16,11 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { definePluginSettings } from "@api/settings";
import { definePluginSettings } from "@api/Settings";
import { Link } from "@components/Link";
import { Devs } from "@utils/constants";
import { isTruthy } from "@utils/guards";
import { useAwaiter } from "@utils/misc";
import { useAwaiter } from "@utils/react";
import definePlugin, { OptionType } from "@utils/types";
import { filters, findByCodeLazy, findByPropsLazy, mapMangledModuleLazy } from "@webpack";
import {

View File

@ -16,15 +16,13 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { addContextMenuPatch, NavContextMenuPatchCallback, removeContextMenuPatch } from "@api/ContextMenu";
import { showNotification } from "@api/Notifications";
import { definePluginSettings } from "@api/settings";
import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants";
import Logger from "@utils/Logger";
import { Logger } from "@utils/Logger";
import { canonicalizeMatch, canonicalizeReplace } from "@utils/patches";
import definePlugin, { OptionType } from "@utils/types";
import { filters, findAll, search } from "@webpack";
import { Menu } from "@webpack/common";
const PORT = 8485;
const NAV_ID = "dev-companion-reconnect";
@ -238,33 +236,25 @@ function initWs(isManual = false) {
});
}
const contextMenuPatch: NavContextMenuPatchCallback = children => () => {
children.unshift(
<Menu.MenuItem
id={NAV_ID}
label="Reconnect Dev Companion"
action={() => {
socket?.close(1000, "Reconnecting");
initWs(true);
}}
/>
);
};
export default definePlugin({
name: "DevCompanion",
description: "Dev Companion Plugin",
authors: [Devs.Ven],
settings,
toolboxActions: {
"Reconnect"() {
socket?.close(1000, "Reconnecting");
initWs(true);
}
},
start() {
initWs();
addContextMenuPatch("user-settings-cog", contextMenuPatch);
},
stop() {
socket?.close(1000, "Plugin Stopped");
socket = void 0;
removeContextMenuPatch("user-settings-cog", contextMenuPatch);
}
});

View File

@ -19,7 +19,7 @@
import { addContextMenuPatch, findGroupChildrenByChildId, NavContextMenuPatchCallback, removeContextMenuPatch } from "@api/ContextMenu";
import { CheckedTextInput } from "@components/CheckedTextInput";
import { Devs } from "@utils/constants";
import Logger from "@utils/Logger";
import { Logger } from "@utils/Logger";
import { Margins } from "@utils/margins";
import { ModalContent, ModalHeader, ModalRoot, openModal } from "@utils/modal";
import definePlugin from "@utils/types";

View File

@ -16,8 +16,11 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { definePluginSettings } from "@api/settings";
import { definePluginSettings } from "@api/Settings";
import ErrorBoundary from "@components/ErrorBoundary";
import { ErrorCard } from "@components/ErrorCard";
import { Devs } from "@utils/constants";
import { Margins } from "@utils/margins";
import definePlugin, { OptionType } from "@utils/types";
import { findByPropsLazy } from "@webpack";
import { Forms, React } from "@webpack/common";
@ -87,6 +90,13 @@ export default definePlugin({
match: /"staging"===window\.GLOBAL_ENV\.RELEASE_CHANNEL/,
replace: "true"
}
},
{
find: 'H1,title:"Experiments"',
replacement: {
match: 'title:"Experiments",children:[',
replace: "$&$self.WarningCard(),"
}
}
],
@ -109,5 +119,19 @@ export default definePlugin({
</Forms.FormText>
</React.Fragment>
);
}
},
WarningCard: ErrorBoundary.wrap(() => (
<ErrorCard id="vc-experiments-warning-card" className={Margins.bottom16}>
<Forms.FormTitle tag="h2">Hold on!!</Forms.FormTitle>
<Forms.FormText>
Experiments are unreleased Discord features. They might not work, or even break your client or get your account disabled.
</Forms.FormText>
<Forms.FormText className={Margins.top8}>
Only use experiments if you know what you're doing. Vencord is not responsible for any damage caused by enabling experiments.
</Forms.FormText>
</ErrorCard>
), { noop: true })
});

View File

@ -17,11 +17,11 @@
*/
import { addPreEditListener, addPreSendListener, removePreEditListener, removePreSendListener } from "@api/MessageEvents";
import { definePluginSettings, Settings } from "@api/settings";
import { definePluginSettings, Settings } from "@api/Settings";
import { Devs } from "@utils/constants";
import { ApngBlendOp, ApngDisposeOp, getGifEncoder, importApngJs } from "@utils/dependencies";
import { getCurrentGuild } from "@utils/discord";
import { proxyLazy } from "@utils/proxyLazy";
import { proxyLazy } from "@utils/lazy";
import definePlugin, { OptionType } from "@utils/types";
import { findByCodeLazy, findByPropsLazy, findLazy, findStoreLazy } from "@webpack";
import { ChannelStore, FluxDispatcher, Parser, PermissionStore, UserStore } from "@webpack/common";
@ -641,7 +641,7 @@ export default definePlugin({
if (!settings.enableStickerBypass)
break stickerBypass;
const sticker = StickerStore.getStickerById(extra?.stickerIds?.[0]!);
const sticker = StickerStore.getStickerById(extra.stickers?.[0]!);
if (!sticker)
break stickerBypass;
@ -663,7 +663,7 @@ export default definePlugin({
link = `https://distok.top/stickers/${packId}/${sticker.id}.gif`;
}
delete extra.stickerIds;
extra.stickers!.length = 0;
messageObj.content += " " + link + `&name=${encodeURIComponent(sticker.name)}`;
}
}

View File

@ -17,7 +17,7 @@
*/
// This plugin is a port from Alyxia's Vendetta plugin
import { definePluginSettings } from "@api/settings";
import { definePluginSettings } from "@api/Settings";
import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants";
import { Margins } from "@utils/margins";

View File

@ -17,7 +17,7 @@
*/
import { ApplicationCommandOptionType } from "@api/Commands";
import { Settings } from "@api/settings";
import { Settings } from "@api/Settings";
import { makeRange } from "@components/PluginSettings/components";
import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types";

View File

@ -19,7 +19,7 @@
import * as DataStore from "@api/DataStore";
import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants";
import { useForceUpdater } from "@utils/misc";
import { useForceUpdater } from "@utils/react";
import definePlugin from "@utils/types";
import { findByPropsLazy, findStoreLazy } from "@webpack";
import { Tooltip } from "webpack/common";

View File

@ -17,7 +17,7 @@
*/
import { addContextMenuPatch, NavContextMenuPatchCallback, removeContextMenuPatch } from "@api/ContextMenu";
import { definePluginSettings } from "@api/settings";
import { definePluginSettings } from "@api/Settings";
import { disableStyle, enableStyle } from "@api/Styles";
import { makeRange } from "@components/PluginSettings/components";
import { Devs } from "@utils/constants";

View File

@ -17,8 +17,8 @@
*/
import { registerCommand, unregisterCommand } from "@api/Commands";
import { Settings } from "@api/settings";
import Logger from "@utils/Logger";
import { Settings } from "@api/Settings";
import { Logger } from "@utils/Logger";
import { Patch, Plugin } from "@utils/types";
import { FluxDispatcher } from "@webpack/common";
import { FluxEvents } from "@webpack/types";
@ -99,9 +99,9 @@ export function startDependenciesRecursive(p: Plugin) {
if (!Settings.plugins[dep].enabled) {
startDependenciesRecursive(Plugins[dep]);
// If the plugin has patches, don't start the plugin, just enable it.
Settings.plugins[dep].enabled = true;
if (Plugins[dep].patches) {
logger.warn(`Enabling dependency ${dep} requires restart.`);
Settings.plugins[dep].enabled = true;
restartNeeded = true;
return;
}

View File

@ -17,7 +17,7 @@
*/
import { addButton, removeButton } from "@api/MessagePopover";
import { definePluginSettings } from "@api/settings";
import { definePluginSettings } from "@api/Settings";
import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants";
import { getStegCloak } from "@utils/dependencies";
@ -85,7 +85,7 @@ function ChatBarIcon() {
onMouseLeave={onMouseLeave}
innerClassName={ButtonWrapperClasses.button}
onClick={() => buildEncModal()}
style={{ padding: "0 4px" }}
style={{ padding: "0 2px", scale: "0.9" }}
>
<div className={ButtonWrapperClasses.buttonWrapper}>
<svg

View File

@ -16,10 +16,10 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { definePluginSettings } from "@api/settings";
import { definePluginSettings } from "@api/Settings";
import { Link } from "@components/Link";
import { Devs } from "@utils/constants";
import Logger from "@utils/Logger";
import { Logger } from "@utils/Logger";
import definePlugin, { OptionType } from "@utils/types";
import { filters, findByPropsLazy, mapMangledModuleLazy } from "@webpack";
import { FluxDispatcher, Forms } from "@webpack/common";

View File

@ -20,31 +20,34 @@ import ErrorBoundary from "@components/ErrorBoundary";
import { Flex } from "@components/Flex";
import { Devs } from "@utils/constants";
import { getCurrentChannel } from "@utils/discord";
import { useForceUpdater } from "@utils/misc";
import definePlugin from "@utils/types";
import { findStoreLazy } from "@webpack";
import { Tooltip } from "@webpack/common";
import { SelectedChannelStore, Tooltip, useStateFromStores } from "@webpack/common";
import { FluxStore } from "@webpack/types";
const counts = {} as Record<string, [number, number]>;
let forceUpdate: () => void;
const GuildMemberCountStore = findStoreLazy("GuildMemberCountStore");
const GuildMemberCountStore = findStoreLazy("GuildMemberCountStore") as FluxStore & { getMemberCount(guildId: string): number | null; };
const ChannelMemberStore = findStoreLazy("ChannelMemberStore") as FluxStore & {
getProps(guildId: string, channelId: string): { groups: { count: number; id: string; }[]; };
};
function MemberCount() {
const guildId = getCurrentChannel().guild_id;
const c = counts[guildId];
const { id: channelId, guild_id: guildId } = useStateFromStores([SelectedChannelStore], () => getCurrentChannel());
const { groups } = useStateFromStores(
[ChannelMemberStore],
() => ChannelMemberStore.getProps(guildId, channelId)
);
const total = useStateFromStores(
[GuildMemberCountStore],
() => GuildMemberCountStore.getMemberCount(guildId)
);
forceUpdate = useForceUpdater();
if (total == null)
return null;
if (!c) return null;
let total = c[0].toLocaleString();
if (total === "0" && c[1] > 0) {
const approx = GuildMemberCountStore.getMemberCount(guildId);
total = approx ? approx.toLocaleString() : "Loading...";
}
const online = c[1].toLocaleString();
const online =
(groups.length === 1 && groups[0].id === "unknown")
? 0
: groups.reduce((count, curr) => count + (curr.id === "offline" ? 0 : curr.count), 0);
return (
<Flex id="vc-membercount" style={{
@ -55,7 +58,7 @@ function MemberCount() {
alignContent: "center",
gap: 0
}}>
<Tooltip text={`${online} Online`} position="bottom">
<Tooltip text={`${online} Online in this Channel`} position="bottom">
{props => (
<div {...props}>
<span
@ -72,7 +75,7 @@ function MemberCount() {
</div>
)}
</Tooltip>
<Tooltip text={`${total} Total Members`} position="bottom">
<Tooltip text={`${total} Total Server Members`} position="bottom">
{props => (
<div {...props}>
<span
@ -102,31 +105,10 @@ export default definePlugin({
patches: [{
find: ".isSidebarVisible,",
replacement: {
match: /(var (.)=.\.className.+?children):\[(.\.useMemo[^}]+"aria-multiselectable")/,
match: /(var (\i)=\i\.className.+?children):\[(\i\.useMemo[^}]+"aria-multiselectable")/,
replace: "$1:[$2.startsWith('members')?$self.render():null,$3"
}
}],
flux: {
GUILD_MEMBER_LIST_UPDATE({ guildId, groups, memberCount, id }) {
// eeeeeh - sometimes it has really wrong counts??? like 10 times less than actual
// but if we only listen to everyone updates, sometimes we never get the count?
// this seems to work but isn't optional
if (id !== "everyone" && counts[guildId]) return;
let count = 0;
for (const group of groups) {
if (group.id !== "offline")
count += group.count;
}
counts[guildId] = [memberCount, count];
forceUpdate?.();
}
},
render: () => (
<ErrorBoundary noop>
<MemberCount />
</ErrorBoundary>
)
render: ErrorBoundary.wrap(MemberCount, { noop: true })
});

View File

@ -17,11 +17,12 @@
*/
import { addAccessory } from "@api/MessageAccessories";
import { definePluginSettings } from "@api/settings";
import { definePluginSettings } from "@api/Settings";
import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants.js";
import { classes, LazyComponent } from "@utils/misc";
import { classes } from "@utils/misc";
import { Queue } from "@utils/Queue";
import { LazyComponent } from "@utils/react";
import definePlugin, { OptionType } from "@utils/types";
import { find, findByCode, findByPropsLazy } from "@webpack";
import {

View File

@ -19,11 +19,11 @@
import "./messageLogger.css";
import { addContextMenuPatch, NavContextMenuPatchCallback, removeContextMenuPatch } from "@api/ContextMenu";
import { Settings } from "@api/settings";
import { Settings } from "@api/Settings";
import { disableStyle, enableStyle } from "@api/Styles";
import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants";
import Logger from "@utils/Logger";
import { Logger } from "@utils/Logger";
import definePlugin, { OptionType } from "@utils/types";
import { findByPropsLazy } from "@webpack";
import { FluxDispatcher, i18n, Menu, moment, Parser, Timestamp, UserStore } from "@webpack/common";

View File

@ -18,7 +18,7 @@
import { ApplicationCommandInputType, ApplicationCommandOptionType, findOption, registerCommand, sendBotMessage, unregisterCommand } from "@api/Commands";
import * as DataStore from "@api/DataStore";
import { Settings } from "@api/settings";
import { Settings } from "@api/Settings";
import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types";

View File

@ -16,9 +16,9 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { definePluginSettings } from "@api/settings";
import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants";
import { proxyLazy } from "@utils/proxyLazy.js";
import { proxyLazy } from "@utils/lazy.js";
import definePlugin, { OptionType } from "@utils/types";
import { find, findByPropsLazy } from "@webpack";
import { ChannelStore, GuildStore } from "@webpack/common";

View File

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { definePluginSettings } from "@api/settings";
import { definePluginSettings } from "@api/Settings";
import { makeRange } from "@components/PluginSettings/components/SettingSliderComponent";
import { Devs } from "@utils/constants";
import { sleep } from "@utils/misc";

View File

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { definePluginSettings } from "@api/settings";
import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants";
import { ModalContent, ModalFooter, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
import definePlugin, { OptionType } from "@utils/types";

View File

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Settings } from "@api/settings";
import { Settings } from "@api/Settings";
import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types";
import { findByPropsLazy } from "@webpack";

View File

@ -1,59 +0,0 @@
/*
* Vencord, a modification for Discord's desktop app
* Copyright (c) 2022 Samu and Linnea Gräf
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { addPreSendListener, MessageObject, removePreSendListener } from "@api/MessageEvents";
import { Settings } from "@api/settings";
import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types";
export default definePlugin({
name: "NoCanaryMessageLinks",
description: "Allows you to change/remove the subdomain of discord message and channel links",
authors: [Devs.Samu, Devs.nea],
options: {
linkPrefix: {
description: "The subdomain for your discord message links",
type: OptionType.STRING,
default: "",
restartNeeded: false,
},
alwaysUseDiscordHost: {
description: "Always use discord.com host (replace discordapp.com)",
type: OptionType.BOOLEAN,
default: false,
restartNeeded: false,
},
},
dependencies: ["MessageEventsAPI"],
removeBetas(msg: MessageObject) {
const settings = Settings.plugins.NoCanaryMessageLinks;
msg.content = msg.content.replace(
/https:\/\/(?:canary\.|ptb\.)?(discord(?:app)?\.com)(\/channels\/(?:\d{17,20}|@me)\/\d{17,20}(?:\/\d{17,20})?)/g,
(_, host, path) => "https://" + (settings.linkPrefix ? settings.linkPrefix + "." : "") + (settings.alwaysUseDiscordHost ? "discord.com" : host) + path
);
},
start() {
this.preSend = addPreSendListener((_, msg) => this.removeBetas(msg));
},
stop() {
removePreSendListener(this.preSend);
}
});

View File

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { definePluginSettings } from "@api/settings";
import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types";
import type { Message } from "discord-types/general";

View File

@ -19,7 +19,7 @@
import { ApplicationCommandInputType, ApplicationCommandOptionType, Argument, CommandContext, findOption, sendBotMessage } from "@api/Commands";
import { Devs } from "@utils/constants";
import { getGifEncoder } from "@utils/dependencies";
import { makeLazy } from "@utils/misc";
import { makeLazy } from "@utils/lazy";
import definePlugin from "@utils/types";
import { findByCodeLazy, findByPropsLazy } from "@webpack";

View File

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { definePluginSettings, Settings, useSettings } from "@api/settings";
import { definePluginSettings, Settings, useSettings } from "@api/Settings";
import { OptionType } from "@utils/types";
import { findStoreLazy } from "@webpack";

View File

@ -19,7 +19,7 @@
import { addBadge, BadgePosition, ProfileBadge, removeBadge } from "@api/Badges";
import { addDecorator, removeDecorator } from "@api/MemberListDecorators";
import { addDecoration, removeDecoration } from "@api/MessageDecorations";
import { Settings } from "@api/settings";
import { Settings } from "@api/Settings";
import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types";
@ -30,7 +30,7 @@ import { User } from "discord-types/general";
const SessionsStore = findStoreLazy("SessionsStore");
function Icon(path: string, viewBox = "0 0 24 24") {
return ({ color, tooltip }: { color: string; tooltip: string; }) => (
return ({ color, tooltip, wantMargin }: { color: string; tooltip: string; wantMargin: boolean; }) => (
<Tooltip text={tooltip} >
{(tooltipProps: any) => (
<svg
@ -39,6 +39,12 @@ function Icon(path: string, viewBox = "0 0 24 24") {
width="20"
viewBox={viewBox}
fill={color}
style={{
marginLeft: wantMargin ? 4 : 0,
verticalAlign: "top",
position: "relative",
top: wantMargin ? 1 : 0,
}}
>
<path d={path} />
</svg>
@ -57,16 +63,16 @@ type Platform = keyof typeof Icons;
const getStatusColor = findByCodeLazy(".TWITCH", ".STREAMING", ".INVISIBLE");
const PlatformIcon = ({ platform, status }: { platform: Platform, status: string; }) => {
const PlatformIcon = ({ platform, status, wantMargin }: { platform: Platform, status: string; wantMargin: boolean; }) => {
const tooltip = platform[0].toUpperCase() + platform.slice(1);
const Icon = Icons[platform] ?? Icons.desktop;
return <Icon color={`var(--${getStatusColor(status)}`} tooltip={tooltip} />;
return <Icon color={`var(--${getStatusColor(status)}`} tooltip={tooltip} wantMargin={wantMargin} />;
};
const getStatus = (id: string): Record<Platform, string> => PresenceStore.getState()?.clientStatuses?.[id];
const PlatformIndicator = ({ user, inline = false, marginLeft = "4px" }: { user: User; inline?: boolean; marginLeft?: string; }) => {
const PlatformIndicator = ({ user, wantMargin = true }: { user: User; wantMargin?: boolean; }) => {
if (!user || user.bot) return null;
if (user.id === UserStore.getCurrentUser().id) {
@ -99,29 +105,21 @@ const PlatformIndicator = ({ user, inline = false, marginLeft = "4px" }: { user:
key={platform}
platform={platform as Platform}
status={status}
wantMargin={wantMargin}
/>
));
if (!icons.length) return null;
return (
<div
className="vc-platform-indicator"
style={{
marginLeft,
gap: "4px",
display: inline ? "inline-flex" : "flex",
alignItems: "center",
translate: inline ? "0 3px 0" : undefined
}}
>
<span className="vc-platform-indicator">
{icons}
</div>
</span>
);
};
const badge: ProfileBadge = {
component: p => <PlatformIndicator {...p} marginLeft="" />,
component: p => <PlatformIndicator {...p} wantMargin={false} />,
position: BadgePosition.START,
shouldShow: userInfo => !!Object.keys(getStatus(userInfo.user.id) ?? {}).length,
key: "indicator"
@ -146,7 +144,7 @@ const indicatorLocations = {
description: "Inside messages",
onEnable: () => addDecoration("platform-indicator", props =>
<ErrorBoundary noop>
<PlatformIndicator user={props.message?.author} inline />
<PlatformIndicator user={props.message?.author} />
</ErrorBoundary>
),
onDisable: () => removeDecoration("platform-indicator")

View File

@ -16,10 +16,10 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Settings } from "@api/settings";
import { Settings } from "@api/Settings";
import { VENCORD_USER_AGENT } from "@utils/constants";
import { debounce } from "@utils/debounce";
import { useAwaiter } from "@utils/misc";
import { useAwaiter } from "@utils/react";
import { UserStore } from "@webpack/common";
import { settings } from "./settings";

View File

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { definePluginSettings } from "@api/settings";
import { definePluginSettings } from "@api/Settings";
import { OptionType } from "@utils/types";
import { PronounsFormat } from "./pronoundbUtils";

View File

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { definePluginSettings, Settings } from "@api/settings";
import { definePluginSettings, Settings } from "@api/Settings";
import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types";
import { findByPropsLazy } from "@webpack";

View File

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { definePluginSettings } from "@api/settings";
import { definePluginSettings } from "@api/Settings";
import { OptionType } from "@utils/types";
export default definePluginSettings({

View File

@ -18,7 +18,7 @@
import { DataStore, Notices } from "@api/index";
import { showNotification } from "@api/Notifications";
import { ChannelStore, GuildStore, RelationshipStore, UserStore, UserUtils } from "@webpack/common";
import { ChannelStore, GuildMemberStore, GuildStore, RelationshipStore, UserStore, UserUtils } from "@webpack/common";
import settings from "./settings";
import { ChannelType, RelationshipType, SimpleGroupChannel, SimpleGuild } from "./types";
@ -106,12 +106,16 @@ export function deleteGuild(id: string) {
}
export async function syncGuilds() {
guilds.clear();
const me = UserStore.getCurrentUser().id;
for (const [id, { name, icon }] of Object.entries(GuildStore.getGuilds())) {
guilds.set(id, {
id,
name,
iconURL: icon && `https://cdn.discordapp.com/icons/${id}/${icon}.png`
});
if (GuildMemberStore.isMember(id, me))
guilds.set(id, {
id,
name,
iconURL: icon && `https://cdn.discordapp.com/icons/${id}/${icon}.png`
});
}
await DataStore.set(guildsKey(), guilds);
}
@ -126,6 +130,8 @@ export function deleteGroup(id: string) {
}
export async function syncGroups() {
groups.clear();
for (const { type, id, name, rawRecipients, icon } of ChannelStore.getSortedPrivateChannels()) {
if (type === ChannelType.GROUP_DM)
groups.set(id, {

View File

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Settings } from "@api/settings";
import { Settings } from "@api/Settings";
import { Review } from "../entities/Review";
import { ReviewDBUser } from "../entities/User";

View File

@ -16,8 +16,8 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Settings } from "@api/settings";
import Logger from "@utils/Logger";
import { Settings } from "@api/Settings";
import { Logger } from "@utils/Logger";
import { openModal } from "@utils/modal";
import { findByProps } from "@webpack";
import { FluxDispatcher, React, SelectedChannelStore, Toasts, UserUtils } from "@webpack/common";

View File

@ -16,11 +16,12 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { classes, LazyComponent } from "@utils/misc";
import { classes } from "@utils/misc";
import { LazyComponent } from "@utils/react";
import { findByProps } from "@webpack";
export default LazyComponent(() => {
const { button, dangerous } = findByProps("button", "wrapper", "disabled","separator");
const { button, dangerous } = findByProps("button", "wrapper", "disabled", "separator");
return function MessageButton(props) {
return props.type === "delete"

View File

@ -16,8 +16,9 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Settings } from "@api/settings";
import { classes, LazyComponent } from "@utils/misc";
import { Settings } from "@api/Settings";
import { classes } from "@utils/misc";
import { LazyComponent } from "@utils/react";
import { filters, findBulk } from "@webpack";
import { Alerts, moment, Timestamp, UserStore } from "@webpack/common";

View File

@ -16,8 +16,9 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Settings } from "@api/settings";
import { classes, useAwaiter } from "@utils/misc";
import { Settings } from "@api/Settings";
import { classes } from "@utils/misc";
import { useAwaiter } from "@utils/react";
import { findLazy } from "@webpack";
import { Forms, React, Text, UserStore } from "@webpack/common";
import type { KeyboardEvent } from "react";

View File

@ -18,7 +18,7 @@
import "./style.css";
import { Settings } from "@api/settings";
import { Settings } from "@api/Settings";
import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types";

View File

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { definePluginSettings } from "@api/settings";
import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types";
import { ChannelStore, GuildMemberStore, GuildStore } from "@webpack/common";

View File

@ -18,7 +18,7 @@
import { addContextMenuPatch, findGroupChildrenByChildId, NavContextMenuPatchCallback, removeContextMenuPatch } from "@api/ContextMenu";
import { Devs } from "@utils/constants";
import { LazyComponent } from "@utils/misc";
import { LazyComponent } from "@utils/react";
import definePlugin from "@utils/types";
import { findByCode, findByCodeLazy } from "@webpack";
import { ChannelStore, i18n, Menu, SelectedChannelStore } from "@webpack/common";

View File

@ -43,7 +43,7 @@
}
.vc-st-button {
padding: 0 8px;
padding: 0 6px;
}
.vc-st-button svg {

View File

@ -17,10 +17,10 @@
*/
import { addServerListElement, removeServerListElement, ServerListRenderPosition } from "@api/ServerList";
import { Settings } from "@api/settings";
import { Settings } from "@api/Settings";
import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants";
import { useForceUpdater } from "@utils/misc";
import { useForceUpdater } from "@utils/react";
import definePlugin, { OptionType } from "@utils/types";
import { GuildStore, PresenceStore, RelationshipStore } from "@webpack/common";

View File

@ -17,11 +17,11 @@
*/
import { addContextMenuPatch } from "@api/ContextMenu";
import { Settings } from "@api/settings";
import { Settings } from "@api/Settings";
import PatchHelper from "@components/PatchHelper";
import { Devs } from "@utils/constants";
import Logger from "@utils/Logger";
import { LazyComponent } from "@utils/misc";
import { Logger } from "@utils/Logger";
import { LazyComponent } from "@utils/react";
import definePlugin, { OptionType } from "@utils/types";
import { SettingsRouter } from "@webpack/common";
@ -155,12 +155,12 @@ export default definePlugin({
},
get electronVersion() {
return VencordNative.getVersions().electron || window.armcord?.electron || null;
return VencordNative.native.getVersions().electron || window.armcord?.electron || null;
},
get chromiumVersion() {
try {
return VencordNative.getVersions().chrome
return VencordNative.native.getVersions().chrome
// @ts-ignore Typescript will add userAgentData IMMEDIATELY
|| navigator.userAgentData?.brands?.find(b => b.brand === "Chromium" || b.brand === "Google Chrome")?.version
|| null;

View File

@ -17,8 +17,7 @@
*/
import ErrorBoundary from "@components/ErrorBoundary";
import { useAwaiter } from "@utils/misc";
import { useIntersection } from "@utils/react";
import { useAwaiter, useIntersection } from "@utils/react";
import { hljs, React } from "@webpack/common";
import { resolveLang } from "../api/languages";

View File

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { definePluginSettings } from "@api/settings";
import { definePluginSettings } from "@api/Settings";
import { disableStyle, enableStyle } from "@api/Styles";
import { parseUrl } from "@utils/misc";
import { wordsFromPascal, wordsToTitle } from "@utils/text";

View File

@ -61,10 +61,7 @@
display: flex;
position: absolute;
justify-content: center;
top: 0;
right: 0;
bottom: 0;
left: 0;
inset: 0;
}
.shiki-preview {

View File

@ -1,6 +1,6 @@
/*
* Vencord, a modification for Discord's desktop app
* Copyright (c) 2022 Samu
* 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
@ -16,27 +16,22 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { addPreSendListener, MessageObject, removePreSendListener } from "@api/MessageEvents";
import { Devs } from "@utils/constants";
import definePlugin from "@utils/types";
const re = /https?:\/\/twitter\.com(?=\/\w+?\/status\/)/g;
export default definePlugin({
name: "FxTwitter",
description: "Uses FxTwitter to improve embeds from twitter on send",
authors: [Devs.Samu],
dependencies: ["MessageEventsAPI"],
name: "ShowAllMessageButtons",
description: "Always show all message buttons no matter if you are holding the shift key or not.",
authors: [Devs.Nuckyz],
addPrefix(msg: MessageObject) {
msg.content = msg.content.replace(re, "https://fxtwitter.com");
},
start() {
this.preSend = addPreSendListener((_, msg) => this.addPrefix(msg));
},
stop() {
removePreSendListener(this.preSend);
}
patches: [
{
find: ".Messages.MESSAGE_UTILITIES_A11Y_LABEL",
replacement: {
// isExpanded: V, (?<=,V = shiftKeyDown && !H...;)
match: /isExpanded:(\i),(?<=,\1=\i&&(!.+);.+?)/,
replace: "isExpanded:$2,"
}
}
]
});

View File

@ -0,0 +1,38 @@
/*
* Vencord, a modification for Discord's desktop app
* Copyright (c) 2023 Vendicated and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { LazyComponent } from "@utils/react";
import { findByCode, findLazy } from "@webpack";
import { i18n, useToken } from "@webpack/common";
const ColorMap = findLazy(m => m.colors?.INTERACTIVE_MUTED?.css);
const VerifiedIconComponent = LazyComponent(() => findByCode(".CONNECTIONS_ROLE_OFFICIAL_ICON_TOOLTIP"));
export function VerifiedIcon() {
const color = useToken(ColorMap.colors.INTERACTIVE_MUTED).hex();
const forcedIconColor = useToken(ColorMap.colors.INTERACTIVE_ACTIVE).hex();
return (
<VerifiedIconComponent
color={color}
forcedIconColor={forcedIconColor}
size={16}
tooltipText={i18n.Messages.CONNECTION_VERIFIED}
/>
);
}

View File

@ -18,15 +18,20 @@
import "./styles.css";
import { definePluginSettings } from "@api/settings";
import { definePluginSettings } from "@api/Settings";
import ErrorBoundary from "@components/ErrorBoundary";
import { Flex } from "@components/Flex";
import { CopyIcon, LinkIcon } from "@components/Icons";
import { Devs } from "@utils/constants";
import { copyWithToast, LazyComponent } from "@utils/misc";
import { copyWithToast } from "@utils/misc";
import { LazyComponent } from "@utils/react";
import definePlugin, { OptionType } from "@utils/types";
import { findByCode, findByCodeLazy, findByPropsLazy, findStoreLazy } from "@webpack";
import { Text, Tooltip } from "@webpack/common";
import { User } from "discord-types/general";
import { VerifiedIcon } from "./VerifiedIcon";
const Section = LazyComponent(() => findByCode("().lastSection"));
const UserProfileStore = findStoreLazy("UserProfileStore");
const ThemeStore = findStoreLazy("ThemeStore");
@ -96,7 +101,13 @@ function ConnectionsComponent({ id, theme }: { id: string, theme: string; }) {
>
Connections
</Text>
{connections.map(connection => <CompactConnectionComponent connection={connection} theme={theme} />)}
<Flex style={{
marginTop: "8px",
gap: getSpacingPx(settings.store.iconSpacing),
flexWrap: "wrap"
}}>
{connections.map(connection => <CompactConnectionComponent connection={connection} theme={theme} />)}
</Flex>
</Section>
);
}
@ -110,17 +121,23 @@ function CompactConnectionComponent({ connection, theme }: { connection: Connect
aria-label={connection.name}
src={theme === "light" ? platform.icon.lightSVG : platform.icon.darkSVG}
style={{
marginTop: getSpacingPx(settings.store.iconSpacing),
marginRight: getSpacingPx(settings.store.iconSpacing),
width: settings.store.iconSize,
height: settings.store.iconSize
}}
/>
);
const TooltipIcon = url ? LinkIcon : CopyIcon;
return (
<Tooltip
text={`${connection.name}${!connection.verified ? " (unverified)" : ""} (click to ${url ? "open" : "copy"})`}
text={
<span className="vc-sc-tooltip">
{connection.name}
{connection.verified && <VerifiedIcon />}
<TooltipIcon height={16} width={16} />
</span>
}
key={connection.id}
>
{tooltipProps =>

View File

@ -3,3 +3,9 @@
display: inline-block;
cursor: pointer;
}
.vc-sc-tooltip {
display: inline-flex;
gap: 0.25em;
align-items: center;
}

View File

@ -17,7 +17,7 @@
*/
import ErrorBoundary from "@components/ErrorBoundary";
import { LazyComponent } from "@utils/misc";
import { LazyComponent } from "@utils/react";
import { formatDuration } from "@utils/text";
import { find, findByPropsLazy, findStoreLazy } from "@webpack";
import { FluxDispatcher, GuildMemberStore, GuildStore, moment, Parser, PermissionStore, SnowflakeUtils, Text, Timestamp, Tooltip } from "@webpack/common";

View File

@ -18,7 +18,7 @@
import "./style.css";
import { definePluginSettings } from "@api/settings";
import { definePluginSettings } from "@api/Settings";
import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants";
import { canonicalizeMatch } from "@utils/patches";

View File

@ -18,13 +18,13 @@
import "./styles.css";
import { definePluginSettings } from "@api/settings";
import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types";
import { Message } from "discord-types/general";
interface UsernameProps {
author: { nick: string };
author: { nick: string; };
message: Message;
withMentionPrefix?: boolean;
isRepliedMessage: boolean;

View File

@ -17,7 +17,7 @@
*/
import { addPreSendListener, removePreSendListener, SendListener } from "@api/MessageEvents";
import { definePluginSettings } from "@api/settings";
import { definePluginSettings } from "@api/Settings";
import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types";
@ -72,7 +72,7 @@ function SilentMessageToggle(chatBoxProps: {
size=""
look={ButtonLooks.BLANK}
innerClassName={ButtonWrapperClasses.button}
style={{ padding: "0 8px" }}
style={{ padding: "0 6px" }}
>
<div className={ButtonWrapperClasses.buttonWrapper}>
<svg

View File

@ -17,7 +17,7 @@
*/
import { ApplicationCommandInputType, ApplicationCommandOptionType, findOption, sendBotMessage } from "@api/Commands";
import { definePluginSettings } from "@api/settings";
import { definePluginSettings } from "@api/Settings";
import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types";
@ -57,7 +57,7 @@ function SilentTypingToggle(chatBoxProps: {
size=""
look={ButtonLooks.BLANK}
innerClassName={ButtonWrapperClasses.button}
style={{ padding: "0 8px" }}
style={{ padding: "0 6px" }}
>
<div className={ButtonWrapperClasses.buttonWrapper}>
<svg width="24" height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512">

View File

@ -16,9 +16,8 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Settings } from "@api/settings";
import IpcEvents from "@utils/IpcEvents";
import { proxyLazy } from "@utils/proxyLazy";
import { Settings } from "@api/Settings";
import { proxyLazy } from "@utils/lazy";
import { findByPropsLazy } from "@webpack";
import { Flux, FluxDispatcher } from "@webpack/common";
@ -94,7 +93,7 @@ export const SpotifyStore = proxyLazy(() => {
? "spotify:" + path.replaceAll("/", (_, idx) => idx === 0 ? "" : ":")
: "https://open.spotify.com" + path;
VencordNative.ipc.invoke(IpcEvents.OPEN_EXTERNAL, url);
VencordNative.native.openExternal(url);
}
// Need to keep track of this manually

Some files were not shown because too many files have changed in this diff Show More