Merge branch 'dev'
This commit is contained in:
commit
3b56cb94a6
67 changed files with 451 additions and 443 deletions
|
|
@ -1,3 +1,4 @@
|
|||
export * from "./channel";
|
||||
export * from "./commands";
|
||||
export * from "./messages";
|
||||
export * from "./channel";
|
||||
export * from "./misc";
|
||||
|
|
|
|||
4
packages/discord-types/enums/misc.ts
Normal file
4
packages/discord-types/enums/misc.ts
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
export const enum CloudUploadPlatform {
|
||||
REACT_NATIVE = 0,
|
||||
WEB = 1,
|
||||
}
|
||||
1
packages/discord-types/src/index.d.ts
vendored
1
packages/discord-types/src/index.d.ts
vendored
|
|
@ -4,6 +4,7 @@ export * from "./components";
|
|||
export * from "./flux";
|
||||
export * from "./fluxEvents";
|
||||
export * from "./menu";
|
||||
export * from "./modules";
|
||||
export * from "./stores";
|
||||
export * from "./passiveupdatestate";
|
||||
export * from "./utils";
|
||||
|
|
|
|||
74
packages/discord-types/src/modules/CloudUpload.d.ts
vendored
Normal file
74
packages/discord-types/src/modules/CloudUpload.d.ts
vendored
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
import EventEmitter from "events";
|
||||
import { CloudUploadPlatform } from "../../enums";
|
||||
|
||||
interface BaseUploadItem {
|
||||
platform: CloudUploadPlatform;
|
||||
id?: string;
|
||||
origin?: string;
|
||||
isThumbnail?: boolean;
|
||||
clip?: unknown;
|
||||
}
|
||||
|
||||
export interface ReactNativeUploadItem extends BaseUploadItem {
|
||||
platform: CloudUploadPlatform.REACT_NATIVE;
|
||||
uri: string;
|
||||
filename?: string;
|
||||
mimeType?: string;
|
||||
durationSecs?: number;
|
||||
waveform?: string;
|
||||
isRemix?: boolean;
|
||||
}
|
||||
|
||||
export interface WebUploadItem extends BaseUploadItem {
|
||||
platform: CloudUploadPlatform.WEB;
|
||||
file: File;
|
||||
}
|
||||
|
||||
export type CloudUploadItem = ReactNativeUploadItem | WebUploadItem;
|
||||
|
||||
export class CloudUpload extends EventEmitter {
|
||||
constructor(item: CloudUploadItem, channelId: string, showLargeMessageDialog?: boolean, reactNativeFileIndex?: number);
|
||||
|
||||
channelId: string;
|
||||
classification: string;
|
||||
clip: unknown;
|
||||
contentHash: unknown;
|
||||
currentSize: number;
|
||||
description: string | null;
|
||||
durationSecs: number | undefined;
|
||||
etag: string | undefined;
|
||||
error: unknown;
|
||||
filename: string;
|
||||
id: string;
|
||||
isImage: boolean;
|
||||
isRemix: boolean | undefined;
|
||||
isThumbnail: boolean;
|
||||
isVideo: boolean;
|
||||
item: {
|
||||
file: File;
|
||||
platform: CloudUploadPlatform;
|
||||
origin: string;
|
||||
};
|
||||
loaded: number;
|
||||
mimeType: string;
|
||||
origin: string;
|
||||
postCompressionSize: number | undefined;
|
||||
preCompressionSize: number;
|
||||
responseUrl: string;
|
||||
sensitive: boolean;
|
||||
showLargeMessageDialog: boolean;
|
||||
spoiler: boolean;
|
||||
startTime: number;
|
||||
status: "NOT_STARTED" | "STARTED" | "UPLOADING" | "ERROR" | "COMPLETED" | "CANCELLED" | "REMOVED_FROM_MSG_DRAFT";
|
||||
uniqueId: string;
|
||||
uploadedFilename: string;
|
||||
waveform: string | undefined;
|
||||
|
||||
// there are many more methods than just these but I didn't find them particularly useful
|
||||
upload(): Promise<void>;
|
||||
cancel(): void;
|
||||
delete(): Promise<void>;
|
||||
getSize(): number;
|
||||
maybeConvertToWebP(): Promise<void>;
|
||||
removeFromMsgDraft(): void;
|
||||
}
|
||||
1
packages/discord-types/src/modules/index.d.ts
vendored
Normal file
1
packages/discord-types/src/modules/index.d.ts
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
export * from "./CloudUpload";
|
||||
|
|
@ -17,7 +17,7 @@
|
|||
*/
|
||||
|
||||
import { Logger } from "@utils/Logger";
|
||||
import type { Channel, CustomEmoji, Message } from "@vencord/discord-types";
|
||||
import type { Channel, CloudUpload, CustomEmoji, Message } from "@vencord/discord-types";
|
||||
import { MessageStore } from "@webpack/common";
|
||||
import type { Promisable } from "type-fest";
|
||||
|
||||
|
|
@ -30,32 +30,6 @@ export interface MessageObject {
|
|||
tts: boolean;
|
||||
}
|
||||
|
||||
export interface Upload {
|
||||
classification: string;
|
||||
currentSize: number;
|
||||
description: string | null;
|
||||
filename: string;
|
||||
id: string;
|
||||
isImage: boolean;
|
||||
isRemix?: boolean;
|
||||
isThumbnail: 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?: {
|
||||
|
|
@ -68,7 +42,7 @@ export interface MessageReplyOptions {
|
|||
|
||||
export interface MessageOptions {
|
||||
stickers?: string[];
|
||||
uploads?: Upload[];
|
||||
uploads?: CloudUpload[];
|
||||
replyOptions: MessageReplyOptions;
|
||||
content: string;
|
||||
channel: Channel;
|
||||
|
|
|
|||
|
|
@ -16,7 +16,10 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { isPrimitiveReactNode } from "@utils/react";
|
||||
import { waitFor } from "@webpack";
|
||||
import { ReactNode } from "react";
|
||||
|
||||
let NoticesModule: any;
|
||||
waitFor(m => m.show && m.dismiss && !m.suppressAll, m => NoticesModule = m);
|
||||
|
|
@ -36,7 +39,11 @@ export function nextNotice() {
|
|||
}
|
||||
}
|
||||
|
||||
export function showNotice(message: string, buttonText: string, onOkClick: () => void) {
|
||||
noticesQueue.push(["GENERIC", message, buttonText, onOkClick]);
|
||||
export function showNotice(message: ReactNode, buttonText: string, onOkClick: () => void) {
|
||||
const notice = isPrimitiveReactNode(message)
|
||||
? message
|
||||
: <ErrorBoundary fallback={() => "Error Showing Notice"}>{message}</ErrorBoundary>;
|
||||
|
||||
noticesQueue.push(["GENERIC", notice, buttonText, onOkClick]);
|
||||
if (!currentNotice) nextNotice();
|
||||
}
|
||||
|
|
@ -4,9 +4,9 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { initWs } from "@plugins/devCompanion.dev/initWs";
|
||||
import { Logger } from "@utils/Logger";
|
||||
import * as Webpack from "@webpack";
|
||||
import { initWs } from "plugins/devCompanion.dev/initWs";
|
||||
import { getBuildNumber, patchTimings } from "webpack/patchWebpack";
|
||||
|
||||
import { addPatch, patches } from "../plugins";
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
|
||||
import { migratePluginSettings } from "@api/Settings";
|
||||
import { EquicordDevs } from "@utils/constants";
|
||||
import { Devs } from "@utils/constants";
|
||||
import { openUserProfile } from "@utils/discord";
|
||||
import definePlugin from "@utils/types";
|
||||
import { User } from "@vencord/discord-types";
|
||||
|
|
@ -14,7 +14,7 @@ migratePluginSettings("AlwaysExpandProfiles", "AlwaysExpandProfile");
|
|||
export default definePlugin({
|
||||
name: "AlwaysExpandProfiles",
|
||||
description: "Always expands profile popouts to the full modal",
|
||||
authors: [EquicordDevs.thororen],
|
||||
authors: [Devs.thororen],
|
||||
patches: [
|
||||
{
|
||||
find: '"view-profile"',
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
import "./styles.css";
|
||||
|
||||
import { migratePluginSettings } from "@api/Settings";
|
||||
import { Devs, EquicordDevs } from "@utils/constants";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin from "@utils/types";
|
||||
|
||||
import { patchActivityList } from "./patch-helpers/activityList";
|
||||
|
|
@ -19,12 +19,7 @@ migratePluginSettings("BetterActivities", "MemberListActivities");
|
|||
export default definePlugin({
|
||||
name: "BetterActivities",
|
||||
description: "Shows activity icons in the member list and allows showing all activities",
|
||||
authors: [
|
||||
Devs.D3SOX,
|
||||
Devs.Arjix,
|
||||
Devs.AutumnVN,
|
||||
EquicordDevs.thororen
|
||||
],
|
||||
authors: [Devs.D3SOX, Devs.Arjix, Devs.AutumnVN, Devs.thororen],
|
||||
tags: ["activity"],
|
||||
|
||||
settings,
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { EquicordDevs } from "@utils/constants";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
|
||||
const settings = definePluginSettings({
|
||||
|
|
@ -19,7 +19,7 @@ const settings = definePluginSettings({
|
|||
export default definePlugin({
|
||||
name: "BetterCommands",
|
||||
description: "Enhances the command system with miscellaneous improvements.",
|
||||
authors: [EquicordDevs.thororen],
|
||||
authors: [Devs.thororen],
|
||||
settings,
|
||||
patches: [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -4,13 +4,13 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { EquicordDevs } from "@utils/constants";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin from "@utils/types";
|
||||
|
||||
export default definePlugin({
|
||||
name: "BypassPinPrompt",
|
||||
description: "Bypass the pin prompt when using the pin functions",
|
||||
authors: [EquicordDevs.thororen],
|
||||
authors: [Devs.thororen],
|
||||
patches: [
|
||||
{
|
||||
find: '"Channel Pins"',
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
import "./style.css";
|
||||
|
||||
import { EquicordDevs } from "@utils/constants";
|
||||
import { Devs, EquicordDevs } from "@utils/constants";
|
||||
import definePlugin from "@utils/types";
|
||||
import { Channel } from "@vencord/discord-types";
|
||||
import { GuildStore, React, SelectedGuildStore } from "@webpack/common";
|
||||
|
|
@ -32,7 +32,7 @@ function renderBadge(id: number, title: string) {
|
|||
export default definePlugin({
|
||||
name: "ChannelBadges",
|
||||
description: "Adds badges to channels based on their type",
|
||||
authors: [EquicordDevs.creations, EquicordDevs.thororen],
|
||||
authors: [EquicordDevs.creations, Devs.thororen],
|
||||
settings,
|
||||
patches: [
|
||||
// TY TypingIndicator
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
import "./style.css";
|
||||
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { EquicordDevs } from "@utils/constants";
|
||||
import { Devs, EquicordDevs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { UserStore } from "@webpack/common";
|
||||
|
||||
|
|
@ -22,7 +22,7 @@ const settings = definePluginSettings({
|
|||
export default definePlugin({
|
||||
name: "CharacterCounter",
|
||||
description: "Adds a character counter to the chat input",
|
||||
authors: [EquicordDevs.creations, EquicordDevs.Panniku, EquicordDevs.thororen],
|
||||
authors: [EquicordDevs.creations, EquicordDevs.Panniku, Devs.thororen],
|
||||
settings,
|
||||
patches: [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
import "./styles.css";
|
||||
|
||||
import { EquicordDevs } from "@utils/constants";
|
||||
import { Devs, EquicordDevs } from "@utils/constants";
|
||||
import definePlugin from "@utils/types";
|
||||
|
||||
import { makeDevBanner, settings } from "./components";
|
||||
|
|
@ -14,7 +14,7 @@ import { makeDevBanner, settings } from "./components";
|
|||
export default definePlugin({
|
||||
name: "DiscordDevBanner",
|
||||
description: "Enables the Discord developer banner, in which displays the build-ID",
|
||||
authors: [EquicordDevs.KrystalSkull, EquicordDevs.thororen],
|
||||
authors: [EquicordDevs.KrystalSkull, Devs.thororen],
|
||||
settings,
|
||||
patches: [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
import "@equicordplugins/_misc/styles.css";
|
||||
|
||||
import { EQUICORD_HELPERS, EquicordDevs, GUILD_ID } from "@utils/constants";
|
||||
import { Devs, EQUICORD_HELPERS, EquicordDevs, GUILD_ID } from "@utils/constants";
|
||||
import { Logger } from "@utils/Logger";
|
||||
import definePlugin from "@utils/types";
|
||||
import { Button, ChannelStore, Flex, Forms, GuildMemberStore, showToast, Toasts } from "@webpack/common";
|
||||
|
|
@ -30,7 +30,7 @@ import { toggleEnabled } from "./utils";
|
|||
export default definePlugin({
|
||||
name: "EquicordHelper",
|
||||
description: "Fixes some misc issues with discord",
|
||||
authors: [EquicordDevs.thororen, EquicordDevs.nyx, EquicordDevs.Naibuu],
|
||||
authors: [Devs.thororen, EquicordDevs.nyx, EquicordDevs.Naibuu],
|
||||
settingsAboutComponent: () => <>
|
||||
<Forms.FormText className="plugin-warning" style={{ textAlign: "left" }}>
|
||||
This plugin was created to allow us as the Equicord Team & Contributors
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { EquicordDevs } from "@utils/constants";
|
||||
import { Devs, EquicordDevs } from "@utils/constants";
|
||||
import definePlugin from "@utils/types";
|
||||
|
||||
let clickCount = 0;
|
||||
|
|
@ -33,7 +33,7 @@ function play() {
|
|||
export default definePlugin({
|
||||
name: "Equissant",
|
||||
description: "Crossant every 10 clicks :trolley:",
|
||||
authors: [EquicordDevs.SomeAspy, EquicordDevs.thororen],
|
||||
authors: [EquicordDevs.SomeAspy, Devs.thororen],
|
||||
start() {
|
||||
document.addEventListener("click", play);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { EquicordDevs } from "@utils/constants";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { Constants, PermissionsBits, PermissionStore, React, RestAPI, useCallback, useEffect, useState } from "@webpack/common";
|
||||
|
||||
|
|
@ -74,7 +74,7 @@ const settings = definePluginSettings({
|
|||
export default definePlugin({
|
||||
name: "FastDeleteChannels",
|
||||
description: "Adds a trash icon to delete channels",
|
||||
authors: [EquicordDevs.thororen],
|
||||
authors: [Devs.thororen],
|
||||
settings,
|
||||
patches: [
|
||||
// TY TypingIndicator
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
import { Upload } from "@api/MessageEvents";
|
||||
import { Settings } from "@api/Settings";
|
||||
import { tarExtMatcher } from "@plugins/anonymiseFileNames";
|
||||
import { EquicordDevs } from "@utils/constants";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { ReporterTestable } from "@utils/types";
|
||||
|
||||
const extensionMap = {
|
||||
|
|
@ -26,7 +26,7 @@ export const reverseExtensionMap = Object.entries(extensionMap).reduce((acc, [ta
|
|||
|
||||
export default definePlugin({
|
||||
name: "FixFileExtensions",
|
||||
authors: [EquicordDevs.thororen],
|
||||
authors: [Devs.thororen],
|
||||
description: "Fixes file extensions by renaming them to a compatible supported format if possible",
|
||||
reporterTestable: ReporterTestable.None,
|
||||
patches: [
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { EquicordDevs } from "@utils/constants";
|
||||
import { Devs } from "@utils/constants";
|
||||
import { sendMessage } from "@utils/discord";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { Message } from "@vencord/discord-types";
|
||||
|
|
@ -25,7 +25,7 @@ const settings = definePluginSettings({
|
|||
export default definePlugin({
|
||||
name: "ForwardAnywhere",
|
||||
description: "If a forward fails send it as a normal message also allows nsfw forwards",
|
||||
authors: [EquicordDevs.thororen],
|
||||
authors: [Devs.thororen],
|
||||
settings,
|
||||
patches: [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ 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],
|
||||
authors: [EquicordDevs.Cortex, Devs.Samwich, EquicordDevs.Synth, Devs.thororen],
|
||||
contextMenus: {
|
||||
"guild-context": Patch,
|
||||
"guild-header-popout": Patch
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import "@equicordplugins/_misc/styles.css";
|
|||
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { disableStyle, enableStyle } from "@api/Styles";
|
||||
import { EquicordDevs } from "@utils/constants";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
|
||||
import clanBadges from "../_misc/clanBadges.css?managed";
|
||||
|
|
@ -46,7 +46,7 @@ const settings = definePluginSettings({
|
|||
export default definePlugin({
|
||||
name: "GuildTagSettings",
|
||||
description: "Settings for Guild Tags",
|
||||
authors: [EquicordDevs.thororen],
|
||||
authors: [Devs.thororen],
|
||||
settings,
|
||||
patches: [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { Devs, EquicordDevs } from "@utils/constants";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin from "@utils/types";
|
||||
import { VoiceState } from "@vencord/discord-types";
|
||||
import { findByCodeLazy } from "@webpack";
|
||||
|
|
@ -38,7 +38,7 @@ async function autoStartStream() {
|
|||
export default definePlugin({
|
||||
name: "InstantScreenshare",
|
||||
description: "Instantly screenshare when joining a voice channel",
|
||||
authors: [Devs.HAHALOSAH, EquicordDevs.thororen],
|
||||
authors: [Devs.HAHALOSAH, Devs.thororen],
|
||||
settings,
|
||||
flux: {
|
||||
async VOICE_STATE_UPDATES({ voiceStates }: { voiceStates: VoiceState[]; }) {
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ migratePluginSettings("MoreCommands", "CuteAnimeBoys", "CuteNekos", "CutePats",
|
|||
export default definePlugin({
|
||||
name: "MoreCommands",
|
||||
description: "Adds various fun and useful commands",
|
||||
authors: [Devs.Arjix, Devs.amy, Devs.Samu, EquicordDevs.zyqunix, EquicordDevs.ShadyGoat, EquicordDevs.thororen, Devs.Korbo],
|
||||
authors: [Devs.Arjix, Devs.amy, Devs.Samu, EquicordDevs.zyqunix, EquicordDevs.ShadyGoat, Devs.thororen, Devs.Korbo],
|
||||
commands: [
|
||||
...choice,
|
||||
...system,
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ import { YtmPlayer } from "./youtubeMusic/PlayerComponent";
|
|||
export default definePlugin({
|
||||
name: "MusicControls",
|
||||
description: "Music Controls and Lyrics for multiple services ",
|
||||
authors: [Devs.Ven, Devs.afn, Devs.KraXen72, Devs.Av32000, Devs.nin0dev, EquicordDevs.thororen, EquicordDevs.vmohammad, Devs.Joona],
|
||||
authors: [Devs.Ven, Devs.afn, Devs.KraXen72, Devs.Av32000, Devs.nin0dev, Devs.thororen, EquicordDevs.vmohammad, Devs.Joona],
|
||||
settings,
|
||||
tags: [
|
||||
// Spotify
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import "./spotifyStyles.css";
|
|||
import { Settings } from "@api/Settings";
|
||||
import { classNameFactory } from "@api/Styles";
|
||||
import { Flex } from "@components/Flex";
|
||||
import { ImageIcon, LinkIcon, OpenExternalIcon } from "@components/Icons";
|
||||
import { CopyIcon, ImageIcon, LinkIcon, OpenExternalIcon } from "@components/Icons";
|
||||
import { debounce } from "@shared/debounce";
|
||||
import { openImageModal } from "@utils/discord";
|
||||
import { classes, copyWithToast } from "@utils/misc";
|
||||
|
|
@ -76,27 +76,28 @@ function Button(props: React.ButtonHTMLAttributes<HTMLButtonElement>) {
|
|||
);
|
||||
}
|
||||
|
||||
function CopyContextMenu({ name, path }: { name: string; path: string; }) {
|
||||
const copyId = `spotify-copy-${name}`;
|
||||
const openId = `spotify-open-${name}`;
|
||||
|
||||
function CopyContextMenu({ name, type, path }: { type: string; name: string; path: string; }) {
|
||||
return (
|
||||
<Menu.Menu
|
||||
navId={`spotify-${name}-menu`}
|
||||
onClose={() => FluxDispatcher.dispatch({ type: "CONTEXT_MENU_CLOSE" })}
|
||||
aria-label={`Spotify ${name} Menu`}
|
||||
navId="vc-spotify-menu"
|
||||
onClose={ContextMenuApi.closeContextMenu}
|
||||
aria-label={`Spotify ${type} Menu`}
|
||||
>
|
||||
<Menu.MenuItem
|
||||
key={copyId}
|
||||
id={copyId}
|
||||
label={`Copy ${name} Link`}
|
||||
id="vc-spotify-copy-name"
|
||||
label={`Copy ${type} Name`}
|
||||
action={() => copyWithToast(name)}
|
||||
icon={CopyIcon}
|
||||
/>
|
||||
<Menu.MenuItem
|
||||
id="vc-spotify-copy-link"
|
||||
label={`Copy ${type} Link`}
|
||||
action={() => copyWithToast("https://open.spotify.com" + path)}
|
||||
icon={LinkIcon}
|
||||
/>
|
||||
<Menu.MenuItem
|
||||
key={openId}
|
||||
id={openId}
|
||||
label={`Open ${name} in Spotify`}
|
||||
id="vc-spotify-open"
|
||||
label={`Open ${type} in Spotify`}
|
||||
action={() => SpotifyStore.openExternal(path)}
|
||||
icon={OpenExternalIcon}
|
||||
/>
|
||||
|
|
@ -104,11 +105,6 @@ function CopyContextMenu({ name, path }: { name: string; path: string; }) {
|
|||
);
|
||||
}
|
||||
|
||||
function makeContextMenu(name: string, path: string) {
|
||||
return (e: React.MouseEvent<HTMLElement, MouseEvent>) =>
|
||||
ContextMenuApi.openContextMenu(e, () => <CopyContextMenu name={name} path={path} />);
|
||||
}
|
||||
|
||||
function Controls() {
|
||||
const [isPlaying, shuffle, repeat] = useStateFromStores(
|
||||
[SpotifyStore],
|
||||
|
|
@ -259,13 +255,14 @@ function AlbumContextMenu({ track }: { track: Track; }) {
|
|||
);
|
||||
}
|
||||
|
||||
function makeLinkProps(name: string, condition: unknown, path: string) {
|
||||
function makeLinkProps(type: "Song" | "Artist" | "Album", condition: unknown, name: string, path: string) {
|
||||
if (!condition) return {};
|
||||
|
||||
return {
|
||||
role: "link",
|
||||
onClick: () => SpotifyStore.openExternal(path),
|
||||
onContextMenu: makeContextMenu(name, path)
|
||||
onContextMenu: e =>
|
||||
ContextMenuApi.openContextMenu(e, () => <CopyContextMenu type={type} name={name} path={path} />)
|
||||
} satisfies React.HTMLAttributes<HTMLElement>;
|
||||
}
|
||||
|
||||
|
|
@ -306,7 +303,7 @@ function Info({ track }: { track: Track; }) {
|
|||
id={cl("song-title")}
|
||||
className={cl("ellipoverflow")}
|
||||
title={track.name}
|
||||
{...makeLinkProps("Song", track.id, `/track/${track.id}`)}
|
||||
{...makeLinkProps("Song", track.id, track.name, `/track/${track.id}`)}
|
||||
>
|
||||
{track.name}
|
||||
</Forms.FormText>
|
||||
|
|
@ -319,7 +316,7 @@ function Info({ track }: { track: Track; }) {
|
|||
className={cl("artist")}
|
||||
style={{ fontSize: "inherit" }}
|
||||
title={a.name}
|
||||
{...makeLinkProps("Artist", a.id, `/artist/${a.id}`)}
|
||||
{...makeLinkProps("Artist", a.id, a.name, `/artist/${a.id}`)}
|
||||
>
|
||||
{a.name}
|
||||
</span>
|
||||
|
|
@ -336,7 +333,7 @@ function Info({ track }: { track: Track; }) {
|
|||
className={cl("album")}
|
||||
style={{ fontSize: "inherit" }}
|
||||
title={track.album.name}
|
||||
{...makeLinkProps("Album", track.album.id, `/album/${track.album.id}`)}
|
||||
{...makeLinkProps("Album", track.album.id, track.album.name, `/album/${track.album.id}`)}
|
||||
>
|
||||
{track.album.name}
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { EquicordDevs } from "@utils/constants";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin from "@utils/types";
|
||||
import { User } from "@vencord/discord-types";
|
||||
import { UserStore } from "@webpack/common";
|
||||
|
|
@ -36,7 +36,7 @@ function ready(user: ModifiedUser) {
|
|||
export default definePlugin({
|
||||
name: "NoNitroUpsell",
|
||||
description: "Removes ALL of Discord's nitro upsells by tricking the client into thinking you have nitro.",
|
||||
authors: [EquicordDevs.thororen],
|
||||
authors: [Devs.thororen],
|
||||
patches: [
|
||||
{
|
||||
find: "#{intl::USER_PROFILE_ENTRY_POINTS_AMP_UP_YOUR_PROFILE}",
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import { definePluginSettings } from "@api/Settings";
|
|||
import { disableStyle, enableStyle } from "@api/Styles";
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { debounce } from "@shared/debounce";
|
||||
import { EquicordDevs } from "@utils/constants";
|
||||
import { Devs, EquicordDevs } from "@utils/constants";
|
||||
import definePlugin, { makeRange, OptionType } from "@utils/types";
|
||||
import { findByCode, findByProps, findByPropsLazy, findComponentByCodeLazy } from "@webpack";
|
||||
import { ChannelRouter, ChannelStore, ContextMenuApi, GuildStore, MediaEngineStore, Menu, PermissionsBits, PermissionStore, React, SelectedChannelStore, Toasts, UserStore, VoiceActions, VoiceStateStore } from "@webpack/common";
|
||||
|
|
@ -162,7 +162,7 @@ interface VoiceState {
|
|||
export default definePlugin({
|
||||
name: "RandomVoice",
|
||||
description: "Adds a Button near the Mute button to join a random voice call.",
|
||||
authors: [EquicordDevs.xijexo, EquicordDevs.omaw, EquicordDevs.thororen],
|
||||
authors: [EquicordDevs.xijexo, EquicordDevs.omaw, Devs.thororen],
|
||||
patches: [
|
||||
{
|
||||
find: "#{intl::ACCOUNT_SPEAKING_WHILE_MUTED}",
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
import { ChatBarButton, ChatBarButtonFactory } from "@api/ChatButtons";
|
||||
import { showNotification } from "@api/Notifications";
|
||||
import { EquicordDevs } from "@utils/constants";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin from "@utils/types";
|
||||
import { saveFile } from "@utils/web";
|
||||
import { findComponentByCodeLazy } from "@webpack";
|
||||
|
|
@ -56,6 +56,6 @@ const button: ChatBarButtonFactory = ({ isMainChat }) => {
|
|||
export default definePlugin({
|
||||
name: "SaveFavoriteGIFs",
|
||||
description: "Save favorite GIF urls to a file",
|
||||
authors: [EquicordDevs.thororen],
|
||||
authors: [Devs.thororen],
|
||||
renderChatBarButton: button,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import { getListeners } from "./utils";
|
|||
|
||||
export default definePlugin({
|
||||
name: "SoundBoardLogger",
|
||||
authors: [Devs.Moxxie, EquicordDevs.Fres, Devs.amy, EquicordDevs.thororen],
|
||||
authors: [Devs.Moxxie, EquicordDevs.Fres, Devs.amy, Devs.thororen],
|
||||
description: "Logs all soundboards that are played in a voice chat and allows you to download them",
|
||||
dependencies: ["ChatInputButtonAPI"],
|
||||
patches: [
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { EquicordDevs } from "@utils/constants";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin from "@utils/types";
|
||||
import { findComponentByCodeLazy } from "@webpack";
|
||||
import { Constants, React, RestAPI, useEffect, useState } from "@webpack/common";
|
||||
|
|
@ -81,7 +81,7 @@ function SpotifyActivityToggleButton() {
|
|||
export default definePlugin({
|
||||
name: "SpotifyActivityToggle",
|
||||
description: "Adds a toggle button for Spotify activity visibility.",
|
||||
authors: [EquicordDevs.thororen],
|
||||
authors: [Devs.thororen],
|
||||
patches: [
|
||||
{
|
||||
find: "#{intl::ACCOUNT_SPEAKING_WHILE_MUTED}",
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { getUserSettingLazy } from "@api/UserSettings";
|
||||
import { EquicordDevs } from "@utils/constants";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { PresenceStore, UserStore } from "@webpack/common";
|
||||
|
||||
|
|
@ -41,7 +41,7 @@ const settings = definePluginSettings({
|
|||
export default definePlugin({
|
||||
name: "StatusWhilePlaying",
|
||||
description: "Automatically updates your online status (online, idle, dnd) when launching games",
|
||||
authors: [EquicordDevs.thororen],
|
||||
authors: [Devs.thororen],
|
||||
settings,
|
||||
flux: {
|
||||
RUNNING_GAMES_CHANGE(event) {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
*/
|
||||
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { EquicordDevs } from "@utils/constants";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
|
||||
const settings = definePluginSettings({
|
||||
|
|
@ -32,7 +32,7 @@ const settings = definePluginSettings({
|
|||
export default definePlugin({
|
||||
name: "UnlimitedAccounts",
|
||||
description: "Increases the amount of accounts you can add.",
|
||||
authors: [EquicordDevs.thororen],
|
||||
authors: [Devs.thororen],
|
||||
settings,
|
||||
patches: [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { Link } from "@components/Link";
|
||||
import { EquicordDevs } from "@utils/constants";
|
||||
import { Devs, EquicordDevs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { User } from "@vencord/discord-types";
|
||||
|
||||
|
|
@ -29,7 +29,7 @@ export default definePlugin({
|
|||
data,
|
||||
name: "UserPFP",
|
||||
description: "Allows you to use an animated avatar without Nitro",
|
||||
authors: [EquicordDevs.nexpid, EquicordDevs.thororen],
|
||||
authors: [EquicordDevs.nexpid, Devs.thororen],
|
||||
settings,
|
||||
settingsAboutComponent: () => (
|
||||
<>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { EquicordDevs } from "@utils/constants";
|
||||
import { Devs, EquicordDevs } from "@utils/constants";
|
||||
import { isEquicordPluginDev, isPluginDev } from "@utils/misc";
|
||||
import definePlugin from "@utils/types";
|
||||
import { UserStore } from "@webpack/common";
|
||||
|
|
@ -12,7 +12,7 @@ import { UserStore } from "@webpack/common";
|
|||
export default definePlugin({
|
||||
name: "VCSupport",
|
||||
description: "Wumpus Dance + Support Warnings",
|
||||
authors: [EquicordDevs.thororen, EquicordDevs.coolesding],
|
||||
authors: [Devs.thororen, EquicordDevs.coolesding],
|
||||
required: true,
|
||||
start() {
|
||||
const selfId = UserStore.getCurrentUser()?.id;
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ let clientOldChannelId: string | undefined;
|
|||
export default definePlugin({
|
||||
name: "VoiceChannelLog",
|
||||
description: "Logs who joins and leaves voice channels",
|
||||
authors: [Devs.Sqaaakoi, EquicordDevs.thororen, EquicordDevs.nyx],
|
||||
authors: [Devs.Sqaaakoi, Devs.thororen, EquicordDevs.nyx],
|
||||
contextMenus: {
|
||||
"channel-context": patchChannelContextMenu
|
||||
},
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { Devs, EquicordDevs } from "@utils/constants";
|
||||
import { Devs } from "@utils/constants";
|
||||
import { humanFriendlyJoin } from "@utils/text";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { Message, User } from "@vencord/discord-types";
|
||||
|
|
@ -104,7 +104,7 @@ let clientOldChannelId: string | undefined;
|
|||
export default definePlugin({
|
||||
name: "VoiceJoinMessages",
|
||||
description: "Recieve client-side ephemeral messages when your friends join voice channels",
|
||||
authors: [Devs.Sqaaakoi, EquicordDevs.thororen],
|
||||
authors: [Devs.Sqaaakoi, Devs.thororen],
|
||||
settings,
|
||||
flux: {
|
||||
VOICE_STATE_UPDATES({ voiceStates }: { voiceStates: VoiceState[]; }) {
|
||||
|
|
|
|||
1
src/globals.d.ts
vendored
1
src/globals.d.ts
vendored
|
|
@ -65,6 +65,7 @@ declare global {
|
|||
*/
|
||||
export var DiscordNative: any;
|
||||
export var Vesktop: any;
|
||||
export var Equibop: any;
|
||||
export var VesktopNative: any;
|
||||
|
||||
interface Window extends Record<PropertyKey, any> {
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ export default definePlugin({
|
|||
{
|
||||
find: "#{intl::MESSAGE_UTILITIES_A11Y_LABEL}",
|
||||
replacement: {
|
||||
match: /(?<=:null),(.{0,40}togglePopout:.+?}\)),(.+?)\]}\):null,(?<=\((\i\.\i),{label:.+?:null,(\i)\?\(0,\i\.jsxs?\)\(\i\.Fragment.+?message:(\i).+?)/,
|
||||
match: /(?<=\]\}\)),(.{0,40}togglePopout:.+?}\)),(.+?)\]}\):null,(?<=\((\i\.\i),{label:.+?:null,(\i)\?\(0,\i\.jsxs?\)\(\i\.Fragment.+?message:(\i).+?)/,
|
||||
replace: (_, ReactButton, PotionButton, ButtonComponent, showReactButton, message) => "" +
|
||||
`]}):null,Vencord.Api.MessagePopover._buildPopoverElements(${ButtonComponent},${message}),${showReactButton}?${ReactButton}:null,${showReactButton}&&${PotionButton},`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -165,7 +165,7 @@ function generatePluginList() {
|
|||
}
|
||||
|
||||
if (enabledPlugins.length > 100 && !(isPluginDev(UserStore.getCurrentUser()?.id) || isEquicordPluginDev(UserStore.getCurrentUser()?.id))) {
|
||||
return Alerts.show({
|
||||
Alerts.show({
|
||||
title: "You are attempting to get support!",
|
||||
body: <div>
|
||||
<style>
|
||||
|
|
@ -180,6 +180,8 @@ function generatePluginList() {
|
|||
<Forms.FormText>try removing some plugins and see if it fixes!</Forms.FormText>
|
||||
</div>
|
||||
});
|
||||
|
||||
return "You have more than 100 plugins enabled, please reduce the number of enabled plugins to get support.";
|
||||
}
|
||||
|
||||
return content;
|
||||
|
|
|
|||
|
|
@ -17,12 +17,12 @@
|
|||
*/
|
||||
|
||||
import { ApplicationCommandInputType, ApplicationCommandOptionType, findOption, sendBotMessage } from "@api/Commands";
|
||||
import { Upload } from "@api/MessageEvents";
|
||||
import { definePluginSettings, Settings } from "@api/Settings";
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { reverseExtensionMap } from "@equicordplugins/fixFileExtensions";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { CloudUpload } from "@vencord/discord-types";
|
||||
import { findByCodeLazy } from "@webpack";
|
||||
import { useState } from "@webpack/common";
|
||||
|
||||
|
|
@ -96,7 +96,7 @@ export default definePlugin({
|
|||
},
|
||||
],
|
||||
|
||||
AnonymiseUploadButton: ErrorBoundary.wrap(({ upload }: { upload: Upload; }) => {
|
||||
AnonymiseUploadButton: ErrorBoundary.wrap(({ upload }: { upload: CloudUpload; }) => {
|
||||
const [anonymise, setAnonymise] = useState(upload[ANONYMISE_UPLOAD_SYMBOL] ?? settings.store.anonymiseByDefault);
|
||||
|
||||
function onToggleAnonymise() {
|
||||
|
|
@ -117,8 +117,7 @@ export default definePlugin({
|
|||
);
|
||||
}, { noop: true }),
|
||||
|
||||
anonymise(upload: Upload) {
|
||||
|
||||
anonymise(upload: CloudUpload) {
|
||||
const originalFileName = upload.filename;
|
||||
const tarMatch = tarExtMatcher.exec(originalFileName);
|
||||
const extIdx = tarMatch?.index ?? originalFileName.lastIndexOf(".");
|
||||
|
|
|
|||
11
src/plugins/clearURLs/README.md
Normal file
11
src/plugins/clearURLs/README.md
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
# ClearURLs
|
||||
|
||||
Automatically removes tracking elements from URLs you send.
|
||||
|
||||
Uses data from the [ClearURLs browser extension](https://clearurls.xyz/).
|
||||
|
||||
## Example
|
||||
|
||||
**Before:** `https://www.amazon.com/dp/exampleProduct/ref=sxin_0_pb?__mk_de_DE=ÅMÅŽÕÑ&keywords=tea&pd_rd_i=exampleProduct&pd_rd_r=8d39e4cd-1e4f-43db-b6e7-72e969a84aa5&pd_rd_w=1pcKM&pd_rd_wg=hYrNl&pf_rd_p=50bbfd25-5ef7-41a2-68d6-74d854b30e30&pf_rd_r=0GMWD0YYKA7XFGX55ADP&qid=1517757263&rnid=2914120011`
|
||||
|
||||
**After:** `https://www.amazon.com/dp/exampleProduct/`
|
||||
|
|
@ -1,162 +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/>.
|
||||
*/
|
||||
|
||||
export const defaultRules = [
|
||||
"action_object_map",
|
||||
"action_type_map",
|
||||
"action_ref_map",
|
||||
"spm@*.aliexpress.com",
|
||||
"scm@*.aliexpress.com",
|
||||
"aff_platform",
|
||||
"aff_trace_key",
|
||||
"algo_expid@*.aliexpress.*",
|
||||
"algo_pvid@*.aliexpress.*",
|
||||
"btsid",
|
||||
"ws_ab_test",
|
||||
"pd_rd_*@amazon.*",
|
||||
"_encoding@amazon.*",
|
||||
"psc@amazon.*",
|
||||
"tag@amazon.*",
|
||||
"ref_@amazon.*",
|
||||
"pf_rd_*@amazon.*",
|
||||
"pf@amazon.*",
|
||||
"crid@amazon.*",
|
||||
"keywords@amazon.*",
|
||||
"sprefix@amazon.*",
|
||||
"sr@amazon.*",
|
||||
"ie@amazon.*",
|
||||
"node@amazon.*",
|
||||
"qid@amazon.*",
|
||||
"callback@bilibili.com",
|
||||
"cvid@bing.com",
|
||||
"form@bing.com",
|
||||
"sk@bing.com",
|
||||
"sp@bing.com",
|
||||
"sc@bing.com",
|
||||
"qs@bing.com",
|
||||
"pq@bing.com",
|
||||
"sc_cid",
|
||||
"mkt_tok",
|
||||
"trk",
|
||||
"trkCampaign",
|
||||
"ga_*",
|
||||
"gclid",
|
||||
"gclsrc",
|
||||
"hmb_campaign",
|
||||
"hmb_medium",
|
||||
"hmb_source",
|
||||
"spReportId",
|
||||
"spJobID",
|
||||
"spUserID",
|
||||
"spMailingID",
|
||||
"itm_*",
|
||||
"s_cid",
|
||||
"elqTrackId",
|
||||
"elqTrack",
|
||||
"assetType",
|
||||
"assetId",
|
||||
"recipientId",
|
||||
"campaignId",
|
||||
"siteId",
|
||||
"mc_cid",
|
||||
"mc_eid",
|
||||
"pk_*",
|
||||
"sc_campaign",
|
||||
"sc_channel",
|
||||
"sc_content",
|
||||
"sc_medium",
|
||||
"sc_outcome",
|
||||
"sc_geo",
|
||||
"sc_country",
|
||||
"nr_email_referer",
|
||||
"vero_conv",
|
||||
"vero_id",
|
||||
"yclid",
|
||||
"_openstat",
|
||||
"mbid",
|
||||
"cmpid",
|
||||
"cid",
|
||||
"c_id",
|
||||
"campaign_id",
|
||||
"Campaign",
|
||||
"hash@ebay.*",
|
||||
"fb_action_ids",
|
||||
"fb_action_types",
|
||||
"fb_ref",
|
||||
"fb_source",
|
||||
"fbclid",
|
||||
"refsrc@facebook.com",
|
||||
"hrc@facebook.com",
|
||||
"gs_l",
|
||||
"gs_lcp@google.*",
|
||||
"ved@google.*",
|
||||
"ei@google.*",
|
||||
"sei@google.*",
|
||||
"gws_rd@google.*",
|
||||
"gs_gbg@google.*",
|
||||
"gs_mss@google.*",
|
||||
"gs_rn@google.*",
|
||||
"_hsenc",
|
||||
"_hsmi",
|
||||
"__hssc",
|
||||
"__hstc",
|
||||
"hsCtaTracking",
|
||||
"source@sourceforge.net",
|
||||
"position@sourceforge.net",
|
||||
"t@*.twitter.com",
|
||||
"s@*.twitter.com",
|
||||
"ref_*@*.twitter.com",
|
||||
"t@*.x.com",
|
||||
"s@*.x.com",
|
||||
"ref_*@*.x.com",
|
||||
"t@*.fixupx.com",
|
||||
"s@*.fixupx.com",
|
||||
"ref_*@*.fixupx.com",
|
||||
"t@*.fxtwitter.com",
|
||||
"s@*.fxtwitter.com",
|
||||
"ref_*@*.fxtwitter.com",
|
||||
"t@*.vxtwitter.com",
|
||||
"s@*.vxtwitter.com",
|
||||
"ref_*@*.vxtwitter.com",
|
||||
"t@*.twittpr.com",
|
||||
"s@*.twittpr.com",
|
||||
"ref_*@*.twittpr.com",
|
||||
"t@*.fixvx.com",
|
||||
"s@*.fixvx.com",
|
||||
"ref_*@*.fixvx.com",
|
||||
"tt_medium",
|
||||
"tt_content",
|
||||
"lr@yandex.*",
|
||||
"redircnt@yandex.*",
|
||||
"feature@*.youtube.com",
|
||||
"kw@*.youtube.com",
|
||||
"si@*.youtube.com",
|
||||
"pp@*.youtube.com",
|
||||
"si@*.youtu.be",
|
||||
"wt_zmc",
|
||||
"utm_source",
|
||||
"utm_content",
|
||||
"utm_medium",
|
||||
"utm_campaign",
|
||||
"utm_term",
|
||||
"si@open.spotify.com",
|
||||
"igshid",
|
||||
"igsh",
|
||||
"share_id@reddit.com",
|
||||
"si@soundcloud.com",
|
||||
];
|
||||
|
|
@ -22,77 +22,74 @@ import {
|
|||
import { Devs } from "@utils/constants";
|
||||
import definePlugin from "@utils/types";
|
||||
|
||||
import { defaultRules } from "./defaultRules";
|
||||
const CLEAR_URLS_JSON_URL = "https://raw.githubusercontent.com/ClearURLs/Rules/master/data.min.json";
|
||||
|
||||
// From lodash
|
||||
const reRegExpChar = /[\\^$.*+?()[\]{}|]/g;
|
||||
const reHasRegExpChar = RegExp(reRegExpChar.source);
|
||||
interface Provider {
|
||||
urlPattern: string;
|
||||
completeProvider: boolean;
|
||||
rules?: string[];
|
||||
rawRules?: string[];
|
||||
referralMarketing?: string[];
|
||||
exceptions?: string[];
|
||||
redirections?: string[];
|
||||
forceRedirection?: boolean;
|
||||
}
|
||||
|
||||
interface ClearUrlsData {
|
||||
providers: Record<string, Provider>;
|
||||
}
|
||||
|
||||
interface RuleSet {
|
||||
name: string;
|
||||
urlPattern: RegExp;
|
||||
rules?: RegExp[];
|
||||
rawRules?: RegExp[];
|
||||
exceptions?: RegExp[];
|
||||
}
|
||||
|
||||
export default definePlugin({
|
||||
name: "ClearURLs",
|
||||
description: "Removes tracking garbage from URLs",
|
||||
authors: [Devs.adryd],
|
||||
description: "Automatically removes tracking elements from URLs you send",
|
||||
authors: [Devs.adryd, Devs.thororen],
|
||||
|
||||
start() {
|
||||
this.createRules();
|
||||
rules: [] as RuleSet[],
|
||||
|
||||
async start() {
|
||||
await this.createRules();
|
||||
},
|
||||
|
||||
stop() {
|
||||
this.rules = [];
|
||||
},
|
||||
|
||||
onBeforeMessageSend(_, msg) {
|
||||
return this.onSend(msg);
|
||||
return this.cleanMessage(msg);
|
||||
},
|
||||
|
||||
onBeforeMessageEdit(_cid, _mid, msg) {
|
||||
return this.onSend(msg);
|
||||
return this.cleanMessage(msg);
|
||||
},
|
||||
|
||||
escapeRegExp(str: string) {
|
||||
return (str && reHasRegExpChar.test(str))
|
||||
? str.replace(reRegExpChar, "\\$&")
|
||||
: (str || "");
|
||||
},
|
||||
async createRules() {
|
||||
const res = await fetch(CLEAR_URLS_JSON_URL)
|
||||
.then(res => res.json()) as ClearUrlsData;
|
||||
|
||||
createRules() {
|
||||
// Can be extended upon once user configs are available
|
||||
// Eg. (useDefaultRules: boolean, customRules: Array[string])
|
||||
const rules = defaultRules;
|
||||
this.rules = [];
|
||||
|
||||
this.universalRules = new Set();
|
||||
this.rulesByHost = new Map();
|
||||
this.hostRules = new Map();
|
||||
for (const [name, provider] of Object.entries(res.providers)) {
|
||||
const urlPattern = new RegExp(provider.urlPattern, "i");
|
||||
|
||||
for (const rule of rules) {
|
||||
const splitRule = rule.split("@");
|
||||
const paramRule = new RegExp(
|
||||
"^" +
|
||||
this.escapeRegExp(splitRule[0]).replace(/\\\*/, ".+?") +
|
||||
"$"
|
||||
);
|
||||
const rules = provider.rules?.map(rule => new RegExp(rule, "i"));
|
||||
const rawRules = provider.rawRules?.map(rule => new RegExp(rule, "i"));
|
||||
const exceptions = provider.exceptions?.map(ex => new RegExp(ex, "i"));
|
||||
|
||||
if (!splitRule[1]) {
|
||||
this.universalRules.add(paramRule);
|
||||
continue;
|
||||
}
|
||||
const hostRule = new RegExp(
|
||||
"^(www\\.)?" +
|
||||
this.escapeRegExp(splitRule[1])
|
||||
.replace(/\\\./, "\\.")
|
||||
.replace(/^\\\*\\\./, "(.+?\\.)?")
|
||||
.replace(/\\\*/, ".+?") +
|
||||
"$"
|
||||
);
|
||||
const hostRuleIndex = hostRule.toString();
|
||||
|
||||
this.hostRules.set(hostRuleIndex, hostRule);
|
||||
if (this.rulesByHost.get(hostRuleIndex) == null) {
|
||||
this.rulesByHost.set(hostRuleIndex, new Set());
|
||||
}
|
||||
this.rulesByHost.get(hostRuleIndex).add(paramRule);
|
||||
}
|
||||
},
|
||||
|
||||
removeParam(rule: string | RegExp, param: string, parent: URLSearchParams) {
|
||||
if (param === rule || rule instanceof RegExp && rule.test(param)) {
|
||||
parent.delete(param);
|
||||
this.rules.push({
|
||||
name,
|
||||
urlPattern,
|
||||
rules,
|
||||
rawRules,
|
||||
exceptions,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -106,34 +103,40 @@ export default definePlugin({
|
|||
}
|
||||
|
||||
// Cheap way to check if there are any search params
|
||||
if (url.searchParams.entries().next().done) {
|
||||
// If there are none, we don't need to modify anything
|
||||
return match;
|
||||
}
|
||||
if (url.searchParams.entries().next().done) return match;
|
||||
|
||||
// Check all universal rules
|
||||
this.universalRules.forEach(rule => {
|
||||
url.searchParams.forEach((_value, param, parent) => {
|
||||
this.removeParam(rule, param, parent);
|
||||
});
|
||||
});
|
||||
// Check rules for each provider that matches
|
||||
this.rules.forEach(({ urlPattern, exceptions, rawRules, rules }) => {
|
||||
if (!urlPattern.test(url.href) || exceptions?.some(ex => ex.test(url.href))) return;
|
||||
|
||||
// Check rules for each hosts that match
|
||||
this.hostRules.forEach((regex, hostRuleName) => {
|
||||
if (!regex.test(url.hostname)) return;
|
||||
this.rulesByHost.get(hostRuleName).forEach(rule => {
|
||||
url.searchParams.forEach((_value, param, parent) => {
|
||||
this.removeParam(rule, param, parent);
|
||||
const toDelete: string[] = [];
|
||||
|
||||
if (rules) {
|
||||
// Add matched params to delete list
|
||||
url.searchParams.forEach((_, param) => {
|
||||
if (rules.some(rule => rule.test(param))) {
|
||||
toDelete.push(param);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Delete matched params from list
|
||||
toDelete.forEach(param => url.searchParams.delete(param));
|
||||
|
||||
// Match and remove any raw rules
|
||||
let cleanedUrl = url.href;
|
||||
rawRules?.forEach(rawRule => {
|
||||
cleanedUrl = cleanedUrl.replace(rawRule, "");
|
||||
});
|
||||
url = new URL(cleanedUrl);
|
||||
});
|
||||
|
||||
return url.toString();
|
||||
},
|
||||
|
||||
onSend(msg: MessageObject) {
|
||||
cleanMessage(msg: MessageObject) {
|
||||
// Only run on messages that contain URLs
|
||||
if (msg.content.match(/http(s)?:\/\//)) {
|
||||
if (/http(s)?:\/\//.test(msg.content)) {
|
||||
msg.content = msg.content.replace(
|
||||
/(https?:\/\/[^\s<]+[^<.,:;"'>)|\]\s])/g,
|
||||
match => this.replacer(match)
|
||||
|
|
|
|||
|
|
@ -180,6 +180,13 @@ function makeShortcuts() {
|
|||
experimentBucket: bucket,
|
||||
});
|
||||
},
|
||||
switchBranch: (branch: string) => {
|
||||
if (!IS_VESKTOP && !IS_EQUIBOP) throw new Error("This function only works on vesktop and equibop.");
|
||||
const target = IS_VESKTOP ? Vesktop : Equibop;
|
||||
if (target.Settings.store.discordBranch === branch) throw new Error(`Already on ${branch}.`);
|
||||
target.Settings.store.discordBranch = branch;
|
||||
VesktopNative.app.relaunch();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,8 +22,8 @@ import { Logger } from "@utils/Logger";
|
|||
import definePlugin, { OptionType, ReporterTestable } from "@utils/types";
|
||||
|
||||
import { initWs, socket, stopWs } from "./initWs";
|
||||
console.log("imported");
|
||||
export const PORT = 8485;
|
||||
export const CLIENT_VERSION: readonly [major: number, minor: number, patch: number] = [0, 1, 1];
|
||||
|
||||
export const logger = new Logger("DevCompanion");
|
||||
|
||||
|
|
@ -67,4 +67,3 @@ export default definePlugin({
|
|||
|
||||
stop: stopWs,
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import { loadLazyChunks } from "debug/loadLazyChunks";
|
|||
import { reporterData } from "debug/reporterData";
|
||||
import { Settings } from "Vencord";
|
||||
|
||||
import { logger, PORT, settings } from ".";
|
||||
import { CLIENT_VERSION, logger, PORT, settings } from ".";
|
||||
import { Recieve } from "./types";
|
||||
import { FullOutgoingMessage, OutgoingMessage } from "./types/send";
|
||||
import { extractModule, extractOrThrow, findModuleId, getModulePatchedBy, mkRegexFind, parseNode, toggleEnabled, } from "./util";
|
||||
|
|
@ -63,7 +63,6 @@ export function initWs(isManual = false) {
|
|||
}));
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
if (settings.store.notifyOnAutoConnect || isManual) {
|
||||
Toasts.show({
|
||||
|
|
@ -449,6 +448,16 @@ export function initWs(isManual = false) {
|
|||
});
|
||||
break;
|
||||
}
|
||||
case "version": {
|
||||
replyData({
|
||||
type: "version",
|
||||
ok: true,
|
||||
data: {
|
||||
clientVersion: CLIENT_VERSION
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// @ts-expect-error should be never
|
||||
reply("Unknown Type " + d?.type);
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ export type FindData = {
|
|||
args: FindNode[];
|
||||
};
|
||||
|
||||
export type IncomingMessage = DisablePlugin | RawId | DiffPatch | Reload | ExtractModule | TestPatch | TestFind | AllModules | I18nLookup;
|
||||
export type IncomingMessage = DisablePlugin | RawId | DiffPatch | Reload | ExtractModule | TestPatch | TestFind | AllModules | I18nLookup | Version;
|
||||
export type FullIncomingMessage = IncomingMessage & { nonce: number; };
|
||||
|
||||
export type DisablePlugin = {
|
||||
|
|
@ -109,6 +109,14 @@ export type ExtractModule = {
|
|||
data: FindOrSearchData;
|
||||
};
|
||||
|
||||
export type Version = {
|
||||
type: "version";
|
||||
data: {
|
||||
// major minor patch
|
||||
server_version: [number, number, number];
|
||||
};
|
||||
};
|
||||
|
||||
export type TestPatch = {
|
||||
type: "testPatch";
|
||||
data: (
|
||||
|
|
|
|||
|
|
@ -4,20 +4,19 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
// should be the same types as src/server/types/recieve.ts in the companion
|
||||
import { ReporterData as IReporterData } from "debug/reporterData";
|
||||
export type ReporterData = IReporterData;
|
||||
import { ReporterData } from "debug/reporterData";
|
||||
|
||||
export type OutgoingMessage = (Report | DiffModule | ExtractModule | ModuleList | RawId | I18nValue) & Base;
|
||||
export type OutgoingMessage = Base<DiffModule | ExtractModule | ModuleList | RawId | I18nValue | VersionResponse>;
|
||||
export type FullOutgoingMessage = OutgoingMessage & Nonce;
|
||||
|
||||
export type Base = {
|
||||
export type Base<T> = ({
|
||||
ok: true;
|
||||
} | {
|
||||
} & T) | ({
|
||||
ok: false;
|
||||
data?: any;
|
||||
data: null;
|
||||
error: string;
|
||||
};
|
||||
} & Omit<T, "data">);
|
||||
|
||||
export type Nonce = {
|
||||
nonce: number;
|
||||
};
|
||||
|
|
@ -81,4 +80,11 @@ export type RawId = {
|
|||
type: "rawId";
|
||||
data: string;
|
||||
};
|
||||
|
||||
export type VersionResponse = {
|
||||
type: "version";
|
||||
data: {
|
||||
clientVersion: readonly [number, number, number];
|
||||
};
|
||||
};
|
||||
// #endregion
|
||||
|
|
|
|||
|
|
@ -182,8 +182,8 @@ function makeBypassPatches(): Omit<Patch, "plugin"> {
|
|||
|
||||
export default definePlugin({
|
||||
name: "FakeNitro",
|
||||
authors: [Devs.Arjix, Devs.D3SOX, Devs.Ven, Devs.fawn, Devs.captain, Devs.Nuckyz, Devs.AutumnVN],
|
||||
description: "Allows you to stream in nitro quality, send fake emojis/stickers, and use client themes.",
|
||||
authors: [Devs.Arjix, Devs.D3SOX, Devs.Ven, Devs.fawn, Devs.captain, Devs.Nuckyz, Devs.AutumnVN, Devs.sadan],
|
||||
description: "Allows you to send fake emojis/stickers, use nitro themes, and stream in nitro quality",
|
||||
dependencies: ["MessageEventsAPI"],
|
||||
|
||||
settings,
|
||||
|
|
@ -281,6 +281,14 @@ export default definePlugin({
|
|||
replace: (_, rest, backgroundGradientPresetId, originalCall, theme) => `${rest}$self.handleGradientThemeSelect(${backgroundGradientPresetId},${theme},()=>${originalCall});`
|
||||
}
|
||||
},
|
||||
// Allow users to use custom client themes
|
||||
{
|
||||
find: "customUserThemeSettings:{",
|
||||
replacement: {
|
||||
match: /(?<=\i=)\(0,\i\.\i\)\(\i\.\i\.TIER_2\)(?=,|;)/g,
|
||||
replace: "true"
|
||||
}
|
||||
},
|
||||
{
|
||||
find: '["strong","em","u","text","inlineCode","s","spoiler"]',
|
||||
replacement: [
|
||||
|
|
@ -392,24 +400,15 @@ export default definePlugin({
|
|||
if (premiumType !== 2) {
|
||||
proto.appearance ??= AppearanceSettingsActionCreators.create();
|
||||
|
||||
if (UserSettingsProtoStore.settings.appearance?.theme != null) {
|
||||
const appearanceSettingsDummy = AppearanceSettingsActionCreators.create({
|
||||
theme: UserSettingsProtoStore.settings.appearance.theme
|
||||
});
|
||||
const protoStoreAppearenceSettings = UserSettingsProtoStore.settings.appearance;
|
||||
|
||||
proto.appearance.theme = appearanceSettingsDummy.theme;
|
||||
}
|
||||
const appearanceSettingsOverwrite = AppearanceSettingsActionCreators.create({
|
||||
...proto.appearance,
|
||||
theme: protoStoreAppearenceSettings?.theme,
|
||||
clientThemeSettings: protoStoreAppearenceSettings?.clientThemeSettings
|
||||
});
|
||||
|
||||
if (UserSettingsProtoStore.settings.appearance?.clientThemeSettings?.backgroundGradientPresetId?.value != null) {
|
||||
const clientThemeSettingsDummy = ClientThemeSettingsActionsCreators.create({
|
||||
backgroundGradientPresetId: {
|
||||
value: UserSettingsProtoStore.settings.appearance.clientThemeSettings.backgroundGradientPresetId.value
|
||||
}
|
||||
});
|
||||
|
||||
proto.appearance.clientThemeSettings ??= clientThemeSettingsDummy;
|
||||
proto.appearance.clientThemeSettings.backgroundGradientPresetId = clientThemeSettingsDummy.backgroundGradientPresetId;
|
||||
}
|
||||
proto.appearance = appearanceSettingsOverwrite;
|
||||
}
|
||||
} catch (err) {
|
||||
new Logger("FakeNitro").error(err);
|
||||
|
|
|
|||
|
|
@ -20,18 +20,17 @@ import { definePluginSettings } from "@api/Settings";
|
|||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
import { useCallback, useEffect, useRef, useState } from "@webpack/common";
|
||||
|
||||
interface SearchBarComponentProps {
|
||||
ref?: React.MutableRefObject<any>;
|
||||
ref?: React.RefObject<any>;
|
||||
autoFocus: boolean;
|
||||
className: string;
|
||||
size: string;
|
||||
onChange: (query: string) => void;
|
||||
onClear: () => void;
|
||||
query: string;
|
||||
placeholder: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
type TSearchBarComponent =
|
||||
|
|
@ -59,9 +58,6 @@ interface Instance {
|
|||
forceUpdate: () => void;
|
||||
}
|
||||
|
||||
|
||||
const containerClasses: { searchBar: string; } = findByPropsLazy("searchBar", "searchBarFullRow");
|
||||
|
||||
export const settings = definePluginSettings({
|
||||
searchOption: {
|
||||
type: OptionType.SELECT,
|
||||
|
|
@ -136,7 +132,7 @@ export default definePlugin({
|
|||
|
||||
function SearchBar({ instance, SearchBarComponent }: { instance: Instance; SearchBarComponent: TSearchBarComponent; }) {
|
||||
const [query, setQuery] = useState("");
|
||||
const ref = useRef<{ containerRef?: React.MutableRefObject<HTMLDivElement>; } | null>(null);
|
||||
const ref = useRef<{ containerRef?: React.RefObject<HTMLDivElement>; } | null>(null);
|
||||
|
||||
const onChange = useCallback((searchQuery: string) => {
|
||||
setQuery(searchQuery);
|
||||
|
|
@ -152,7 +148,7 @@ function SearchBar({ instance, SearchBarComponent }: { instance: Instance; Searc
|
|||
|
||||
// scroll back to top
|
||||
ref.current?.containerRef?.current
|
||||
.closest("#gif-picker-tab-panel")
|
||||
?.closest("#gif-picker-tab-panel")
|
||||
?.querySelector("[class|=\"content\"]")
|
||||
?.firstElementChild?.scrollTo(0, 0);
|
||||
|
||||
|
|
@ -181,8 +177,8 @@ function SearchBar({ instance, SearchBarComponent }: { instance: Instance; Searc
|
|||
<SearchBarComponent
|
||||
ref={ref}
|
||||
autoFocus={true}
|
||||
className={containerClasses.searchBar}
|
||||
size="md"
|
||||
className=""
|
||||
onChange={onChange}
|
||||
onClear={() => {
|
||||
setQuery("");
|
||||
|
|
|
|||
|
|
@ -1,7 +1,4 @@
|
|||
[class^="panels"] [class^="avatarWrapper"] {
|
||||
min-width: 88px;
|
||||
}
|
||||
|
||||
.vc-game-activity {
|
||||
margin-right: 4px;
|
||||
}
|
||||
/* make gap smaller since we're adding an extra button */
|
||||
[class^="panels"] [class^="buttons"] {
|
||||
gap: 4px;
|
||||
}
|
||||
|
|
@ -48,6 +48,7 @@ interface Activity {
|
|||
buttons?: Array<string>;
|
||||
name: string;
|
||||
application_id: string;
|
||||
status_display_type?: number;
|
||||
metadata?: {
|
||||
button_urls?: Array<string>;
|
||||
};
|
||||
|
|
@ -137,6 +138,25 @@ const settings = definePluginSettings({
|
|||
type: OptionType.STRING,
|
||||
default: "some music",
|
||||
},
|
||||
statusDisplayType: {
|
||||
description: "Show the track / artist name in the member list",
|
||||
type: OptionType.SELECT,
|
||||
options: [
|
||||
{
|
||||
label: "Don't show (shows generic listening message)",
|
||||
value: "off",
|
||||
default: true
|
||||
},
|
||||
{
|
||||
label: "Show artist name",
|
||||
value: "artist"
|
||||
},
|
||||
{
|
||||
label: "Show track name",
|
||||
value: "track"
|
||||
}
|
||||
]
|
||||
},
|
||||
nameFormat: {
|
||||
description: "Show name of song and artist in status name",
|
||||
type: OptionType.SELECT,
|
||||
|
|
@ -354,6 +374,11 @@ export default definePlugin({
|
|||
|
||||
details: trackData.name,
|
||||
state: trackData.artist,
|
||||
status_display_type: {
|
||||
"off": 0,
|
||||
"artist": 1,
|
||||
"track": 2
|
||||
}[settings.store.statusDisplayType],
|
||||
assets,
|
||||
|
||||
buttons: buttons.length ? buttons.map(v => v.label) : undefined,
|
||||
|
|
|
|||
17
src/plugins/memberCount/CircleIcon.tsx
Normal file
17
src/plugins/memberCount/CircleIcon.tsx
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2025 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
export function CircleIcon({ className }: { className?: string; }) {
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" className={className}>
|
||||
<circle
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="8"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@ import { isObjectEmpty } from "@utils/misc";
|
|||
import { ChannelStore, PermissionsBits, PermissionStore, SelectedChannelStore, Tooltip, useEffect, useStateFromStores, VoiceStateStore } from "@webpack/common";
|
||||
|
||||
import { ChannelMemberStore, cl, GuildMemberCountStore, numberFormat, settings, ThreadMemberListStore } from ".";
|
||||
import { CircleIcon } from "./CircleIcon";
|
||||
import { OnlineMemberCountStore } from "./OnlineMemberCountStore";
|
||||
import { VoiceIcon } from "./VoiceIcon";
|
||||
|
||||
|
|
@ -80,7 +81,7 @@ export function MemberCount({ isTooltip, tooltipGuildId }: { isTooltip?: true; t
|
|||
<Tooltip text={`${formattedOnlineCount} online in this channel`} position="bottom">
|
||||
{props => (
|
||||
<div {...props} className={cl("container")}>
|
||||
<span className={cl("online-dot")} />
|
||||
<CircleIcon className={cl("online-count")} />
|
||||
<span className={cl("online")}>{formattedOnlineCount}</span>
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -88,7 +89,7 @@ export function MemberCount({ isTooltip, tooltipGuildId }: { isTooltip?: true; t
|
|||
<Tooltip text={`${numberFormat(totalCount)} total server members`} position="bottom">
|
||||
{props => (
|
||||
<div {...props} className={cl("container")}>
|
||||
<span className={cl("total-dot")} />
|
||||
<CircleIcon className={cl("total-count")} />
|
||||
<span className={cl("total")}>{numberFormat(totalCount)}</span>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -36,7 +36,6 @@ export const ThreadMemberListStore = findStoreLazy("ThreadMemberListStore") as F
|
|||
getMemberListSections(channelId?: string): { [sectionId: string]: { sectionId: string; userIds: string[]; }; };
|
||||
};
|
||||
|
||||
|
||||
export const settings = definePluginSettings({
|
||||
toolTip: {
|
||||
type: OptionType.BOOLEAN,
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
flex-wrap: wrap;
|
||||
margin-top: 1em;
|
||||
padding-inline: 1em;
|
||||
line-height: 1.2em;
|
||||
}
|
||||
|
||||
.vc-membercount-container {
|
||||
|
|
@ -38,20 +39,18 @@
|
|||
color: var(--color-voice);
|
||||
}
|
||||
|
||||
.vc-membercount-online-dot {
|
||||
background-color: var(--color-online);
|
||||
display: inline-block;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
.vc-membercount-online-count {
|
||||
fill: var(--status-online);
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
.vc-membercount-total-dot {
|
||||
display: inline-block;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
border: 3px solid var(--color-total);
|
||||
.vc-membercount-total-count {
|
||||
fill: none;
|
||||
stroke: var(--status-offline);
|
||||
stroke-width: 4px;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
}
|
||||
|
||||
.vc-membercount-voice-icon {
|
||||
|
|
|
|||
|
|
@ -87,8 +87,8 @@ export default definePlugin({
|
|||
replacement: {
|
||||
// The two groups inside the first group grab the minified names of the variables,
|
||||
// they are then referenced later to find unviewedTrialCount + unviewedDiscountCount.
|
||||
match: /(?<=\{unviewedTrialCount:(\i),unviewedDiscountCount:(\i)\}.{0,300}\.NONE\?)\1\+\2/,
|
||||
replace: "0"
|
||||
match: /(\{unviewedTrialCount:(\i),unviewedDiscountCount:(\i)\}.+?)\2\+\3/,
|
||||
replace: (_, rest) => `${rest}0`
|
||||
}
|
||||
}
|
||||
],
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import { ChatBarButton, ChatBarButtonFactory } from "@api/ChatButtons";
|
|||
import { generateId, sendBotMessage } from "@api/Commands";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { StartAt } from "@utils/types";
|
||||
import { MessageAttachment } from "@vencord/discord-types";
|
||||
import { CloudUpload, MessageAttachment } from "@vencord/discord-types";
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
import { DraftStore, DraftType, SelectedChannelStore, UserStore, useStateFromStores } from "@webpack/common";
|
||||
|
||||
|
|
@ -45,7 +45,7 @@ const getImageBox = (url: string): Promise<{ width: number, height: number; } |
|
|||
const getAttachments = async (channelId: string) =>
|
||||
await Promise.all(
|
||||
UploadStore.getUploads(channelId, DraftType.ChannelMessage)
|
||||
.map(async (upload: any) => {
|
||||
.map(async (upload: CloudUpload) => {
|
||||
const { isImage, filename, spoiler, item: { file } } = upload;
|
||||
const url = URL.createObjectURL(file);
|
||||
const attachment: MessageAttachment = {
|
||||
|
|
@ -53,7 +53,7 @@ const getAttachments = async (channelId: string) =>
|
|||
filename: spoiler ? "SPOILER_" + filename : filename,
|
||||
// weird eh? if i give it the normal content type the preview doenst work
|
||||
content_type: undefined,
|
||||
size: await upload.getSize(),
|
||||
size: upload.getSize(),
|
||||
spoiler,
|
||||
// discord adds query params to the url, so we need to add a hash to prevent that
|
||||
url: url + "#",
|
||||
|
|
|
|||
|
|
@ -635,12 +635,26 @@ function renderUsername(
|
|||
const hoveringMessageSet = new Set<string>();
|
||||
const hoveringReactionPopoutSet = new Set<string>();
|
||||
|
||||
function handleHoveringMessage(message: any, animate: boolean) {
|
||||
if (!message) return;
|
||||
|
||||
if (animate) {
|
||||
addHoveringMessage(message.id);
|
||||
addHoveringMessage(message.showMeYourNameGroupId);
|
||||
} else {
|
||||
removeHoveringMessage(message.id);
|
||||
removeHoveringMessage(message.showMeYourNameGroupId);
|
||||
}
|
||||
}
|
||||
|
||||
function addHoveringMessage(id: string) {
|
||||
if (!id) return;
|
||||
hoveringMessageSet.add(id);
|
||||
settings.store.triggerNameRerender = !settings.store.triggerNameRerender;
|
||||
}
|
||||
|
||||
function removeHoveringMessage(id: string) {
|
||||
if (!id) return;
|
||||
hoveringMessageSet.delete(id);
|
||||
settings.store.triggerNameRerender = !settings.store.triggerNameRerender;
|
||||
}
|
||||
|
|
@ -783,14 +797,21 @@ export default definePlugin({
|
|||
]
|
||||
},
|
||||
{
|
||||
// Track hovering on messages to animate mentions.
|
||||
find: /setAnimate.{0,50}\.ANIMATE_CHAT_AVATAR,/,
|
||||
replacement: {
|
||||
// Track hovering on messages to animate names since Discord's
|
||||
// built-in hover tracking is buggy. Used by a patch below.
|
||||
match: /(let{setAnimate:\i}=(\i);)/,
|
||||
replace: "$1if(arguments[0].message){if($2.animate){$self.addHoveringMessage(arguments[0].message.id)}else{$self.removeHoveringMessage(arguments[0].message.id)}};"
|
||||
replace: "$1$self.handleHoveringMessage(arguments[0].message,$2.animate);"
|
||||
}
|
||||
},
|
||||
{
|
||||
// Attach the group ID to their messages to allow animating mentions within a group.
|
||||
find: "CUSTOM_GIFT?\"\":",
|
||||
replacement: {
|
||||
match: /(\(\i,\i,\i\);)(let \i=\i.id===\i(?:.{0,500}?)hovering:(\i))/,
|
||||
replace: "$1arguments[0].message.showMeYourNameGroupId=arguments[0].groupId;$2"
|
||||
},
|
||||
},
|
||||
{
|
||||
// Replace names in mentions.
|
||||
find: ".USER_MENTION)",
|
||||
|
|
@ -815,19 +836,6 @@ export default definePlugin({
|
|||
replace: "$1props:arguments[2],"
|
||||
}
|
||||
},
|
||||
{
|
||||
// Track hovering over second-level messages to animate gradients.
|
||||
// Tack on the groupId, which is how the client groups messages from
|
||||
// the same author together, to the message temporarily so that mentions
|
||||
// across a group can animate together. By default, Discord handles this,
|
||||
// but moving between messages can cause a race condition where the animation
|
||||
// stops on one of the messages in the group. This patch fixes that.
|
||||
find: "CUSTOM_GIFT?\"\":",
|
||||
replacement: {
|
||||
match: /(\(\i,\i,\i\);)(let \i=\i.id===\i(?:.{0,500}?)hovering:(\i))/,
|
||||
replace: "$1arguments[0].message.showMeYourNameGroupId=arguments[0].groupId;if($3){$self.addHoveringMessage(arguments[0].groupId)}else{$self.removeHoveringMessage(arguments[0].groupId)};$2"
|
||||
},
|
||||
},
|
||||
{
|
||||
// Replace names in the member list.
|
||||
find: "let{colorRoleName:",
|
||||
|
|
@ -936,6 +944,7 @@ export default definePlugin({
|
|||
|
||||
addHoveringMessage,
|
||||
removeHoveringMessage,
|
||||
handleHoveringMessage,
|
||||
addHoveringReactionPopout,
|
||||
removeHoveringReactionPopout,
|
||||
getMessageNameText,
|
||||
|
|
|
|||
|
|
@ -27,6 +27,8 @@ import { ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, openModa
|
|||
import { useAwaiter } from "@utils/react";
|
||||
import definePlugin from "@utils/types";
|
||||
import { chooseFile } from "@utils/web";
|
||||
import { CloudUpload as TCloudUpload } from "@vencord/discord-types";
|
||||
import { CloudUploadPlatform } from "@vencord/discord-types/enums";
|
||||
import { findByPropsLazy, findLazy, findStoreLazy } from "@webpack";
|
||||
import { Button, Card, Constants, FluxDispatcher, Forms, lodash, Menu, MessageActions, PermissionsBits, PermissionStore, RestAPI, SelectedChannelStore, showToast, SnowflakeUtils, Toasts, useEffect, useState } from "@webpack/common";
|
||||
import { ComponentType } from "react";
|
||||
|
|
@ -38,7 +40,7 @@ import { cl } from "./utils";
|
|||
import { VoicePreview } from "./VoicePreview";
|
||||
import { VoiceRecorderWeb } from "./WebRecorder";
|
||||
|
||||
const CloudUpload = findLazy(m => m.prototype?.trackUploadFinished);
|
||||
const CloudUpload: typeof TCloudUpload = findLazy(m => m.prototype?.trackUploadFinished);
|
||||
const PendingReplyStore = findStoreLazy("PendingReplyStore");
|
||||
const OptionClasses = findByPropsLazy("optionName", "optionIcon", "optionLabel");
|
||||
|
||||
|
|
@ -93,8 +95,8 @@ function sendAudio(blob: Blob, meta: AudioMetadata) {
|
|||
const upload = new CloudUpload({
|
||||
file: new File([blob], "voice-message.ogg", { type: "audio/ogg; codecs=opus" }),
|
||||
isThumbnail: false,
|
||||
platform: 1,
|
||||
}, channelId, false, 0);
|
||||
platform: CloudUploadPlatform.WEB,
|
||||
}, channelId);
|
||||
|
||||
upload.on("complete", () => {
|
||||
RestAPI.post({
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ const settings = definePluginSettings({
|
|||
multiplier: {
|
||||
description: "Volume Multiplier",
|
||||
type: OptionType.SLIDER,
|
||||
markers: makeRange(1, 5, 1),
|
||||
markers: makeRange(1, 5, 0.5),
|
||||
default: 2,
|
||||
stickToMarkers: true,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -621,6 +621,10 @@ export const Devs = /* #__PURE__*/ Object.freeze({
|
|||
name: "Cootshk",
|
||||
id: 921605971577548820n
|
||||
},
|
||||
thororen: {
|
||||
name: "thororen",
|
||||
id: 848339671629299742n
|
||||
},
|
||||
} satisfies Record<string, Dev>);
|
||||
|
||||
export const EquicordDevs = Object.freeze({
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
*/
|
||||
|
||||
import { MessageObject } from "@api/MessageEvents";
|
||||
import { Channel, Guild, GuildFeatures, Message, User } from "@vencord/discord-types";
|
||||
import { Channel, CloudUpload, Guild, GuildFeatures, Message, User } from "@vencord/discord-types";
|
||||
import { ChannelActionCreators, ChannelStore, ComponentDispatch, Constants, FluxDispatcher, GuildStore, i18n, IconUtils, InviteActions, MessageActions, RestAPI, SelectedChannelStore, SelectedGuildStore, UserProfileActions, UserProfileStore, UserSettingsActionCreators, UserUtils } from "@webpack/common";
|
||||
import { Except } from "type-fest";
|
||||
|
||||
|
|
@ -117,6 +117,20 @@ interface MessageOptions {
|
|||
replied_user: boolean;
|
||||
};
|
||||
stickerIds: string[];
|
||||
attachmentsToUpload: CloudUpload[];
|
||||
poll: {
|
||||
allow_multiselect: boolean;
|
||||
answers: Array<{
|
||||
poll_media: {
|
||||
text: string;
|
||||
attachment_ids?: unknown;
|
||||
emoji?: { name: string; id?: string; };
|
||||
};
|
||||
}>;
|
||||
duration: number;
|
||||
layout_type: number;
|
||||
question: { text: string; };
|
||||
};
|
||||
}
|
||||
|
||||
export function sendMessage(
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
*/
|
||||
|
||||
import { React, useEffect, useMemo, useReducer, useState } from "@webpack/common";
|
||||
import { ActionDispatch } from "react";
|
||||
import { ActionDispatch, ReactNode } from "react";
|
||||
|
||||
import { checkIntersecting } from "./misc";
|
||||
|
||||
|
|
@ -25,6 +25,14 @@ export * from "./lazyReact";
|
|||
|
||||
export const NoopComponent = () => null;
|
||||
|
||||
/**
|
||||
* Check if a React node is a primitive (string, number, bigint, boolean, undefined)
|
||||
*/
|
||||
export function isPrimitiveReactNode(node: ReactNode): boolean {
|
||||
const t = typeof node;
|
||||
return t === "string" || t === "number" || t === "bigint" || t === "boolean" || t === "undefined";
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an element is on screen
|
||||
* @param intersectOnly If `true`, will only update the state when the element comes into view
|
||||
|
|
|
|||
|
|
@ -21,7 +21,8 @@ import { moment } from "@webpack/common";
|
|||
// Utils for readable text transformations eg: `toTitle(fromKebab())`
|
||||
|
||||
// Case style to words
|
||||
export const wordsFromCamel = (text: string) => text.split(/(?=[A-Z])/).map(w => w.toLowerCase());
|
||||
export const wordsFromCamel = (text: string) =>
|
||||
text.split(/(?=[A-Z][a-z])|(?<=[a-z])(?=[A-Z])/).map(w => /^[A-Z]{2,}$/.test(w) ? w : w.toLowerCase());
|
||||
export const wordsFromSnake = (text: string) => text.toLowerCase().split("_");
|
||||
export const wordsFromKebab = (text: string) => text.toLowerCase().split("-");
|
||||
export const wordsFromPascal = (text: string) => text.split(/(?=[A-Z])/).map(w => w.toLowerCase());
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ waitFor(m => m.name === "MenuCheckboxItem", (_, id) => {
|
|||
|
||||
waitFor(filters.componentByCode('path:["empty"]'), m => Menu.Menu = m);
|
||||
waitFor(filters.componentByCode("sliderContainer", "slider", "handleSize:16", "=100"), m => Menu.MenuSliderControl = m);
|
||||
waitFor(filters.componentByCode('role:"searchbox', "top:2", "query:"), m => Menu.MenuSearchControl = m);
|
||||
waitFor(filters.componentByCode(".SEARCH)", ".focus()", "query:"), m => Menu.MenuSearchControl = m);
|
||||
|
||||
export const ContextMenuApi: t.ContextMenuApi = mapMangledModuleLazy('type:"CONTEXT_MENU_OPEN', {
|
||||
closeContextMenu: filters.byCode("CONTEXT_MENU_CLOSE"),
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue