Compare commits
2 commits
b79b102833
...
acccc963ae
| Author | SHA1 | Date | |
|---|---|---|---|
| acccc963ae | |||
| 77084203c6 |
369 changed files with 4749 additions and 7361 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -22,4 +22,4 @@ lerna-debug.log*
|
|||
src/userplugins
|
||||
|
||||
ExtensionCache/
|
||||
/settings
|
||||
settings/
|
||||
|
|
|
|||
9
.vscode/settings.json
vendored
9
.vscode/settings.json
vendored
|
|
@ -13,14 +13,11 @@
|
|||
"typescript.format.semicolons": "insert",
|
||||
"typescript.preferences.quoteStyle": "double",
|
||||
"javascript.preferences.quoteStyle": "double",
|
||||
|
||||
"gitlens.remotes": [
|
||||
{
|
||||
"domain": "codeberg.org",
|
||||
"type": "Gitea"
|
||||
}
|
||||
],
|
||||
"css.format.spaceAroundSelectorSeparator": true,
|
||||
"[css]": {
|
||||
"editor.defaultFormatter": "vscode.css-language-features"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
17
README.md
17
README.md
|
|
@ -1,21 +1,26 @@
|
|||
# A fork. A fork. We're a fork.
|
||||
|
||||
|
||||
# Vencord
|
||||
|
||||

|
||||
[](https://codeberg.org/Vee/cord)
|
||||
|
||||
The cutest Discord client mod
|
||||
|
||||

|
||||
|  |
|
||||
| :--------------------------------------------------------------------------------------------------: |
|
||||
| A screenshot of vencord showcasing the [vencord-theme](https://github.com/synqat/vencord-theme) |
|
||||
|
||||
## Features
|
||||
|
||||
- Easy to install
|
||||
- [100+ built in plugins](https://vencord.dev/plugins)
|
||||
- Super easy to install (Download Installer, open, click install button, done)
|
||||
- 100+ plugins built in: [See a list](https://vencord.dev/plugins)
|
||||
- Some highlights: SpotifyControls, MessageLogger, Experiments, GameActivityToggle, Translate, NoTrack, QuickReply, Free Emotes/Stickers, PermissionsViewer, CustomCommands, ShowHiddenChannels, PronounDB
|
||||
- Fairly lightweight despite the many inbuilt plugins
|
||||
- Excellent Browser Support: Run Vencord in your Browser via extension or UserScript
|
||||
- Works on any Discord branch: Stable, Canary or PTB all work
|
||||
- Works on any Discord branch: Stable, Canary or PTB all work (though for the best experience I recommend stable!)
|
||||
- Custom CSS and Themes: Inbuilt css editor with support to import any css files (including BetterDiscord themes)
|
||||
- Privacy friendly: blocks Discord analytics & crash reporting out of the box and has no telemetry
|
||||
- Privacy friendly, blocks Discord analytics & crash reporting out of the box and has no telemetry
|
||||
- Maintained very actively, broken plugins are usually fixed within 12 hours
|
||||
- Settings sync: Keep your plugins and their settings synchronised between devices / apps (optional)
|
||||
|
||||
|
|
|
|||
|
|
@ -20,13 +20,16 @@
|
|||
/// <reference path="../src/globals.d.ts" />
|
||||
|
||||
import monacoHtmlLocal from "file://monacoWin.html?minify";
|
||||
import monacoHtmlCdn from "file://../src/main/monacoWin.html?minify";
|
||||
import * as DataStore from "../src/api/DataStore";
|
||||
import { debounce, localStorage } from "../src/utils";
|
||||
import { debounce } from "../src/utils";
|
||||
import { EXTENSION_BASE_URL } from "../src/utils/web-metadata";
|
||||
import { getTheme, Theme } from "../src/utils/discord";
|
||||
import { getThemeInfo } from "../src/main/themes";
|
||||
import { Settings } from "../src/Vencord";
|
||||
import { getStylusWebStoreUrl } from "@utils/web";
|
||||
|
||||
// Discord deletes this so need to store in variable
|
||||
const { localStorage } = window;
|
||||
|
||||
// listeners for ipc.on
|
||||
const cssListeners = new Set<(css: string) => void>();
|
||||
|
|
@ -42,13 +45,12 @@ window.VencordNative = {
|
|||
themes: {
|
||||
uploadTheme: (fileName: string, fileData: string) => DataStore.set(fileName, fileData, themeStore),
|
||||
deleteTheme: (fileName: string) => DataStore.del(fileName, themeStore),
|
||||
getThemesDir: async () => "",
|
||||
getThemesList: () => DataStore.entries(themeStore).then(entries =>
|
||||
entries.map(([name, css]) => getThemeInfo(css, name.toString()))
|
||||
),
|
||||
getThemeData: (fileName: string) => DataStore.get(fileName, themeStore),
|
||||
getSystemValues: async () => ({}),
|
||||
|
||||
openFolder: async () => Promise.reject("themes:openFolder is not supported on web"),
|
||||
},
|
||||
|
||||
native: {
|
||||
|
|
@ -75,14 +77,6 @@ window.VencordNative = {
|
|||
addThemeChangeListener: NOOP,
|
||||
openFile: NOOP_ASYNC,
|
||||
async openEditor() {
|
||||
if (IS_USERSCRIPT) {
|
||||
const shouldOpenWebStore = confirm("QuickCSS is not supported on the Userscript. You can instead use the Stylus extension.\n\nDo you want to open the Stylus web store page?");
|
||||
if (shouldOpenWebStore) {
|
||||
window.open(getStylusWebStoreUrl(), "_blank");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const features = `popup,width=${Math.min(window.innerWidth, 1000)},height=${Math.min(window.innerHeight, 1000)}`;
|
||||
const win = open("about:blank", "VencordQuickCss", features);
|
||||
if (!win) {
|
||||
|
|
@ -98,7 +92,7 @@ window.VencordNative = {
|
|||
? "vs-light"
|
||||
: "vs-dark";
|
||||
|
||||
win.document.write(monacoHtmlLocal);
|
||||
win.document.write(IS_EXTENSION ? monacoHtmlLocal : monacoHtmlCdn);
|
||||
},
|
||||
},
|
||||
|
||||
|
|
@ -112,9 +106,8 @@ window.VencordNative = {
|
|||
}
|
||||
},
|
||||
set: async (s: Settings) => localStorage.setItem("VencordSettings", JSON.stringify(s)),
|
||||
openFolder: async () => Promise.reject("settings:openFolder is not supported on web"),
|
||||
getSettingsDir: async () => "LocalStorage"
|
||||
},
|
||||
|
||||
pluginHelpers: {} as any,
|
||||
csp: {} as any,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "vencord",
|
||||
"private": "true",
|
||||
"version": "1.13.0",
|
||||
"version": "1.12.2",
|
||||
"description": "The cutest Discord client mod",
|
||||
"homepage": "https://github.com/Vendicated/Vencord#readme",
|
||||
"bugs": {
|
||||
|
|
@ -53,8 +53,8 @@
|
|||
"@types/react": "^19.0.10",
|
||||
"@types/react-dom": "^19.0.4",
|
||||
"@types/yazl": "^2.4.5",
|
||||
"@vencord/discord-types": "link:packages/discord-types",
|
||||
"diff": "^7.0.0",
|
||||
"discord-types": "^1.3.26",
|
||||
"esbuild": "^0.25.1",
|
||||
"eslint": "9.20.1",
|
||||
"eslint-import-resolver-alias": "^1.1.2",
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
Hint: https://docs.discord.food is an incredible resource and allows you to copy paste complete enums and interfaces
|
||||
|
|
@ -1,165 +0,0 @@
|
|||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
|
||||
This version of the GNU Lesser General Public License incorporates
|
||||
the terms and conditions of version 3 of the GNU General Public
|
||||
License, supplemented by the additional permissions listed below.
|
||||
|
||||
0. Additional Definitions.
|
||||
|
||||
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||
General Public License.
|
||||
|
||||
"The Library" refers to a covered work governed by this License,
|
||||
other than an Application or a Combined Work as defined below.
|
||||
|
||||
An "Application" is any work that makes use of an interface provided
|
||||
by the Library, but which is not otherwise based on the Library.
|
||||
Defining a subclass of a class defined by the Library is deemed a mode
|
||||
of using an interface provided by the Library.
|
||||
|
||||
A "Combined Work" is a work produced by combining or linking an
|
||||
Application with the Library. The particular version of the Library
|
||||
with which the Combined Work was made is also called the "Linked
|
||||
Version".
|
||||
|
||||
The "Minimal Corresponding Source" for a Combined Work means the
|
||||
Corresponding Source for the Combined Work, excluding any source code
|
||||
for portions of the Combined Work that, considered in isolation, are
|
||||
based on the Application, and not on the Linked Version.
|
||||
|
||||
The "Corresponding Application Code" for a Combined Work means the
|
||||
object code and/or source code for the Application, including any data
|
||||
and utility programs needed for reproducing the Combined Work from the
|
||||
Application, but excluding the System Libraries of the Combined Work.
|
||||
|
||||
1. Exception to Section 3 of the GNU GPL.
|
||||
|
||||
You may convey a covered work under sections 3 and 4 of this License
|
||||
without being bound by section 3 of the GNU GPL.
|
||||
|
||||
2. Conveying Modified Versions.
|
||||
|
||||
If you modify a copy of the Library, and, in your modifications, a
|
||||
facility refers to a function or data to be supplied by an Application
|
||||
that uses the facility (other than as an argument passed when the
|
||||
facility is invoked), then you may convey a copy of the modified
|
||||
version:
|
||||
|
||||
a) under this License, provided that you make a good faith effort to
|
||||
ensure that, in the event an Application does not supply the
|
||||
function or data, the facility still operates, and performs
|
||||
whatever part of its purpose remains meaningful, or
|
||||
|
||||
b) under the GNU GPL, with none of the additional permissions of
|
||||
this License applicable to that copy.
|
||||
|
||||
3. Object Code Incorporating Material from Library Header Files.
|
||||
|
||||
The object code form of an Application may incorporate material from
|
||||
a header file that is part of the Library. You may convey such object
|
||||
code under terms of your choice, provided that, if the incorporated
|
||||
material is not limited to numerical parameters, data structure
|
||||
layouts and accessors, or small macros, inline functions and templates
|
||||
(ten or fewer lines in length), you do both of the following:
|
||||
|
||||
a) Give prominent notice with each copy of the object code that the
|
||||
Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
4. Combined Works.
|
||||
|
||||
You may convey a Combined Work under terms of your choice that,
|
||||
taken together, effectively do not restrict modification of the
|
||||
portions of the Library contained in the Combined Work and reverse
|
||||
engineering for debugging such modifications, if you also do each of
|
||||
the following:
|
||||
|
||||
a) Give prominent notice with each copy of the Combined Work that
|
||||
the Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
c) For a Combined Work that displays copyright notices during
|
||||
execution, include the copyright notice for the Library among
|
||||
these notices, as well as a reference directing the user to the
|
||||
copies of the GNU GPL and this license document.
|
||||
|
||||
d) Do one of the following:
|
||||
|
||||
0) Convey the Minimal Corresponding Source under the terms of this
|
||||
License, and the Corresponding Application Code in a form
|
||||
suitable for, and under terms that permit, the user to
|
||||
recombine or relink the Application with a modified version of
|
||||
the Linked Version to produce a modified Combined Work, in the
|
||||
manner specified by section 6 of the GNU GPL for conveying
|
||||
Corresponding Source.
|
||||
|
||||
1) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (a) uses at run time
|
||||
a copy of the Library already present on the user's computer
|
||||
system, and (b) will operate properly with a modified version
|
||||
of the Library that is interface-compatible with the Linked
|
||||
Version.
|
||||
|
||||
e) Provide Installation Information, but only if you would otherwise
|
||||
be required to provide such information under section 6 of the
|
||||
GNU GPL, and only to the extent that such information is
|
||||
necessary to install and execute a modified version of the
|
||||
Combined Work produced by recombining or relinking the
|
||||
Application with a modified version of the Linked Version. (If
|
||||
you use option 4d0, the Installation Information must accompany
|
||||
the Minimal Corresponding Source and Corresponding Application
|
||||
Code. If you use option 4d1, you must provide the Installation
|
||||
Information in the manner specified by section 6 of the GNU GPL
|
||||
for conveying Corresponding Source.)
|
||||
|
||||
5. Combined Libraries.
|
||||
|
||||
You may place library facilities that are a work based on the
|
||||
Library side by side in a single library together with other library
|
||||
facilities that are not Applications and are not covered by this
|
||||
License, and convey such a combined library under terms of your
|
||||
choice, if you do both of the following:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work based
|
||||
on the Library, uncombined with any other library facilities,
|
||||
conveyed under the terms of this License.
|
||||
|
||||
b) Give prominent notice with the combined library that part of it
|
||||
is a work based on the Library, and explaining where to find the
|
||||
accompanying uncombined form of the same work.
|
||||
|
||||
6. Revised Versions of the GNU Lesser General Public License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions
|
||||
of the GNU Lesser General Public License from time to time. Such new
|
||||
versions will be similar in spirit to the present version, but may
|
||||
differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Library as you received it specifies that a certain numbered version
|
||||
of the GNU Lesser General Public License "or any later version"
|
||||
applies to it, you have the option of following the terms and
|
||||
conditions either of that published version or of any later version
|
||||
published by the Free Software Foundation. If the Library as you
|
||||
received it does not specify a version number of the GNU Lesser
|
||||
General Public License, you may choose any version of the GNU Lesser
|
||||
General Public License ever published by the Free Software Foundation.
|
||||
|
||||
If the Library as you received it specifies that a proxy can decide
|
||||
whether future versions of the GNU Lesser General Public License shall
|
||||
apply, that proxy's public statement of acceptance of any version is
|
||||
permanent authorization for you to choose that version for the
|
||||
Library.
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
# Discord Types
|
||||
|
||||
This package provides TypeScript types for the Webpack modules of Discord's web app.
|
||||
|
||||
While it was primarily created for Vencord, other client mods could also benefit from this, so it is published as a standalone package!
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
npm install -D @vencord/discord-types
|
||||
yarn add -D @vencord/discord-types
|
||||
pnpm add -D @vencord/discord-types
|
||||
```
|
||||
|
||||
## Example Usage
|
||||
|
||||
```ts
|
||||
import type { UserStore } from "@vencord/discord-types";
|
||||
|
||||
const userStore: UserStore = findStore("UserStore"); // findStore is up to you to implement, this library only provides types and no runtime code
|
||||
```
|
||||
|
||||
## Enums
|
||||
|
||||
This library also exports some const enums that you can use from Typescript code:
|
||||
```ts
|
||||
import { ApplicationCommandType } from "@vencord/discord-types/enums";
|
||||
|
||||
console.log(ApplicationCommandType.CHAT_INPUT); // 1
|
||||
```
|
||||
|
||||
### License
|
||||
|
||||
This package is licensed under the [LGPL-3.0](./LICENSE) (or later) license.
|
||||
|
||||
A very short summary of the license is that you can use this package as a library in both open source and closed source projects,
|
||||
similar to an MIT-licensed project.
|
||||
However, if you modify the code of this package, you must release source code of your modified version under the same license.
|
||||
|
||||
### Credit
|
||||
|
||||
This package was inspired by Swishilicous' [discord-types](https://www.npmjs.com/package/discord-types) package.
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
export const enum ActivityType {
|
||||
PLAYING = 0,
|
||||
STREAMING = 1,
|
||||
LISTENING = 2,
|
||||
WATCHING = 3,
|
||||
CUSTOM_STATUS = 4,
|
||||
COMPETING = 5,
|
||||
HANG_STATUS = 6
|
||||
}
|
||||
|
||||
export const enum ActivityFlags {
|
||||
INSTANCE = 1 << 0,
|
||||
JOIN = 1 << 1,
|
||||
/** @deprecated */
|
||||
SPECTATE = 1 << 2,
|
||||
/** @deprecated */
|
||||
JOIN_REQUEST = 1 << 3,
|
||||
SYNC = 1 << 4,
|
||||
PLAY = 1 << 5,
|
||||
PARTY_PRIVACY_FRIENDS = 1 << 6,
|
||||
PARTY_PRIVACY_VOICE_CHANNEL = 1 << 7,
|
||||
EMBEDDED = 1 << 8,
|
||||
CONTEXTLESS = 1 << 9
|
||||
}
|
||||
|
||||
export const enum ActivityStatusDisplayType {
|
||||
NAME = 0,
|
||||
STATE = 1,
|
||||
DETAILS = 2
|
||||
}
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
export const enum ChannelType {
|
||||
GUILD_TEXT = 0,
|
||||
DM = 1,
|
||||
GUILD_VOICE = 2,
|
||||
GROUP_DM = 3,
|
||||
GUILD_CATEGORY = 4,
|
||||
GUILD_ANNOUNCEMENT = 5,
|
||||
ANNOUNCEMENT_THREAD = 10,
|
||||
PUBLIC_THREAD = 11,
|
||||
PRIVATE_THREAD = 12,
|
||||
GUILD_STAGE_VOICE = 13,
|
||||
GUILD_DIRECTORY = 14,
|
||||
GUILD_FORUM = 15,
|
||||
GUILD_MEDIA = 16
|
||||
}
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
export const enum ApplicationCommandOptionType {
|
||||
SUB_COMMAND = 1,
|
||||
SUB_COMMAND_GROUP = 2,
|
||||
STRING = 3,
|
||||
INTEGER = 4,
|
||||
BOOLEAN = 5,
|
||||
USER = 6,
|
||||
CHANNEL = 7,
|
||||
ROLE = 8,
|
||||
MENTIONABLE = 9,
|
||||
NUMBER = 10,
|
||||
ATTACHMENT = 11,
|
||||
}
|
||||
|
||||
export const enum ApplicationCommandInputType {
|
||||
BUILT_IN = 0,
|
||||
BUILT_IN_TEXT = 1,
|
||||
BUILT_IN_INTEGRATION = 2,
|
||||
BOT = 3,
|
||||
PLACEHOLDER = 4,
|
||||
}
|
||||
|
||||
export const enum ApplicationCommandType {
|
||||
CHAT_INPUT = 1,
|
||||
USER = 2,
|
||||
MESSAGE = 3,
|
||||
}
|
||||
|
||||
export const enum ApplicationIntegrationType {
|
||||
GUILD_INSTALL = 0,
|
||||
USER_INSTALL = 1
|
||||
}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
export * from "./activity";
|
||||
export * from "./channel";
|
||||
export * from "./commands";
|
||||
export * from "./messages";
|
||||
export * from "./misc";
|
||||
|
|
@ -1,596 +0,0 @@
|
|||
export const enum StickerType {
|
||||
/** an official sticker in a pack */
|
||||
STANDARD = 1,
|
||||
/** a sticker uploaded to a guild for the guild's members */
|
||||
GUILD = 2
|
||||
}
|
||||
|
||||
export const enum StickerFormatType {
|
||||
PNG = 1,
|
||||
APNG = 2,
|
||||
LOTTIE = 3,
|
||||
GIF = 4
|
||||
}
|
||||
|
||||
export const enum MessageType {
|
||||
/**
|
||||
* A default message (see below)
|
||||
*
|
||||
* Value: 0
|
||||
* Name: DEFAULT
|
||||
* Rendered Content: "{content}"
|
||||
* Deletable: true
|
||||
*/
|
||||
DEFAULT = 0,
|
||||
/**
|
||||
* A message sent when a user is added to a group DM or thread
|
||||
*
|
||||
* Value: 1
|
||||
* Name: RECIPIENT_ADD
|
||||
* Rendered Content: "{author} added {mentions [0] } to the {group/thread}."
|
||||
* Deletable: false
|
||||
*/
|
||||
RECIPIENT_ADD = 1,
|
||||
/**
|
||||
* A message sent when a user is removed from a group DM or thread
|
||||
*
|
||||
* Value: 2
|
||||
* Name: RECIPIENT_REMOVE
|
||||
* Rendered Content: "{author} removed {mentions [0] } from the {group/thread}."
|
||||
* Deletable: false
|
||||
*/
|
||||
RECIPIENT_REMOVE = 2,
|
||||
/**
|
||||
* A message sent when a user creates a call in a private channel
|
||||
*
|
||||
* Value: 3
|
||||
* Name: CALL
|
||||
* Rendered Content: participated ? "{author} started a call{ended ? " that lasted {duration}" : " — Join the call"}." : "You missed a call from {author} that lasted {duration}."
|
||||
* Deletable: false
|
||||
*/
|
||||
CALL = 3,
|
||||
/**
|
||||
* A message sent when a group DM or thread's name is changed
|
||||
*
|
||||
* Value: 4
|
||||
* Name: CHANNEL_NAME_CHANGE
|
||||
* Rendered Content: "{author} changed the {is_forum ? "post title" : "channel name"}: {content} "
|
||||
* Deletable: false
|
||||
*/
|
||||
CHANNEL_NAME_CHANGE = 4,
|
||||
/**
|
||||
* A message sent when a group DM's icon is changed
|
||||
*
|
||||
* Value: 5
|
||||
* Name: CHANNEL_ICON_CHANGE
|
||||
* Rendered Content: "{author} changed the channel icon."
|
||||
* Deletable: false
|
||||
*/
|
||||
CHANNEL_ICON_CHANGE = 5,
|
||||
/**
|
||||
* A message sent when a message is pinned in a channel
|
||||
*
|
||||
* Value: 6
|
||||
* Name: CHANNEL_PINNED_MESSAGE
|
||||
* Rendered Content: "{author} pinned a message to this channel."
|
||||
* Deletable: true
|
||||
*/
|
||||
CHANNEL_PINNED_MESSAGE = 6,
|
||||
/**
|
||||
* A message sent when a user joins a guild
|
||||
*
|
||||
* Value: 7
|
||||
* Name: USER_JOIN
|
||||
* Rendered Content: See user join message type , obtained via the formula timestamp_ms % 13
|
||||
* Deletable: true
|
||||
*/
|
||||
USER_JOIN = 7,
|
||||
/**
|
||||
* A message sent when a user subscribes to (boosts) a guild
|
||||
*
|
||||
* Value: 8
|
||||
* Name: PREMIUM_GUILD_SUBSCRIPTION
|
||||
* Rendered Content: "{author} just boosted the server{content ? " {content} times"}!"
|
||||
* Deletable: true
|
||||
*/
|
||||
PREMIUM_GUILD_SUBSCRIPTION = 8,
|
||||
/**
|
||||
* A message sent when a user subscribes to (boosts) a guild to tier 1
|
||||
*
|
||||
* Value: 9
|
||||
* Name: PREMIUM_GUILD_SUBSCRIPTION_TIER_1
|
||||
* Rendered Content: "{author} just boosted the server{content ? " {content} times"}! {guild} has achieved Level 1! "
|
||||
* Deletable: true
|
||||
*/
|
||||
PREMIUM_GUILD_SUBSCRIPTION_TIER_1 = 9,
|
||||
/**
|
||||
* A message sent when a user subscribes to (boosts) a guild to tier 2
|
||||
*
|
||||
* Value: 10
|
||||
* Name: PREMIUM_GUILD_SUBSCRIPTION_TIER_2
|
||||
* Rendered Content: "{author} just boosted the server{content ? " {content} times"}! {guild} has achieved Level 2! "
|
||||
* Deletable: true
|
||||
*/
|
||||
PREMIUM_GUILD_SUBSCRIPTION_TIER_2 = 10,
|
||||
/**
|
||||
* A message sent when a user subscribes to (boosts) a guild to tier 3
|
||||
*
|
||||
* Value: 11
|
||||
* Name: PREMIUM_GUILD_SUBSCRIPTION_TIER_3
|
||||
* Rendered Content: "{author} just boosted the server{content ? " {content} times"}! {guild} has achieved Level 3! "
|
||||
* Deletable: true
|
||||
*/
|
||||
PREMIUM_GUILD_SUBSCRIPTION_TIER_3 = 11,
|
||||
/**
|
||||
* A message sent when a news channel is followed
|
||||
*
|
||||
* Value: 12
|
||||
* Name: CHANNEL_FOLLOW_ADD
|
||||
* Rendered Content: "{author} has added {content} to this channel. Its most important updates will show up here."
|
||||
* Deletable: true
|
||||
*/
|
||||
CHANNEL_FOLLOW_ADD = 12,
|
||||
/**
|
||||
* A message sent when a guild is disqualified from discovery
|
||||
*
|
||||
* Value: 14
|
||||
* Name: GUILD_DISCOVERY_DISQUALIFIED
|
||||
* Rendered Content: "This server has been removed from Server Discovery because it no longer passes all the requirements. Check Server Settings for more details."
|
||||
* Deletable: true
|
||||
*/
|
||||
GUILD_DISCOVERY_DISQUALIFIED = 14,
|
||||
/**
|
||||
* A message sent when a guild requalifies for discovery
|
||||
*
|
||||
* Value: 15
|
||||
* Name: GUILD_DISCOVERY_REQUALIFIED
|
||||
* Rendered Content: "This server is eligible for Server Discovery again and has been automatically relisted!"
|
||||
* Deletable: true
|
||||
*/
|
||||
GUILD_DISCOVERY_REQUALIFIED = 15,
|
||||
/**
|
||||
* A message sent when a guild has failed discovery requirements for a week
|
||||
*
|
||||
* Value: 16
|
||||
* Name: GUILD_DISCOVERY_GRACE_PERIOD_INITIAL_WARNING
|
||||
* Rendered Content: "This server has failed Discovery activity requirements for 1 week. If this server fails for 4 weeks in a row, it will be automatically removed from Discovery."
|
||||
* Deletable: true
|
||||
*/
|
||||
GUILD_DISCOVERY_GRACE_PERIOD_INITIAL_WARNING = 16,
|
||||
/**
|
||||
* A message sent when a guild has failed discovery requirements for 3 weeks
|
||||
*
|
||||
* Value: 17
|
||||
* Name: GUILD_DISCOVERY_GRACE_PERIOD_FINAL_WARNING
|
||||
* Rendered Content: "This server has failed Discovery activity requirements for 3 weeks in a row. If this server fails for 1 more week, it will be removed from Discovery."
|
||||
* Deletable: true
|
||||
*/
|
||||
GUILD_DISCOVERY_GRACE_PERIOD_FINAL_WARNING = 17,
|
||||
/**
|
||||
* A message sent when a thread is created
|
||||
*
|
||||
* Value: 18
|
||||
* Name: THREAD_CREATED
|
||||
* Rendered Content: "{author} started a thread: {content} . See all threads."
|
||||
* Deletable: true
|
||||
*/
|
||||
THREAD_CREATED = 18,
|
||||
/**
|
||||
* A message sent when a user replies to a message
|
||||
*
|
||||
* Value: 19
|
||||
* Name: REPLY
|
||||
* Rendered Content: "{content}"
|
||||
* Deletable: true
|
||||
*/
|
||||
REPLY = 19,
|
||||
/**
|
||||
* A message sent when a user uses a slash command
|
||||
*
|
||||
* Value: 20
|
||||
* Name: CHAT_INPUT_COMMAND
|
||||
* Rendered Content: "{content}"
|
||||
* Deletable: true
|
||||
*/
|
||||
CHAT_INPUT_COMMAND = 20,
|
||||
/**
|
||||
* A message sent when a thread starter message is added to a thread
|
||||
*
|
||||
* Value: 21
|
||||
* Name: THREAD_STARTER_MESSAGE
|
||||
* Rendered Content: "{referenced_message?.content}" ?? "Sorry, we couldn't load the first message in this thread"
|
||||
* Deletable: false
|
||||
*/
|
||||
THREAD_STARTER_MESSAGE = 21,
|
||||
/**
|
||||
* A message sent to remind users to invite friends to a guild
|
||||
*
|
||||
* Value: 22
|
||||
* Name: GUILD_INVITE_REMINDER
|
||||
* Rendered Content: "Wondering who to invite?\nStart by inviting anyone who can help you build the server!"
|
||||
* Deletable: true
|
||||
*/
|
||||
GUILD_INVITE_REMINDER = 22,
|
||||
/**
|
||||
* A message sent when a user uses a context menu command
|
||||
*
|
||||
* Value: 23
|
||||
* Name: CONTEXT_MENU_COMMAND
|
||||
* Rendered Content: "{content}"
|
||||
* Deletable: true
|
||||
*/
|
||||
CONTEXT_MENU_COMMAND = 23,
|
||||
/**
|
||||
* A message sent when auto moderation takes an action
|
||||
*
|
||||
* Value: 24
|
||||
* Name: AUTO_MODERATION_ACTION
|
||||
* Rendered Content: Special embed rendered from embeds[0]
|
||||
* Deletable: true 1
|
||||
*/
|
||||
AUTO_MODERATION_ACTION = 24,
|
||||
/**
|
||||
* A message sent when a user purchases or renews a role subscription
|
||||
*
|
||||
* Value: 25
|
||||
* Name: ROLE_SUBSCRIPTION_PURCHASE
|
||||
* Rendered Content: "{author} {is_renewal ? "renewed" : "joined"} {role_subscription.tier_name} and has been a subscriber of {guild} for {role_subscription.total_months_subscribed} month(?s)!"
|
||||
* Deletable: true
|
||||
*/
|
||||
ROLE_SUBSCRIPTION_PURCHASE = 25,
|
||||
/**
|
||||
* A message sent when a user is upsold to a premium interaction
|
||||
*
|
||||
* Value: 26
|
||||
* Name: INTERACTION_PREMIUM_UPSELL
|
||||
* Rendered Content: "{content}"
|
||||
* Deletable: true
|
||||
*/
|
||||
INTERACTION_PREMIUM_UPSELL = 26,
|
||||
/**
|
||||
* A message sent when a stage channel starts
|
||||
*
|
||||
* Value: 27
|
||||
* Name: STAGE_START
|
||||
* Rendered Content: "{author} started {content} "
|
||||
* Deletable: true
|
||||
*/
|
||||
STAGE_START = 27,
|
||||
/**
|
||||
* A message sent when a stage channel ends
|
||||
*
|
||||
* Value: 28
|
||||
* Name: STAGE_END
|
||||
* Rendered Content: "{author} ended {content} "
|
||||
* Deletable: true
|
||||
*/
|
||||
STAGE_END = 28,
|
||||
/**
|
||||
* A message sent when a user starts speaking in a stage channel
|
||||
*
|
||||
* Value: 29
|
||||
* Name: STAGE_SPEAKER
|
||||
* Rendered Content: "{author} is now a speaker."
|
||||
* Deletable: true
|
||||
*/
|
||||
STAGE_SPEAKER = 29,
|
||||
/**
|
||||
* A message sent when a user raises their hand in a stage channel
|
||||
*
|
||||
* Value: 30
|
||||
* Name: STAGE_RAISE_HAND
|
||||
* Rendered Content: "{author} requested to speak."
|
||||
* Deletable: true
|
||||
*/
|
||||
STAGE_RAISE_HAND = 30,
|
||||
/**
|
||||
* A message sent when a stage channel's topic is changed
|
||||
*
|
||||
* Value: 31
|
||||
* Name: STAGE_TOPIC
|
||||
* Rendered Content: "{author} changed the Stage topic: {content} "
|
||||
* Deletable: true
|
||||
*/
|
||||
STAGE_TOPIC = 31,
|
||||
/**
|
||||
* A message sent when a user purchases an application premium subscription
|
||||
*
|
||||
* Value: 32
|
||||
* Name: GUILD_APPLICATION_PREMIUM_SUBSCRIPTION
|
||||
* Rendered Content: "{author} upgraded {application ?? "a deleted application"} to premium for this server!"
|
||||
* Deletable: true
|
||||
*/
|
||||
GUILD_APPLICATION_PREMIUM_SUBSCRIPTION = 32,
|
||||
/**
|
||||
* A message sent when a user gifts a premium (Nitro) referral
|
||||
*
|
||||
* Value: 35
|
||||
* Name: PREMIUM_REFERRAL
|
||||
* Rendered Content: "{content}"
|
||||
* Deletable: true
|
||||
*/
|
||||
PREMIUM_REFERRAL = 35,
|
||||
/**
|
||||
* A message sent when a user enabled lockdown for the guild
|
||||
*
|
||||
* Value: 36
|
||||
* Name: GUILD_INCIDENT_ALERT_MODE_ENABLED
|
||||
* Rendered Content: "{author} enabled security actions until {content}."
|
||||
* Deletable: true
|
||||
*/
|
||||
GUILD_INCIDENT_ALERT_MODE_ENABLED = 36,
|
||||
/**
|
||||
* A message sent when a user disables lockdown for the guild
|
||||
*
|
||||
* Value: 37
|
||||
* Name: GUILD_INCIDENT_ALERT_MODE_DISABLED
|
||||
* Rendered Content: "{author} disabled security actions."
|
||||
* Deletable: true
|
||||
*/
|
||||
GUILD_INCIDENT_ALERT_MODE_DISABLED = 37,
|
||||
/**
|
||||
* A message sent when a user reports a raid for the guild
|
||||
*
|
||||
* Value: 38
|
||||
* Name: GUILD_INCIDENT_REPORT_RAID
|
||||
* Rendered Content: "{author} reported a raid in {guild}."
|
||||
* Deletable: true
|
||||
*/
|
||||
GUILD_INCIDENT_REPORT_RAID = 38,
|
||||
/**
|
||||
* A message sent when a user reports a false alarm for the guild
|
||||
*
|
||||
* Value: 39
|
||||
* Name: GUILD_INCIDENT_REPORT_FALSE_ALARM
|
||||
* Rendered Content: "{author} reported a false alarm in {guild}."
|
||||
* Deletable: true
|
||||
*/
|
||||
GUILD_INCIDENT_REPORT_FALSE_ALARM = 39,
|
||||
/**
|
||||
* A message sent when no one sends a message in the current channel for 1 hour
|
||||
*
|
||||
* Value: 40
|
||||
* Name: GUILD_DEADCHAT_REVIVE_PROMPT
|
||||
* Rendered Content: "{content}"
|
||||
* Deletable: true
|
||||
*/
|
||||
GUILD_DEADCHAT_REVIVE_PROMPT = 40,
|
||||
/**
|
||||
* A message sent when a user buys another user a gift
|
||||
*
|
||||
* Value: 41
|
||||
* Name: CUSTOM_GIFT
|
||||
* Rendered Content: Special embed rendered from embeds[0].url and gift_info
|
||||
* Deletable: true
|
||||
*/
|
||||
CUSTOM_GIFT = 41,
|
||||
/**
|
||||
* Value: 42
|
||||
* Name: GUILD_GAMING_STATS_PROMPT
|
||||
* Rendered Content: "{content}"
|
||||
* Deletable: true
|
||||
*/
|
||||
GUILD_GAMING_STATS_PROMPT = 42,
|
||||
/**
|
||||
* A message sent when a user purchases a guild product
|
||||
*
|
||||
* Value: 44
|
||||
* Name: PURCHASE_NOTIFICATION
|
||||
* Rendered Content: "{author} has purchased {purchase_notification.guild_product_purchase.product_name}!"
|
||||
* Deletable: true
|
||||
*/
|
||||
PURCHASE_NOTIFICATION = 44,
|
||||
/**
|
||||
* A message sent when a poll is finalized
|
||||
*
|
||||
* Value: 46
|
||||
* Name: POLL_RESULT
|
||||
* Rendered Content: Special embed rendered from embeds[0]
|
||||
* Deletable: true
|
||||
*/
|
||||
POLL_RESULT = 46,
|
||||
/**
|
||||
* A message sent by the Discord Updates account when a new changelog is posted
|
||||
*
|
||||
* Value: 47
|
||||
* Name: CHANGELOG
|
||||
* Rendered Content: "{content}"
|
||||
* Deletable: true
|
||||
*/
|
||||
CHANGELOG = 47,
|
||||
/**
|
||||
* A message sent when a Nitro promotion is triggered
|
||||
*
|
||||
* Value: 48
|
||||
* Name: NITRO_NOTIFICATION
|
||||
* Rendered Content: Special embed rendered from content
|
||||
* Deletable: true
|
||||
*/
|
||||
NITRO_NOTIFICATION = 48,
|
||||
/**
|
||||
* A message sent when a voice channel is linked to a lobby
|
||||
*
|
||||
* Value: 49
|
||||
* Name: CHANNEL_LINKED_TO_LOBBY
|
||||
* Rendered Content: "{content}"
|
||||
* Deletable: true
|
||||
*/
|
||||
CHANNEL_LINKED_TO_LOBBY = 49,
|
||||
/**
|
||||
* A local-only ephemeral message sent when a user is prompted to gift Nitro to a friend on their friendship anniversary
|
||||
*
|
||||
* Value: 50
|
||||
* Name: GIFTING_PROMPT
|
||||
* Rendered Content: Special embed
|
||||
* Deletable: true
|
||||
*/
|
||||
GIFTING_PROMPT = 50,
|
||||
/**
|
||||
* A local-only message sent when a user receives an in-game message NUX
|
||||
*
|
||||
* Value: 51
|
||||
* Name: IN_GAME_MESSAGE_NUX
|
||||
* Rendered Content: "{author} messaged you from {application.name}. In-game chat may not include rich messaging features such as images, polls, or apps. Learn More "
|
||||
* Deletable: true
|
||||
*/
|
||||
IN_GAME_MESSAGE_NUX = 51,
|
||||
/**
|
||||
* A message sent when a user accepts a guild join request
|
||||
*
|
||||
* Value: 52
|
||||
* Name: GUILD_JOIN_REQUEST_ACCEPT_NOTIFICATION 2
|
||||
* Rendered Content: "{join_request.user}'s application to {content} was approved! Welcome!"
|
||||
* Deletable: true
|
||||
*/
|
||||
GUILD_JOIN_REQUEST_ACCEPT_NOTIFICATION = 52,
|
||||
/**
|
||||
* A message sent when a user rejects a guild join request
|
||||
*
|
||||
* Value: 53
|
||||
* Name: GUILD_JOIN_REQUEST_REJECT_NOTIFICATION 2
|
||||
* Rendered Content: "{join_request.user}'s application to {content} was rejected."
|
||||
* Deletable: true
|
||||
*/
|
||||
GUILD_JOIN_REQUEST_REJECT_NOTIFICATION = 53,
|
||||
/**
|
||||
* A message sent when a user withdraws a guild join request
|
||||
*
|
||||
* Value: 54
|
||||
* Name: GUILD_JOIN_REQUEST_WITHDRAWN_NOTIFICATION 2
|
||||
* Rendered Content: "{join_request.user}'s application to {content} has been withdrawn."
|
||||
* Deletable: true
|
||||
*/
|
||||
GUILD_JOIN_REQUEST_WITHDRAWN_NOTIFICATION = 54,
|
||||
/**
|
||||
* A message sent when a user upgrades to HD streaming
|
||||
*
|
||||
* Value: 55
|
||||
* Name: HD_STREAMING_UPGRADED
|
||||
* Rendered Content: "{author} activated HD Splash Potion "
|
||||
* Deletable: true
|
||||
*/
|
||||
HD_STREAMING_UPGRADED = 55,
|
||||
/**
|
||||
* A message sent when a user resolves a moderation report by deleting the offending message
|
||||
*
|
||||
* Value: 58
|
||||
* Name: REPORT_TO_MOD_DELETED_MESSAGE
|
||||
* Rendered Content: "{author} deleted the message"
|
||||
* Deletable: true
|
||||
*/
|
||||
REPORT_TO_MOD_DELETED_MESSAGE = 58,
|
||||
/**
|
||||
* A message sent when a user resolves a moderation report by timing out the offending user
|
||||
*
|
||||
* Value: 59
|
||||
* Name: REPORT_TO_MOD_TIMEOUT_USER
|
||||
* Rendered Content: "{author} timed out {mentions [0] }"
|
||||
* Deletable: true
|
||||
*/
|
||||
REPORT_TO_MOD_TIMEOUT_USER = 59,
|
||||
/**
|
||||
* A message sent when a user resolves a moderation report by kicking the offending user
|
||||
*
|
||||
* Value: 60
|
||||
* Name: REPORT_TO_MOD_KICK_USER
|
||||
* Rendered Content: "{author} kicked {mentions [0] }"
|
||||
* Deletable: true
|
||||
*/
|
||||
REPORT_TO_MOD_KICK_USER = 60,
|
||||
/**
|
||||
* A message sent when a user resolves a moderation report by banning the offending user
|
||||
*
|
||||
* Value: 61
|
||||
* Name: REPORT_TO_MOD_BAN_USER
|
||||
* Rendered Content: "{author} banned {mentions [0] }"
|
||||
* Deletable: true
|
||||
*/
|
||||
REPORT_TO_MOD_BAN_USER = 61,
|
||||
/**
|
||||
* A message sent when a user resolves a moderation report
|
||||
*
|
||||
* Value: 62
|
||||
* Name: REPORT_TO_MOD_CLOSED_REPORT
|
||||
* Rendered Content: "{author} resolved this flag"
|
||||
* Deletable: true
|
||||
*/
|
||||
REPORT_TO_MOD_CLOSED_REPORT = 62,
|
||||
/**
|
||||
* A message sent when a user adds a new emoji to a guild
|
||||
*
|
||||
* Value: 63
|
||||
* Name: EMOJI_ADDED
|
||||
* Rendered Content: "{author} added a new emoji, {content} :{emoji.name}: "
|
||||
* Deletable: true
|
||||
*/
|
||||
EMOJI_ADDED = 63,
|
||||
}
|
||||
|
||||
export const enum MessageFlags {
|
||||
/**
|
||||
* Message has been published to subscribed channels (via Channel Following)
|
||||
*
|
||||
* Value: 1 << 0
|
||||
*/
|
||||
CROSSPOSTED = 1 << 0,
|
||||
/**
|
||||
* Message originated from a message in another channel (via Channel Following)
|
||||
*/
|
||||
IS_CROSSPOST = 1 << 1,
|
||||
/**
|
||||
* Embeds will not be included when serializing this message
|
||||
*/
|
||||
SUPPRESS_EMBEDS = 1 << 2,
|
||||
/**
|
||||
* Source message for this crosspost has been deleted (via Channel Following)
|
||||
*/
|
||||
SOURCE_MESSAGE_DELETED = 1 << 3,
|
||||
/**
|
||||
* Message came from the urgent message system
|
||||
*/
|
||||
URGENT = 1 << 4,
|
||||
/**
|
||||
* Message has an associated thread, with the same ID as the message
|
||||
*/
|
||||
HAS_THREAD = 1 << 5,
|
||||
/**
|
||||
* Message is only visible to the user who invoked the interaction
|
||||
*/
|
||||
EPHEMERAL = 1 << 6,
|
||||
/**
|
||||
* Message is an interaction response and the bot is "thinking"
|
||||
*/
|
||||
LOADING = 1 << 7,
|
||||
/**
|
||||
* Some roles were not mentioned and added to the thread
|
||||
*/
|
||||
FAILED_TO_MENTION_SOME_ROLES_IN_THREAD = 1 << 8,
|
||||
/**
|
||||
* Message is hidden from the guild's feed
|
||||
*/
|
||||
GUILD_FEED_HIDDEN = 1 << 9,
|
||||
/**
|
||||
* Message contains a link that impersonates Discord
|
||||
*/
|
||||
SHOULD_SHOW_LINK_NOT_DISCORD_WARNING = 1 << 10,
|
||||
/**
|
||||
* Message will not trigger push and desktop notifications
|
||||
*/
|
||||
SUPPRESS_NOTIFICATIONS = 1 << 12,
|
||||
/**
|
||||
* Message's audio attachment is rendered as a voice message
|
||||
*/
|
||||
IS_VOICE_MESSAGE = 1 << 13,
|
||||
/**
|
||||
* Message has a forwarded message snapshot attached
|
||||
*/
|
||||
HAS_SNAPSHOT = 1 << 14,
|
||||
/**
|
||||
* Message contains components from version 2 of the UI kit
|
||||
*/
|
||||
IS_COMPONENTS_V2 = 1 << 15,
|
||||
/**
|
||||
* Message was triggered by the social layer integration
|
||||
*/
|
||||
SENT_BY_SOCIAL_LAYER_INTEGRATION = 1 << 16,
|
||||
}
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
export const enum CloudUploadPlatform {
|
||||
REACT_NATIVE = 0,
|
||||
WEB = 1,
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
{
|
||||
"name": "@vencord/discord-types",
|
||||
"author": "Vencord Contributors",
|
||||
"description": "Typescript definitions for the webpack modules of the Discord Web app",
|
||||
"version": "1.0.0",
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"types": "src/index.d.ts",
|
||||
"type": "module",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/Vendicated/Vencord.git",
|
||||
"directory": "packages/discord-types"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/react": "^19.0.10",
|
||||
"moment": "^2.22.2",
|
||||
"type-fest": "^4.41.0"
|
||||
}
|
||||
}
|
||||
16
packages/discord-types/src/classes.d.ts
vendored
16
packages/discord-types/src/classes.d.ts
vendored
|
|
@ -1,16 +0,0 @@
|
|||
export interface ButtonWrapperClasses {
|
||||
hoverScale: string;
|
||||
buttonWrapper: string;
|
||||
button: string;
|
||||
iconMask: string;
|
||||
buttonContent: string;
|
||||
icon: string;
|
||||
pulseIcon: string;
|
||||
pulseButton: string;
|
||||
notificationDot: string;
|
||||
sparkleContainer: string;
|
||||
sparkleStar: string;
|
||||
sparklePlus: string;
|
||||
sparkle: string;
|
||||
active: string;
|
||||
}
|
||||
36
packages/discord-types/src/common/Activity.d.ts
vendored
36
packages/discord-types/src/common/Activity.d.ts
vendored
|
|
@ -1,36 +0,0 @@
|
|||
import { ActivityFlags, ActivityStatusDisplayType, ActivityType } from "../../enums";
|
||||
|
||||
export interface ActivityAssets {
|
||||
large_image?: string;
|
||||
large_text?: string;
|
||||
small_image?: string;
|
||||
small_text?: string;
|
||||
}
|
||||
|
||||
export interface ActivityButton {
|
||||
label: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
export interface Activity {
|
||||
name: string;
|
||||
application_id: string;
|
||||
type: ActivityType;
|
||||
state?: string;
|
||||
state_url?: string;
|
||||
details?: string;
|
||||
details_url?: string;
|
||||
url?: string;
|
||||
flags: ActivityFlags;
|
||||
status_display_type?: ActivityStatusDisplayType;
|
||||
timestamps?: {
|
||||
start?: number;
|
||||
end?: number;
|
||||
};
|
||||
assets?: ActivityAssets;
|
||||
buttons?: string[];
|
||||
metadata?: {
|
||||
button_urls?: Array<string>;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
import { User } from "./User";
|
||||
|
||||
export interface Application {
|
||||
id: string;
|
||||
name: string;
|
||||
description?: string | null;
|
||||
type: number | null;
|
||||
icon: string | null | undefined;
|
||||
is_discoverable: boolean;
|
||||
is_monetized: boolean;
|
||||
is_verified: boolean;
|
||||
bot?: User;
|
||||
deeplink_uri?: string;
|
||||
flags?: number;
|
||||
privacy_policy_url?: string;
|
||||
terms_of_service_url?: string;
|
||||
install_params?: ApplicationInstallParams;
|
||||
}
|
||||
|
||||
export interface ApplicationInstallParams {
|
||||
permissions: string | null;
|
||||
scopes: string[];
|
||||
}
|
||||
83
packages/discord-types/src/common/Channel.d.ts
vendored
83
packages/discord-types/src/common/Channel.d.ts
vendored
|
|
@ -1,83 +0,0 @@
|
|||
import { DiscordRecord } from "./Record";
|
||||
|
||||
export class Channel extends DiscordRecord {
|
||||
constructor(channel: object);
|
||||
application_id: number | undefined;
|
||||
bitrate: number;
|
||||
defaultAutoArchiveDuration: number | undefined;
|
||||
flags: number;
|
||||
guild_id: string;
|
||||
icon: string;
|
||||
id: string;
|
||||
lastMessageId: string;
|
||||
lastPinTimestamp: string | undefined;
|
||||
member: unknown;
|
||||
memberCount: number | undefined;
|
||||
memberIdsPreview: string[] | undefined;
|
||||
memberListId: unknown;
|
||||
messageCount: number | undefined;
|
||||
name: string;
|
||||
nicks: Record<string, unknown>;
|
||||
nsfw: boolean;
|
||||
originChannelId: unknown;
|
||||
ownerId: string;
|
||||
parent_id: string;
|
||||
permissionOverwrites: {
|
||||
[role: string]: {
|
||||
id: string;
|
||||
type: number;
|
||||
deny: bigint;
|
||||
allow: bigint;
|
||||
};
|
||||
};
|
||||
position: number;
|
||||
rateLimitPerUser: number;
|
||||
rawRecipients: {
|
||||
id: string;
|
||||
avatar: string;
|
||||
username: string;
|
||||
public_flags: number;
|
||||
discriminator: string;
|
||||
}[];
|
||||
recipients: string[];
|
||||
rtcRegion: string;
|
||||
threadMetadata: {
|
||||
locked: boolean;
|
||||
archived: boolean;
|
||||
invitable: boolean;
|
||||
createTimestamp: string | undefined;
|
||||
autoArchiveDuration: number;
|
||||
archiveTimestamp: string | undefined;
|
||||
};
|
||||
topic: string;
|
||||
type: number;
|
||||
userLimit: number;
|
||||
videoQualityMode: undefined;
|
||||
|
||||
get accessPermissions(): bigint;
|
||||
get lastActiveTimestamp(): number;
|
||||
|
||||
computeLurkerPermissionsAllowList(): unknown;
|
||||
getApplicationId(): unknown;
|
||||
getGuildId(): string;
|
||||
getRecipientId(): unknown;
|
||||
hasFlag(flag: number): boolean;
|
||||
isActiveThread(): boolean;
|
||||
isArchivedThread(): boolean;
|
||||
isCategory(): boolean;
|
||||
isDM(): boolean;
|
||||
isDirectory(): boolean;
|
||||
isForumChannel(): boolean;
|
||||
isGroupDM(): boolean;
|
||||
isGuildStageVoice(): boolean;
|
||||
isGuildVoice(): boolean;
|
||||
isListenModeCapable(): boolean;
|
||||
isManaged(): boolean;
|
||||
isMultiUserDM(): boolean;
|
||||
isNSFW(): boolean;
|
||||
isOwner(): boolean;
|
||||
isPrivate(): boolean;
|
||||
isSystemDM(): boolean;
|
||||
isThread(): boolean;
|
||||
isVocal(): boolean;
|
||||
}
|
||||
64
packages/discord-types/src/common/Guild.d.ts
vendored
64
packages/discord-types/src/common/Guild.d.ts
vendored
|
|
@ -1,64 +0,0 @@
|
|||
import { Role } from './Role';
|
||||
import { DiscordRecord } from './Record';
|
||||
|
||||
// copy(Object.keys(findByProps("CREATOR_MONETIZABLE")).map(JSON.stringify).join("|"))
|
||||
export type GuildFeatures =
|
||||
"INVITE_SPLASH" | "VIP_REGIONS" | "VANITY_URL" | "MORE_EMOJI" | "MORE_STICKERS" | "MORE_SOUNDBOARD" | "VERIFIED" | "COMMERCE" | "DISCOVERABLE" | "COMMUNITY" | "FEATURABLE" | "NEWS" | "HUB" | "PARTNERED" | "ANIMATED_ICON" | "BANNER" | "ENABLED_DISCOVERABLE_BEFORE" | "WELCOME_SCREEN_ENABLED" | "MEMBER_VERIFICATION_GATE_ENABLED" | "PREVIEW_ENABLED" | "ROLE_SUBSCRIPTIONS_ENABLED" | "ROLE_SUBSCRIPTIONS_AVAILABLE_FOR_PURCHASE" | "CREATOR_MONETIZABLE" | "CREATOR_MONETIZABLE_PROVISIONAL" | "CREATOR_MONETIZABLE_WHITEGLOVE" | "CREATOR_MONETIZABLE_DISABLED" | "CREATOR_MONETIZABLE_RESTRICTED" | "CREATOR_STORE_PAGE" | "CREATOR_MONETIZABLE_PENDING_NEW_OWNER_ONBOARDING" | "PRODUCTS_AVAILABLE_FOR_PURCHASE" | "GUILD_WEB_PAGE_VANITY_URL" | "THREADS_ENABLED" | "THREADS_ENABLED_TESTING" | "NEW_THREAD_PERMISSIONS" | "ROLE_ICONS" | "TEXT_IN_STAGE_ENABLED" | "TEXT_IN_VOICE_ENABLED" | "HAS_DIRECTORY_ENTRY" | "ANIMATED_BANNER" | "LINKED_TO_HUB" | "EXPOSED_TO_ACTIVITIES_WTP_EXPERIMENT" | "GUILD_HOME_DEPRECATION_OVERRIDE" | "GUILD_HOME_TEST" | "GUILD_HOME_OVERRIDE" | "GUILD_ONBOARDING" | "GUILD_ONBOARDING_EVER_ENABLED" | "GUILD_ONBOARDING_HAS_PROMPTS" | "GUILD_SERVER_GUIDE" | "INTERNAL_EMPLOYEE_ONLY" | "AUTO_MODERATION" | "INVITES_DISABLED" | "BURST_REACTIONS" | "SOUNDBOARD" | "SHARD" | "ACTIVITY_FEED_ENABLED_BY_USER" | "ACTIVITY_FEED_DISABLED_BY_USER" | "SUMMARIES_ENABLED_GA" | "LEADERBOARD_ENABLED" | "SUMMARIES_ENABLED_BY_USER" | "SUMMARIES_OPT_OUT_EXPERIENCE" | "CHANNEL_ICON_EMOJIS_GENERATED" | "NON_COMMUNITY_RAID_ALERTS" | "RAID_ALERTS_DISABLED" | "AUTOMOD_TRIGGER_USER_PROFILE" | "ENABLED_MODERATION_EXPERIENCE_FOR_NON_COMMUNITY" | "GUILD_PRODUCTS_ALLOW_ARCHIVED_FILE" | "CLAN" | "MEMBER_VERIFICATION_MANUAL_APPROVAL" | "FORWARDING_DISABLED" | "MEMBER_VERIFICATION_ROLLOUT_TEST" | "AUDIO_BITRATE_128_KBPS" | "AUDIO_BITRATE_256_KBPS" | "AUDIO_BITRATE_384_KBPS" | "VIDEO_BITRATE_ENHANCED" | "MAX_FILE_SIZE_50_MB" | "MAX_FILE_SIZE_100_MB" | "GUILD_TAGS" | "ENHANCED_ROLE_COLORS" | "PREMIUM_TIER_3_OVERRIDE" | "REPORT_TO_MOD_PILOT" | "TIERLESS_BOOSTING_SYSTEM_MESSAGE";
|
||||
export type GuildPremiumFeatures =
|
||||
"ANIMATED_ICON" | "STAGE_CHANNEL_VIEWERS_150" | "ROLE_ICONS" | "GUILD_TAGS" | "BANNER" | "MAX_FILE_SIZE_50_MB" | "VIDEO_QUALITY_720_60FPS" | "STAGE_CHANNEL_VIEWERS_50" | "VIDEO_QUALITY_1080_60FPS" | "MAX_FILE_SIZE_100_MB" | "VANITY_URL" | "VIDEO_BITRATE_ENHANCED" | "STAGE_CHANNEL_VIEWERS_300" | "AUDIO_BITRATE_128_KBPS" | "ANIMATED_BANNER" | "TIERLESS_BOOSTING" | "ENHANCED_ROLE_COLORS" | "INVITE_SPLASH" | "AUDIO_BITRATE_256_KBPS" | "AUDIO_BITRATE_384_KBPS";
|
||||
|
||||
export class Guild extends DiscordRecord {
|
||||
constructor(guild: object);
|
||||
afkChannelId: string | undefined;
|
||||
afkTimeout: number;
|
||||
applicationCommandCounts: {
|
||||
0: number;
|
||||
1: number;
|
||||
2: number;
|
||||
};
|
||||
application_id: unknown;
|
||||
banner: string | undefined;
|
||||
defaultMessageNotifications: number;
|
||||
description: string | undefined;
|
||||
discoverySplash: string | undefined;
|
||||
explicitContentFilter: number;
|
||||
features: Set<GuildFeatures>;
|
||||
homeHeader: string | undefined;
|
||||
hubType: unknown;
|
||||
icon: string | undefined;
|
||||
id: string;
|
||||
joinedAt: Date;
|
||||
latestOnboardingQuestionId: string | undefined;
|
||||
maxMembers: number;
|
||||
maxStageVideoChannelUsers: number;
|
||||
maxVideoChannelUsers: number;
|
||||
mfaLevel: number;
|
||||
moderatorReporting: unknown;
|
||||
name: string;
|
||||
nsfwLevel: number;
|
||||
ownerConfiguredContentLevel: number;
|
||||
ownerId: string;
|
||||
preferredLocale: string;
|
||||
premiumFeatures: {
|
||||
additionalEmojiSlots: number;
|
||||
additionalSoundSlots: number;
|
||||
additionalStickerSlots: number;
|
||||
features: Array<GuildPremiumFeatures>;
|
||||
};
|
||||
premiumProgressBarEnabled: boolean;
|
||||
premiumSubscriberCount: number;
|
||||
premiumTier: number;
|
||||
profile: {
|
||||
badge: string | undefined;
|
||||
tag: string | undefined;
|
||||
} | undefined;
|
||||
publicUpdatesChannelId: string | undefined;
|
||||
roles: Record<string, Role>;
|
||||
rulesChannelId: string | undefined;
|
||||
safetyAlertsChannelId: string | undefined;
|
||||
splash: string | undefined;
|
||||
systemChannelFlags: number;
|
||||
systemChannelId: string | undefined;
|
||||
vanityURLCode: string | undefined;
|
||||
verificationLevel: number;
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
export interface GuildMember {
|
||||
avatar: string | undefined;
|
||||
avatarDecoration: string | undefined;
|
||||
banner: string | undefined;
|
||||
bio: string;
|
||||
colorRoleId: string | undefined;
|
||||
colorString: string;
|
||||
colorStrings: {
|
||||
primaryColor: string | undefined;
|
||||
secondaryColor: string | undefined;
|
||||
tertiaryColor: string | undefined;
|
||||
};
|
||||
communicationDisabledUntil: string | undefined;
|
||||
flags: number;
|
||||
fullProfileLoadedTimestamp: number;
|
||||
guildId: string;
|
||||
highestRoleId: string;
|
||||
hoistRoleId: string;
|
||||
iconRoleId: string;
|
||||
isPending: boolean | undefined;
|
||||
joinedAt: string | undefined;
|
||||
nick: string | undefined;
|
||||
premiumSince: string | undefined;
|
||||
roles: string[];
|
||||
userId: string;
|
||||
}
|
||||
12
packages/discord-types/src/common/Record.d.ts
vendored
12
packages/discord-types/src/common/Record.d.ts
vendored
|
|
@ -1,12 +0,0 @@
|
|||
type Updater = (value: any) => any;
|
||||
|
||||
/**
|
||||
* Common Record class extended by various Discord data structures, like User, Channel, Guild, etc.
|
||||
*/
|
||||
export class DiscordRecord {
|
||||
toJS(): Record<string, any>;
|
||||
|
||||
set(key: string, value: any): this;
|
||||
merge(data: Record<string, any>): this;
|
||||
update(key: string, defaultValueOrUpdater: Updater | any, updater?: Updater): this;
|
||||
}
|
||||
33
packages/discord-types/src/common/Role.d.ts
vendored
33
packages/discord-types/src/common/Role.d.ts
vendored
|
|
@ -1,33 +0,0 @@
|
|||
export interface Role {
|
||||
color: number;
|
||||
colorString: string | undefined;
|
||||
colorStrings: {
|
||||
primaryColor: string | undefined;
|
||||
secondaryColor: string | undefined;
|
||||
tertiaryColor: string | undefined;
|
||||
};
|
||||
colors: {
|
||||
primary_color: number | undefined;
|
||||
secondary_color: number | undefined;
|
||||
tertiary_color: number | undefined;
|
||||
};
|
||||
flags: number;
|
||||
hoist: boolean;
|
||||
icon: string | undefined;
|
||||
id: string;
|
||||
managed: boolean;
|
||||
mentionable: boolean;
|
||||
name: string;
|
||||
originalPosition: number;
|
||||
permissions: bigint;
|
||||
position: number;
|
||||
/**
|
||||
* probably incomplete
|
||||
*/
|
||||
tags: {
|
||||
bot_id: string;
|
||||
integration_id: string;
|
||||
premium_subscriber: unknown;
|
||||
} | undefined;
|
||||
unicodeEmoji: string | undefined;
|
||||
}
|
||||
65
packages/discord-types/src/common/User.d.ts
vendored
65
packages/discord-types/src/common/User.d.ts
vendored
|
|
@ -1,65 +0,0 @@
|
|||
// TODO: a lot of optional params can also be null, not just undef
|
||||
|
||||
import { DiscordRecord } from "./Record";
|
||||
|
||||
export class User extends DiscordRecord {
|
||||
constructor(user: object);
|
||||
accentColor: number;
|
||||
avatar: string;
|
||||
banner: string | null | undefined;
|
||||
bio: string;
|
||||
bot: boolean;
|
||||
desktop: boolean;
|
||||
discriminator: string;
|
||||
email: string | undefined;
|
||||
flags: number;
|
||||
globalName: string | undefined;
|
||||
guildMemberAvatars: Record<string, string>;
|
||||
id: string;
|
||||
mfaEnabled: boolean;
|
||||
mobile: boolean;
|
||||
nsfwAllowed: boolean | undefined;
|
||||
phone: string | undefined;
|
||||
premiumType: number | undefined;
|
||||
premiumUsageFlags: number;
|
||||
publicFlags: number;
|
||||
purchasedFlags: number;
|
||||
system: boolean;
|
||||
username: string;
|
||||
verified: boolean;
|
||||
|
||||
get createdAt(): Date;
|
||||
get hasPremiumPerks(): boolean;
|
||||
get tag(): string;
|
||||
get usernameNormalized(): string;
|
||||
|
||||
addGuildAvatarHash(guildId: string, avatarHash: string): User;
|
||||
getAvatarSource(guildId: string, canAnimate?: boolean): { uri: string; };
|
||||
getAvatarURL(guildId?: string | null, t?: unknown, canAnimate?: boolean): string;
|
||||
hasAvatarForGuild(guildId: string): boolean;
|
||||
hasDisabledPremium(): boolean;
|
||||
hasFlag(flag: number): boolean;
|
||||
hasFreePremium(): boolean;
|
||||
hasHadSKU(e: unknown): boolean;
|
||||
hasPremiumUsageFlag(flag: number): boolean;
|
||||
hasPurchasedFlag(flag: number): boolean;
|
||||
hasUrgentMessages(): boolean;
|
||||
isClaimed(): boolean;
|
||||
isLocalBot(): boolean;
|
||||
isNonUserBot(): boolean;
|
||||
isPhoneVerified(): boolean;
|
||||
isStaff(): boolean;
|
||||
isSystemUser(): boolean;
|
||||
isVerifiedBot(): boolean;
|
||||
removeGuildAvatarHash(guildId: string): User;
|
||||
toString(): string;
|
||||
}
|
||||
|
||||
export interface UserJSON {
|
||||
avatar: string;
|
||||
avatarDecoration: unknown | undefined;
|
||||
discriminator: string;
|
||||
id: string;
|
||||
publicFlags: number;
|
||||
username: string;
|
||||
}
|
||||
9
packages/discord-types/src/common/index.d.ts
vendored
9
packages/discord-types/src/common/index.d.ts
vendored
|
|
@ -1,9 +0,0 @@
|
|||
export * from "./Activity";
|
||||
export * from "./Application";
|
||||
export * from "./Channel";
|
||||
export * from "./Guild";
|
||||
export * from "./GuildMember";
|
||||
export * from "./messages";
|
||||
export * from "./Role";
|
||||
export * from "./User";
|
||||
export * from "./Record";
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
import { Channel } from "../Channel";
|
||||
import { Guild } from "../Guild";
|
||||
import { Promisable } from "type-fest";
|
||||
import { ApplicationCommandInputType, ApplicationCommandOptionType, ApplicationCommandType } from "../../../enums";
|
||||
|
||||
export interface CommandContext {
|
||||
channel: Channel;
|
||||
guild?: Guild;
|
||||
}
|
||||
|
||||
export interface CommandOption {
|
||||
name: string;
|
||||
displayName?: string;
|
||||
type: ApplicationCommandOptionType;
|
||||
description: string;
|
||||
displayDescription?: string;
|
||||
required?: boolean;
|
||||
options?: CommandOption[];
|
||||
choices?: Array<ChoicesOption>;
|
||||
}
|
||||
|
||||
export interface ChoicesOption {
|
||||
label: string;
|
||||
value: string;
|
||||
name: string;
|
||||
displayName?: string;
|
||||
}
|
||||
|
||||
export interface CommandReturnValue {
|
||||
content: string;
|
||||
// TODO: implement
|
||||
// cancel?: boolean;
|
||||
}
|
||||
|
||||
export interface CommandArgument {
|
||||
type: ApplicationCommandOptionType;
|
||||
name: string;
|
||||
value: string;
|
||||
focused: undefined;
|
||||
options: CommandArgument[];
|
||||
}
|
||||
|
||||
export interface Command {
|
||||
id?: string;
|
||||
applicationId?: string;
|
||||
type?: ApplicationCommandType;
|
||||
inputType?: ApplicationCommandInputType;
|
||||
plugin?: string;
|
||||
|
||||
name: string;
|
||||
untranslatedName?: string;
|
||||
displayName?: string;
|
||||
description: string;
|
||||
untranslatedDescription?: string;
|
||||
displayDescription?: string;
|
||||
|
||||
options?: CommandOption[];
|
||||
predicate?(ctx: CommandContext): boolean;
|
||||
|
||||
execute(args: CommandArgument[], ctx: CommandContext): Promisable<void | CommandReturnValue>;
|
||||
}
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
export interface Embed {
|
||||
author?: {
|
||||
name: string;
|
||||
url: string;
|
||||
iconURL: string | undefined;
|
||||
iconProxyURL: string | undefined;
|
||||
};
|
||||
color: string;
|
||||
fields: [];
|
||||
id: string;
|
||||
image?: {
|
||||
height: number;
|
||||
width: number;
|
||||
url: string;
|
||||
proxyURL: string;
|
||||
};
|
||||
provider?: {
|
||||
name: string;
|
||||
url: string | undefined;
|
||||
};
|
||||
rawDescription: string;
|
||||
rawTitle: string;
|
||||
referenceId: unknown;
|
||||
timestamp: string;
|
||||
thumbnail?: {
|
||||
height: number;
|
||||
proxyURL: string | undefined;
|
||||
url: string;
|
||||
width: number;
|
||||
};
|
||||
type: string;
|
||||
url: string | undefined;
|
||||
video?: {
|
||||
height: number;
|
||||
width: number;
|
||||
url: string;
|
||||
proxyURL: string | undefined;
|
||||
};
|
||||
}
|
||||
|
||||
export interface EmbedJSON {
|
||||
author?: {
|
||||
name: string;
|
||||
url: string;
|
||||
icon_url: string;
|
||||
proxy_icon_url: string;
|
||||
};
|
||||
title: string;
|
||||
color: string;
|
||||
description: string;
|
||||
type: string;
|
||||
url: string | undefined;
|
||||
provider?: {
|
||||
name: string;
|
||||
url: string;
|
||||
};
|
||||
timestamp: string;
|
||||
thumbnail?: {
|
||||
height: number;
|
||||
width: number;
|
||||
url: string;
|
||||
proxy_url: string | undefined;
|
||||
};
|
||||
video?: {
|
||||
height: number;
|
||||
width: number;
|
||||
url: string;
|
||||
proxy_url: string | undefined;
|
||||
};
|
||||
}
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
export type Emoji = CustomEmoji | UnicodeEmoji;
|
||||
|
||||
export interface CustomEmoji {
|
||||
type: 1;
|
||||
allNamesString: string;
|
||||
animated: boolean;
|
||||
available: boolean;
|
||||
guildId: string;
|
||||
id: string;
|
||||
managed: boolean;
|
||||
name: string;
|
||||
originalName?: string;
|
||||
require_colons: boolean;
|
||||
roles: string[];
|
||||
}
|
||||
|
||||
export interface UnicodeEmoji {
|
||||
type: 0;
|
||||
diversityChildren: Record<any, any>;
|
||||
emojiObject: {
|
||||
names: string[];
|
||||
surrogates: string;
|
||||
unicodeVersion: number;
|
||||
};
|
||||
index: number;
|
||||
surrogates: string;
|
||||
uniqueName: string;
|
||||
useSpriteSheet: boolean;
|
||||
get allNamesString(): string;
|
||||
get animated(): boolean;
|
||||
get defaultDiversityChild(): any;
|
||||
get hasDiversity(): boolean | undefined;
|
||||
get hasDiversityParent(): boolean | undefined;
|
||||
get hasMultiDiversity(): boolean | undefined;
|
||||
get hasMultiDiversityParent(): boolean | undefined;
|
||||
get managed(): boolean;
|
||||
get name(): string;
|
||||
get names(): string[];
|
||||
get optionallyDiverseSequence(): string | undefined;
|
||||
get unicodeVersion(): number;
|
||||
get url(): string;
|
||||
}
|
||||
|
|
@ -1,204 +0,0 @@
|
|||
import { CommandOption } from './Commands';
|
||||
import { User, UserJSON } from '../User';
|
||||
import { Embed, EmbedJSON } from './Embed';
|
||||
import { DiscordRecord } from "../Record";
|
||||
import { MessageFlags, MessageType, StickerFormatType } from "../../../enums";
|
||||
|
||||
/*
|
||||
* TODO: looks like discord has moved over to Date instead of Moment;
|
||||
*/
|
||||
export class Message extends DiscordRecord {
|
||||
constructor(message: object);
|
||||
activity: unknown;
|
||||
application: unknown;
|
||||
applicationId: string | unknown;
|
||||
attachments: MessageAttachment[];
|
||||
author: User;
|
||||
blocked: boolean;
|
||||
bot: boolean;
|
||||
call: {
|
||||
duration: moment.Duration;
|
||||
endedTimestamp: moment.Moment;
|
||||
participants: string[];
|
||||
};
|
||||
channel_id: string;
|
||||
/**
|
||||
* NOTE: not fully typed
|
||||
*/
|
||||
codedLinks: {
|
||||
code?: string;
|
||||
type: string;
|
||||
}[];
|
||||
colorString: unknown;
|
||||
components: unknown[];
|
||||
content: string;
|
||||
customRenderedContent: unknown;
|
||||
editedTimestamp: Date;
|
||||
embeds: Embed[];
|
||||
flags: MessageFlags;
|
||||
giftCodes: string[];
|
||||
id: string;
|
||||
interaction: {
|
||||
id: string;
|
||||
name: string;
|
||||
type: number;
|
||||
user: User;
|
||||
}[] | undefined;
|
||||
interactionData: {
|
||||
application_command: {
|
||||
application_id: string;
|
||||
default_member_permissions: unknown;
|
||||
default_permission: boolean;
|
||||
description: string;
|
||||
dm_permission: unknown;
|
||||
id: string;
|
||||
name: string;
|
||||
options: CommandOption[];
|
||||
permissions: unknown[];
|
||||
type: number;
|
||||
version: string;
|
||||
};
|
||||
attachments: MessageAttachment[];
|
||||
guild_id: string | undefined;
|
||||
id: string;
|
||||
name: string;
|
||||
options: {
|
||||
focused: unknown;
|
||||
name: string;
|
||||
type: number;
|
||||
value: string;
|
||||
}[];
|
||||
type: number;
|
||||
version: string;
|
||||
}[];
|
||||
interactionError: unknown[];
|
||||
isSearchHit: boolean;
|
||||
loggingName: unknown;
|
||||
mentionChannels: string[];
|
||||
mentionEveryone: boolean;
|
||||
mentionRoles: string[];
|
||||
mentioned: boolean;
|
||||
mentions: string[];
|
||||
messageReference: {
|
||||
guild_id?: string;
|
||||
channel_id: string;
|
||||
message_id: string;
|
||||
} | undefined;
|
||||
messageSnapshots: {
|
||||
message: Message;
|
||||
}[];
|
||||
nick: unknown; // probably a string
|
||||
nonce: string | undefined;
|
||||
pinned: boolean;
|
||||
reactions: MessageReaction[];
|
||||
state: string;
|
||||
stickerItems: {
|
||||
format_type: StickerFormatType;
|
||||
id: string;
|
||||
name: string;
|
||||
}[];
|
||||
stickers: unknown[];
|
||||
timestamp: moment.Moment;
|
||||
tts: boolean;
|
||||
type: MessageType;
|
||||
webhookId: string | undefined;
|
||||
|
||||
/**
|
||||
* Doesn't actually update the original message; it just returns a new message instance with the added reaction.
|
||||
*/
|
||||
addReaction(emoji: ReactionEmoji, fromCurrentUser: boolean): Message;
|
||||
/**
|
||||
* Searches each reaction and if the provided string has an index above -1 it'll return the reaction object.
|
||||
*/
|
||||
getReaction(name: string): MessageReaction;
|
||||
/**
|
||||
* Doesn't actually update the original message; it just returns the message instance without the reaction searched with the provided emoji object.
|
||||
*/
|
||||
removeReactionsForEmoji(emoji: ReactionEmoji): Message;
|
||||
/**
|
||||
* Doesn't actually update the original message; it just returns the message instance without the reaction.
|
||||
*/
|
||||
removeReaction(emoji: ReactionEmoji, fromCurrentUser: boolean): Message;
|
||||
|
||||
getChannelId(): string;
|
||||
hasFlag(flag: MessageFlags): boolean;
|
||||
isCommandType(): boolean;
|
||||
isEdited(): boolean;
|
||||
isSystemDM(): boolean;
|
||||
|
||||
/** Vencord added */
|
||||
deleted?: boolean;
|
||||
}
|
||||
|
||||
/** A smaller Message object found in FluxDispatcher and elsewhere. */
|
||||
export interface MessageJSON {
|
||||
attachments: MessageAttachment[];
|
||||
author: UserJSON;
|
||||
channel_id: string;
|
||||
components: unknown[];
|
||||
content: string;
|
||||
edited_timestamp: string;
|
||||
embeds: EmbedJSON[];
|
||||
flags: number;
|
||||
guild_id: string | undefined;
|
||||
id: string;
|
||||
loggingName: unknown;
|
||||
member: {
|
||||
avatar: string | undefined;
|
||||
communication_disabled_until: string | undefined;
|
||||
deaf: boolean;
|
||||
hoisted_role: string | undefined;
|
||||
is_pending: boolean;
|
||||
joined_at: string;
|
||||
mute: boolean;
|
||||
nick: string | boolean;
|
||||
pending: boolean;
|
||||
premium_since: string | undefined;
|
||||
roles: string[];
|
||||
} | undefined;
|
||||
mention_everyone: boolean;
|
||||
mention_roles: string[];
|
||||
mentions: UserJSON[];
|
||||
message_reference: {
|
||||
guild_id?: string;
|
||||
channel_id: string;
|
||||
message_id: string;
|
||||
} | undefined;
|
||||
nonce: string | undefined;
|
||||
pinned: boolean;
|
||||
referenced_message: MessageJSON | undefined;
|
||||
state: string;
|
||||
timestamp: string;
|
||||
tts: boolean;
|
||||
type: number;
|
||||
}
|
||||
|
||||
export interface MessageAttachment {
|
||||
filename: string;
|
||||
id: string;
|
||||
proxy_url: string;
|
||||
size: number;
|
||||
spoiler: boolean;
|
||||
url: string;
|
||||
content_type?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
}
|
||||
|
||||
export interface ReactionEmoji {
|
||||
id: string | undefined;
|
||||
name: string;
|
||||
animated: boolean;
|
||||
}
|
||||
|
||||
export interface MessageReaction {
|
||||
count: number;
|
||||
emoji: ReactionEmoji;
|
||||
me: boolean;
|
||||
}
|
||||
|
||||
// Object.keys(findByProps("REPLYABLE")).map(JSON.stringify).join("|")
|
||||
export type MessageTypeSets = Record<
|
||||
"UNDELETABLE" | "GUILD_DISCOVERY_STATUS" | "USER_MESSAGE" | "NOTIFIABLE_SYSTEM_MESSAGE" | "REPLYABLE" | "FORWARDABLE" | "REFERENCED_MESSAGE_AVAILABLE" | "AVAILABLE_IN_GUILD_FEED" | "DEADCHAT_PROMPTS" | "NON_COLLAPSIBLE" | "NON_PARSED" | "AUTOMOD_INCIDENT_ACTIONS" | "SELF_MENTIONABLE_SYSTEM" | "SCHEDULABLE",
|
||||
Set<MessageType>
|
||||
>;
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
import { StickerFormatType, StickerType } from "../../../enums";
|
||||
|
||||
interface BaseSticker {
|
||||
asset: string;
|
||||
available: boolean;
|
||||
description: string;
|
||||
format_type: StickerFormatType;
|
||||
id: string;
|
||||
name: string;
|
||||
sort_value?: number;
|
||||
/** a comma separated string */
|
||||
tags: string;
|
||||
}
|
||||
|
||||
export interface PackSticker extends BaseSticker {
|
||||
pack_id: string;
|
||||
type: StickerType.STANDARD;
|
||||
}
|
||||
|
||||
export interface GuildSticker extends BaseSticker {
|
||||
guild_id: string;
|
||||
type: StickerType.GUILD;
|
||||
}
|
||||
|
||||
export type Sticker = PackSticker | GuildSticker;
|
||||
|
||||
export interface PremiumStickerPack {
|
||||
banner_asset_id?: string;
|
||||
cover_sticker_id?: string;
|
||||
description: string;
|
||||
id: string;
|
||||
name: string;
|
||||
sku_id: string;
|
||||
stickers: PackSticker[];
|
||||
}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
export * from "./Commands";
|
||||
export * from "./Message";
|
||||
export * from "./Embed";
|
||||
export * from "./Emoji";
|
||||
export * from "./Sticker";
|
||||
30
packages/discord-types/src/flux.d.ts
vendored
30
packages/discord-types/src/flux.d.ts
vendored
|
|
@ -1,30 +0,0 @@
|
|||
import { FluxStore } from "./stores/FluxStore";
|
||||
|
||||
export class FluxEmitter {
|
||||
constructor();
|
||||
|
||||
changeSentinel: number;
|
||||
changedStores: Set<FluxStore>;
|
||||
isBatchEmitting: boolean;
|
||||
isDispatching: boolean;
|
||||
isPaused: boolean;
|
||||
pauseTimer: NodeJS.Timeout | null;
|
||||
reactChangedStores: Set<FluxStore>;
|
||||
|
||||
batched(batch: (...args: any[]) => void): void;
|
||||
destroy(): void;
|
||||
emit(): void;
|
||||
emitNonReactOnce(): void;
|
||||
emitReactOnce(): void;
|
||||
getChangeSentinel(): number;
|
||||
getIsPaused(): boolean;
|
||||
injectBatchEmitChanges(batch: (...args: any[]) => void): void;
|
||||
markChanged(store: FluxStore): void;
|
||||
pause(): void;
|
||||
resume(): void;
|
||||
}
|
||||
|
||||
export interface Flux {
|
||||
Store: typeof FluxStore;
|
||||
Emitter: FluxEmitter;
|
||||
}
|
||||
22
packages/discord-types/src/fluxEvents.d.ts
vendored
22
packages/discord-types/src/fluxEvents.d.ts
vendored
File diff suppressed because one or more lines are too long
10
packages/discord-types/src/index.d.ts
vendored
10
packages/discord-types/src/index.d.ts
vendored
|
|
@ -1,10 +0,0 @@
|
|||
export * from "./common";
|
||||
export * from "./classes";
|
||||
export * from "./components";
|
||||
export * from "./flux";
|
||||
export * from "./fluxEvents";
|
||||
export * from "./menu";
|
||||
export * from "./modules";
|
||||
export * from "./stores";
|
||||
export * from "./utils";
|
||||
export * as Webpack from "../webpack";
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
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 +0,0 @@
|
|||
export * from "./CloudUpload";
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
import { FluxStore } from "..";
|
||||
|
||||
export class AuthenticationStore extends FluxStore {
|
||||
/**
|
||||
* Gets the id of the current user
|
||||
*/
|
||||
getId(): string;
|
||||
|
||||
// This Store has a lot more methods related to everything Auth, but they really should
|
||||
// not be needed, so they are not typed
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
import { Channel, FluxStore } from "..";
|
||||
|
||||
export class ChannelStore extends FluxStore {
|
||||
getChannel(channelId: string): Channel;
|
||||
getBasicChannel(channelId: string): Channel | undefined;
|
||||
hasChannel(channelId: string): boolean;
|
||||
|
||||
getChannelIds(guildId?: string | null): string[];
|
||||
getMutableBasicGuildChannelsForGuild(guildId: string): Record<string, Channel>;
|
||||
getMutableGuildChannelsForGuild(guildId: string): Record<string, Channel>;
|
||||
getAllThreadsForGuild(guildId: string): Channel[];
|
||||
getAllThreadsForParent(channelId: string): Channel[];
|
||||
|
||||
getDMFromUserId(userId: string): string;
|
||||
getDMChannelFromUserId(userId: string): Channel | undefined;
|
||||
getDMUserIds(): string[];
|
||||
getMutableDMsByUserIds(): Record<string, string>;
|
||||
getMutablePrivateChannels(): Record<string, Channel>;
|
||||
getSortedPrivateChannels(): Channel[];
|
||||
|
||||
getGuildChannelsVersion(guildId: string): number;
|
||||
getPrivateChannelsVersion(): number;
|
||||
getInitialOverlayState(): Record<string, Channel>;
|
||||
}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
import { FluxStore } from "..";
|
||||
|
||||
export enum DraftType {
|
||||
ChannelMessage = 0,
|
||||
ThreadSettings = 1,
|
||||
FirstThreadMessage = 2,
|
||||
ApplicationLauncherCommand = 3,
|
||||
Poll = 4,
|
||||
SlashCommand = 5,
|
||||
ForwardContextMessage = 6
|
||||
}
|
||||
|
||||
export interface Draft {
|
||||
timestamp: number;
|
||||
draft: string;
|
||||
}
|
||||
|
||||
export interface ThreadSettingsDraft {
|
||||
timestamp: number;
|
||||
parentMessageId?: string;
|
||||
name?: string;
|
||||
isPrivate?: boolean;
|
||||
parentChannelId?: string;
|
||||
location?: string;
|
||||
}
|
||||
|
||||
export type ChannelDrafts = {
|
||||
[DraftType.ThreadSettings]: ThreadSettingsDraft;
|
||||
} & {
|
||||
[key in Exclude<DraftType, DraftType.ThreadSettings>]: Draft;
|
||||
};
|
||||
|
||||
export type UserDrafts = Partial<Record<string, ChannelDrafts>>;
|
||||
export type DraftState = Partial<Record<string, UserDrafts>>;
|
||||
|
||||
export class DraftStore extends FluxStore {
|
||||
getState(): DraftState;
|
||||
getRecentlyEditedDrafts(type: DraftType): Array<Draft & { channelId: string; }>;
|
||||
getDraft(channelId: string, type: DraftType): string;
|
||||
|
||||
getThreadSettings(channelId: string): ThreadSettingsDraft | null | undefined;
|
||||
getThreadDraftWithParentMessageId(parentMessageId: string): ThreadSettingsDraft | null | undefined;
|
||||
}
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
import { Channel, CustomEmoji, Emoji, FluxStore } from "..";
|
||||
|
||||
export class EmojiStore extends FluxStore {
|
||||
getCustomEmojiById(id?: string | null): CustomEmoji | undefined;
|
||||
getUsableCustomEmojiById(id?: string | null): CustomEmoji | undefined;
|
||||
getGuilds(): Record<string, {
|
||||
id: string;
|
||||
get emojis(): CustomEmoji[];
|
||||
get rawEmojis(): CustomEmoji[];
|
||||
get usableEmojis(): CustomEmoji[];
|
||||
get emoticons(): any[];
|
||||
getEmoji(id: string): CustomEmoji | undefined;
|
||||
isUsable(emoji: CustomEmoji): boolean;
|
||||
}>;
|
||||
getGuildEmoji(guildId?: string | null): CustomEmoji[];
|
||||
getNewlyAddedEmoji(guildId?: string | null): CustomEmoji[];
|
||||
getTopEmoji(guildId?: string | null): CustomEmoji[];
|
||||
getTopEmojisMetadata(guildId?: string | null): {
|
||||
emojiIds: string[];
|
||||
topEmojisTTL: number;
|
||||
};
|
||||
hasPendingUsage(): boolean;
|
||||
hasUsableEmojiInAnyGuild(): boolean;
|
||||
searchWithoutFetchingLatest(data: any): any;
|
||||
getSearchResultsOrder(...args: any[]): any;
|
||||
getState(): {
|
||||
pendingUsages: { key: string, timestamp: number; }[];
|
||||
};
|
||||
searchWithoutFetchingLatest(data: {
|
||||
channel: Channel;
|
||||
query: string;
|
||||
count?: number;
|
||||
intention: number;
|
||||
includeExternalGuilds?: boolean;
|
||||
matchComparator?(name: string): boolean;
|
||||
}): Record<"locked" | "unlocked", Emoji[]>;
|
||||
|
||||
getDisambiguatedEmojiContext(): {
|
||||
backfillTopEmojis: Record<any, any>;
|
||||
customEmojis: Record<string, CustomEmoji>;
|
||||
emojisById: Record<string, CustomEmoji>;
|
||||
emojisByName: Record<string, CustomEmoji>;
|
||||
emoticonRegex: RegExp | null;
|
||||
emoticonsByName: Record<string, any>;
|
||||
escapedEmoticonNames: string;
|
||||
favoriteNamesAndIds?: any;
|
||||
favorites?: any;
|
||||
frequentlyUsed?: any;
|
||||
groupedCustomEmojis: Record<string, CustomEmoji[]>;
|
||||
guildId?: string;
|
||||
isFavoriteEmojiWithoutFetchingLatest(e: Emoji): boolean;
|
||||
newlyAddedEmoji: Record<string, CustomEmoji[]>;
|
||||
topEmojis?: any;
|
||||
unicodeAliases: Record<string, string>;
|
||||
get favoriteEmojisWithoutFetchingLatest(): Emoji[];
|
||||
};
|
||||
}
|
||||
44
packages/discord-types/src/stores/FluxStore.d.ts
vendored
44
packages/discord-types/src/stores/FluxStore.d.ts
vendored
|
|
@ -1,44 +0,0 @@
|
|||
import { FluxDispatcher, FluxEvents } from "..";
|
||||
|
||||
type Callback = () => void;
|
||||
|
||||
/*
|
||||
For some reason, this causes type errors when you try to destructure it:
|
||||
```ts
|
||||
interface FluxEvent {
|
||||
type: FluxEvents;
|
||||
[key: string]: any;
|
||||
}
|
||||
```
|
||||
*/
|
||||
export type FluxEvent = any;
|
||||
|
||||
export type ActionHandler = (event: FluxEvent) => void;
|
||||
export type ActionHandlers = Partial<Record<FluxEvents, ActionHandler>>;
|
||||
|
||||
export class FluxStore {
|
||||
constructor(dispatcher: FluxDispatcher, actionHandlers?: ActionHandlers);
|
||||
|
||||
getName(): string;
|
||||
|
||||
addChangeListener(callback: Callback): void;
|
||||
/** Listener will be removed once the callback returns false. */
|
||||
addConditionalChangeListener(callback: () => boolean, preemptive?: boolean): void;
|
||||
addReactChangeListener(callback: Callback): void;
|
||||
removeChangeListener(callback: Callback): void;
|
||||
removeReactChangeListener(callback: Callback): void;
|
||||
|
||||
doEmitChanges(event: FluxEvent): void;
|
||||
emitChange(): void;
|
||||
|
||||
getDispatchToken(): string;
|
||||
initialize(): void;
|
||||
initializeIfNeeded(): void;
|
||||
/** this is a setter */
|
||||
mustEmitChanges(actionHandler: ActionHandler | undefined): void;
|
||||
registerActionHandlers(actionHandlers: ActionHandlers): void;
|
||||
syncWith(stores: FluxStore[], callback: Callback, timeout?: number): void;
|
||||
waitFor(...stores: FluxStore[]): void;
|
||||
|
||||
static getAll(): FluxStore[];
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
import { FluxStore, GuildMember } from "..";
|
||||
|
||||
export class GuildMemberStore extends FluxStore {
|
||||
/** @returns Format: [guildId-userId: Timestamp (string)] */
|
||||
getCommunicationDisabledUserMap(): Record<string, string>;
|
||||
getCommunicationDisabledVersion(): number;
|
||||
|
||||
getMutableAllGuildsAndMembers(): Record<string, Record<string, GuildMember>>;
|
||||
|
||||
getMember(guildId: string, userId: string): GuildMember | null;
|
||||
getTrueMember(guildId: string, userId: string): GuildMember | null;
|
||||
getMemberIds(guildId: string): string[];
|
||||
getMembers(guildId: string): GuildMember[];
|
||||
|
||||
getCachedSelfMember(guildId: string): GuildMember | null;
|
||||
getSelfMember(guildId: string): GuildMember | null;
|
||||
getSelfMemberJoinedAt(guildId: string): Date | null;
|
||||
|
||||
getNick(guildId: string, userId: string): string | null;
|
||||
getNicknameGuildsMapping(userId: string): Record<string, string[]>;
|
||||
getNicknames(userId: string): string[];
|
||||
|
||||
isMember(guildId: string, userId: string): boolean;
|
||||
isMember(guildId: string, userId: string): boolean;
|
||||
isGuestOrLurker(guildId: string, userId: string): boolean;
|
||||
isCurrentUserGuest(guildId: string): boolean;
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
import { FluxStore, Role } from "..";
|
||||
|
||||
// TODO: add the rest of the methods for GuildRoleStore
|
||||
export class GuildRoleStore extends FluxStore {
|
||||
getRole(guildId: string, roleId: string): Role;
|
||||
getSortedRoles(guildId: string): Role[];
|
||||
getRolesSnapshot(guildId: string): Record<string, Role>;
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
import { Guild, FluxStore } from "..";
|
||||
|
||||
export class GuildStore extends FluxStore {
|
||||
getGuild(guildId: string): Guild;
|
||||
getGuildCount(): number;
|
||||
getGuilds(): Record<string, Guild>;
|
||||
getGuildIds(): string[];
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
import { MessageJSON, FluxStore, Message } from "..";
|
||||
|
||||
export class MessageStore extends FluxStore {
|
||||
getMessage(channelId: string, messageId: string): Message;
|
||||
/** @returns This return object is fucking huge; I'll type it later. */
|
||||
getMessages(channelId: string): unknown;
|
||||
getRawMessages(channelId: string): Record<string | number, MessageJSON>;
|
||||
hasCurrentUserSentMessage(channelId: string): boolean;
|
||||
hasPresent(channelId: string): boolean;
|
||||
isLoadingMessages(channelId: string): boolean;
|
||||
jumpedMessageId(channelId: string): string | undefined;
|
||||
whenReady(channelId: string, callback: () => void): void;
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
import { FluxStore } from "..";
|
||||
|
||||
export class RelationshipStore extends FluxStore {
|
||||
getFriendIDs(): string[];
|
||||
getIgnoredIDs(): string[];
|
||||
getBlockedIDs(): string[];
|
||||
|
||||
getPendingCount(): number;
|
||||
getRelationshipCount(): number;
|
||||
|
||||
/** Related to friend nicknames. */
|
||||
getNickname(userId: string): string;
|
||||
/** @returns Enum value from constants.RelationshipTypes */
|
||||
getRelationshipType(userId: string): number;
|
||||
isFriend(userId: string): boolean;
|
||||
isBlocked(userId: string): boolean;
|
||||
isIgnored(userId: string): boolean;
|
||||
/**
|
||||
* @see {@link isBlocked}
|
||||
* @see {@link isIgnored}
|
||||
*/
|
||||
isBlockedOrIgnored(userId: string): boolean;
|
||||
getSince(userId: string): string;
|
||||
|
||||
getMutableRelationships(): Map<string, number>;
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
import { FluxStore } from "..";
|
||||
|
||||
export class SelectedChannelStore extends FluxStore {
|
||||
getChannelId(guildId?: string | null): string;
|
||||
getVoiceChannelId(): string | undefined;
|
||||
getCurrentlySelectedChannelId(guildId?: string): string | undefined;
|
||||
getMostRecentSelectedTextChannelId(guildId: string): string | undefined;
|
||||
getLastSelectedChannelId(guildId?: string): string;
|
||||
// yes this returns a string
|
||||
getLastSelectedChannels(guildId?: string): string;
|
||||
|
||||
/** If you follow an announcement channel, this will return whichever channel you chose as destination */
|
||||
getLastChannelFollowingDestination(): { guildId?: string; channelId?: string; } | undefined;
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
import { FluxStore } from "..";
|
||||
|
||||
export interface SelectedGuildState {
|
||||
selectedGuildTimestampMillis: Record<string | number, number>;
|
||||
selectedGuildId: string | null;
|
||||
lastSelectedGuildId: string | null;
|
||||
}
|
||||
|
||||
export class SelectedGuildStore extends FluxStore {
|
||||
getGuildId(): string | null;
|
||||
getLastSelectedGuildId(): string | null;
|
||||
getLastSelectedTimestamp(guildId: string): number | null;
|
||||
getState(): SelectedGuildState | undefined;
|
||||
}
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
import { FluxStore, GuildSticker, PremiumStickerPack, Sticker } from "..";
|
||||
|
||||
export type StickerGuildMap = Map<string, GuildSticker[]>;
|
||||
|
||||
export class StickersStore extends FluxStore {
|
||||
getAllGuildStickers(): StickerGuildMap;
|
||||
getRawStickersByGuild(): StickerGuildMap;
|
||||
getPremiumPacks(): PremiumStickerPack[];
|
||||
|
||||
getStickerById(id: string): Sticker | undefined;
|
||||
getStickerPack(id: string): PremiumStickerPack | undefined;
|
||||
getStickersByGuildId(guildId: string): Sticker[] | undefined;
|
||||
|
||||
isPremiumPack(id: string): boolean;
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
import { FluxStore } from "@vencord/discord-types";
|
||||
|
||||
export class StreamerModeStore extends FluxStore {
|
||||
get autoToggle(): boolean;
|
||||
get disableNotifications(): boolean;
|
||||
get disableSounds(): boolean;
|
||||
get enableContentProtection(): boolean;
|
||||
get enabled(): boolean;
|
||||
get hideInstantInvites(): boolean;
|
||||
get hidePersonalInformation(): boolean;
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
import { FluxStore } from "..";
|
||||
|
||||
export type ThemePreference = "dark" | "light" | "unknown";
|
||||
export type SystemTheme = "dark" | "light";
|
||||
export type Theme = "light" | "dark" | "darker" | "midnight";
|
||||
|
||||
export interface ThemeState {
|
||||
theme: Theme;
|
||||
status: 0 | 1;
|
||||
preferences: Record<ThemePreference, Theme>;
|
||||
}
|
||||
export class ThemeStore extends FluxStore {
|
||||
get theme(): Theme;
|
||||
get darkSidebar(): boolean;
|
||||
get systemTheme(): SystemTheme;
|
||||
themePreferenceForSystemTheme(preference: ThemePreference): Theme;
|
||||
getState(): ThemeState;
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
import { FluxStore } from "..";
|
||||
|
||||
export class TypingStore extends FluxStore {
|
||||
/**
|
||||
* returns a map of user ids to timeout ids
|
||||
*/
|
||||
getTypingUsers(channelId: string): Record<string, number>;
|
||||
isTyping(channelId: string, userId: string): boolean;
|
||||
}
|
||||
|
|
@ -1,151 +0,0 @@
|
|||
import { FluxStore, Guild, User, Application, ApplicationInstallParams } from "..";
|
||||
import { ApplicationIntegrationType } from "../../enums";
|
||||
|
||||
export interface MutualFriend {
|
||||
/**
|
||||
* the userid of the mutual friend
|
||||
*/
|
||||
key: string;
|
||||
/**
|
||||
* the status of the mutual friend
|
||||
*/
|
||||
status: "online" | "offline" | "idle" | "dnd";
|
||||
/**
|
||||
* the user object of the mutual friend
|
||||
*/
|
||||
user: User;
|
||||
}
|
||||
|
||||
export interface MutualGuild {
|
||||
/**
|
||||
* the guild object of the mutual guild
|
||||
*/
|
||||
guild: Guild;
|
||||
/**
|
||||
* the user's nickname in the guild, if any
|
||||
*/
|
||||
nick: string | null;
|
||||
|
||||
}
|
||||
|
||||
export interface ProfileBadge {
|
||||
id: string;
|
||||
description: string;
|
||||
icon: string;
|
||||
link?: string;
|
||||
}
|
||||
|
||||
export interface ConnectedAccount {
|
||||
type: "twitch" | "youtube" | "skype" | "steam" | "leagueoflegends" | "battlenet" | "bluesky" | "bungie" | "reddit" | "twitter" | "twitter_legacy" | "spotify" | "facebook" | "xbox" | "samsung" | "contacts" | "instagram" | "mastodon" | "soundcloud" | "github" | "playstation" | "playstation-stg" | "epicgames" | "riotgames" | "roblox" | "paypal" | "ebay" | "tiktok" | "crunchyroll" | "domain" | "amazon-music";
|
||||
/**
|
||||
* underlying id of connected account
|
||||
* eg. account uuid
|
||||
*/
|
||||
id: string;
|
||||
/**
|
||||
* display name of connected account
|
||||
*/
|
||||
name: string;
|
||||
verified: boolean;
|
||||
metadata?: Record<string, string>;
|
||||
}
|
||||
|
||||
export interface ProfileApplication {
|
||||
id: string;
|
||||
customInstallUrl: string | undefined;
|
||||
installParams: ApplicationInstallParams | undefined;
|
||||
flags: number;
|
||||
popularApplicationCommandIds?: string[];
|
||||
integrationTypesConfig: Record<ApplicationIntegrationType, Partial<{
|
||||
oauth2_install_params: ApplicationInstallParams;
|
||||
}>>;
|
||||
primarySkuId: string | undefined;
|
||||
storefront_available: boolean;
|
||||
}
|
||||
|
||||
export interface UserProfileBase extends Pick<User, "banner"> {
|
||||
accentColor: number | null;
|
||||
/**
|
||||
* often empty for guild profiles, get the user profile for badges
|
||||
*/
|
||||
badges: ProfileBadge[];
|
||||
bio: string | undefined;
|
||||
popoutAnimationParticleType: string | null;
|
||||
profileEffectExpiresAt: number | Date | undefined;
|
||||
profileEffectId: undefined | string;
|
||||
/**
|
||||
* often an empty string when not set
|
||||
*/
|
||||
pronouns: string | "" | undefined;
|
||||
themeColors: [number, number] | undefined;
|
||||
userId: string;
|
||||
}
|
||||
|
||||
export interface ApplicationRoleConnection {
|
||||
application: Application;
|
||||
application_metadata: Record<string, any>;
|
||||
metadata: Record<string, any>;
|
||||
platform_name: string;
|
||||
platform_username: string;
|
||||
}
|
||||
|
||||
export interface UserProfile extends UserProfileBase, Pick<User, "premiumType"> {
|
||||
/** If this is a bot user profile, this will be its application */
|
||||
application: ProfileApplication | null;
|
||||
applicationRoleConnections: ApplicationRoleConnection[] | undefined;
|
||||
connectedAccounts: ConnectedAccount[] | undefined;
|
||||
fetchStartedAt: number;
|
||||
fetchEndedAt: number;
|
||||
legacyUsername: string | undefined;
|
||||
premiumGuildSince: Date | null;
|
||||
premiumSince: Date | null;
|
||||
}
|
||||
|
||||
export class UserProfileStore extends FluxStore {
|
||||
/**
|
||||
* @param userId the user ID of the profile being fetched.
|
||||
* @param guildId the guild ID to of the profile being fetched.
|
||||
* defaults to the internal symbol `NO GUILD ID` if nullish
|
||||
*
|
||||
* @returns true if the profile is being fetched, false otherwise.
|
||||
*/
|
||||
isFetchingProfile(userId: string, guildId?: string): boolean;
|
||||
/**
|
||||
* Check if mutual friends for {@link userId} are currently being fetched.
|
||||
*
|
||||
* @param userId the user ID of the mutual friends being fetched.
|
||||
*
|
||||
* @returns true if mutual friends are being fetched, false otherwise.
|
||||
*/
|
||||
isFetchingFriends(userId: string): boolean;
|
||||
|
||||
get isSubmitting(): boolean;
|
||||
|
||||
getUserProfile(userId: string): UserProfile | undefined;
|
||||
|
||||
getGuildMemberProfile(userId: string, guildId: string | undefined): UserProfileBase | null;
|
||||
/**
|
||||
* Get the mutual friends of a user.
|
||||
*
|
||||
* @param userId the user ID of the user to get the mutual friends of.
|
||||
*
|
||||
* @returns an array of mutual friends, or undefined if the user has no mutual friends
|
||||
*/
|
||||
getMutualFriends(userId: string): MutualFriend[] | undefined;
|
||||
/**
|
||||
* Get the count of mutual friends for a user.
|
||||
*
|
||||
* @param userId the user ID of the user to get the mutual friends count of.
|
||||
*
|
||||
* @returns the count of mutual friends, or undefined if the user has no mutual friends
|
||||
*/
|
||||
getMutualFriendsCount(userId: string): number | undefined;
|
||||
/**
|
||||
* Get the mutual guilds of a user.
|
||||
*
|
||||
* @param userId the user ID of the user to get the mutual guilds of.
|
||||
*
|
||||
* @returns an array of mutual guilds, or undefined if the user has no mutual guilds
|
||||
*/
|
||||
getMutualGuilds(userId: string): MutualGuild[] | undefined;
|
||||
}
|
||||
10
packages/discord-types/src/stores/UserStore.d.ts
vendored
10
packages/discord-types/src/stores/UserStore.d.ts
vendored
|
|
@ -1,10 +0,0 @@
|
|||
import { FluxStore, User } from "..";
|
||||
|
||||
export class UserStore extends FluxStore {
|
||||
filter(filter: (user: User) => boolean, sort?: boolean): Record<string, User>;
|
||||
findByTag(username: string, discriminator: string): User;
|
||||
forEach(action: (user: User) => void): void;
|
||||
getCurrentUser(): User;
|
||||
getUser(userId: string): User;
|
||||
getUsers(): Record<string, User>;
|
||||
}
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
import { DiscordRecord } from "../common";
|
||||
import { FluxStore } from "./FluxStore";
|
||||
|
||||
export type UserVoiceStateRecords = Record<string, VoiceState>;
|
||||
export type VoiceStates = Record<string, UserVoiceStateRecords>;
|
||||
|
||||
export interface VoiceState extends DiscordRecord {
|
||||
userId: string;
|
||||
channelId: string | null | undefined;
|
||||
sessionId: string | null | undefined;
|
||||
mute: boolean;
|
||||
deaf: boolean;
|
||||
selfMute: boolean;
|
||||
selfDeaf: boolean;
|
||||
selfVideo: boolean;
|
||||
selfStream: boolean | undefined;
|
||||
suppress: boolean;
|
||||
requestToSpeakTimestamp: string | null | undefined;
|
||||
discoverable: boolean;
|
||||
|
||||
isVoiceMuted(): boolean;
|
||||
isVoiceDeafened(): boolean;
|
||||
}
|
||||
|
||||
export class VoiceStateStore extends FluxStore {
|
||||
getAllVoiceStates(): VoiceStates;
|
||||
|
||||
getVoiceStates(guildId?: string | null): UserVoiceStateRecords;
|
||||
getVoiceStatesForChannel(channelId: string): UserVoiceStateRecords;
|
||||
getVideoVoiceStatesForChannel(channelId: string): UserVoiceStateRecords;
|
||||
|
||||
getVoiceState(guildId: string | null, userId: string): VoiceState | undefined;
|
||||
getUserVoiceChannelId(guildId: string | null, userId: string): string | undefined;
|
||||
getVoiceStateForChannel(channelId: string, userId?: string): VoiceState | undefined;
|
||||
getVoiceStateForUser(userId: string): VoiceState | undefined;
|
||||
|
||||
getCurrentClientVoiceChannelId(guildId: string | null): string | undefined;
|
||||
isCurrentClientInVoiceChannel(): boolean;
|
||||
|
||||
isInChannel(channelId: string, userId?: string): boolean;
|
||||
hasVideo(channelId: string): boolean;
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
import { FluxStore } from "..";
|
||||
|
||||
export class WindowStore extends FluxStore {
|
||||
isElementFullScreen(): boolean;
|
||||
isFocused(): boolean;
|
||||
windowSize(): Record<"width" | "height", number>;
|
||||
}
|
||||
38
packages/discord-types/src/stores/index.d.ts
vendored
38
packages/discord-types/src/stores/index.d.ts
vendored
|
|
@ -1,38 +0,0 @@
|
|||
// please keep in alphabetical order
|
||||
export * from "./AuthenticationStore";
|
||||
export * from "./ChannelStore";
|
||||
export * from "./DraftStore";
|
||||
export * from "./EmojiStore";
|
||||
export * from "./FluxStore";
|
||||
export * from "./GuildMemberStore";
|
||||
export * from "./GuildRoleStore";
|
||||
export * from "./GuildStore";
|
||||
export * from "./MessageStore";
|
||||
export * from "./RelationshipStore";
|
||||
export * from "./SelectedChannelStore";
|
||||
export * from "./SelectedGuildStore";
|
||||
export * from "./StickersStore";
|
||||
export * from "./StreamerModeStore";
|
||||
export * from "./ThemeStore";
|
||||
export * from "./TypingStore";
|
||||
export * from "./UserProfileStore";
|
||||
export * from "./UserStore";
|
||||
export * from "./VoiceStateStore";
|
||||
export * from "./WindowStore";
|
||||
|
||||
/**
|
||||
* React hook that returns stateful data for one or more stores
|
||||
* You might need a custom comparator (4th argument) if your store data is an object
|
||||
* @param stores The stores to listen to
|
||||
* @param mapper A function that returns the data you need
|
||||
* @param dependencies An array of reactive values which the hook depends on. Use this if your mapper or equality function depends on the value of another hook
|
||||
* @param isEqual A custom comparator for the data returned by mapper
|
||||
*
|
||||
* @example const user = useStateFromStores([UserStore], () => UserStore.getCurrentUser(), null, (old, current) => old.id === current.id);
|
||||
*/
|
||||
export type useStateFromStores = <T>(
|
||||
stores: any[],
|
||||
mapper: () => T,
|
||||
dependencies?: any,
|
||||
isEqual?: (old: T, newer: T) => boolean
|
||||
) => T;
|
||||
|
|
@ -21,6 +21,7 @@
|
|||
"@types/node": "^22.13.4",
|
||||
"@types/react": "18.3.1",
|
||||
"@types/react-dom": "18.3.1",
|
||||
"discord-types": "^1.3.26",
|
||||
"standalone-electron-types": "^34.2.0",
|
||||
"type-fest": "^4.35.0"
|
||||
}
|
||||
|
|
|
|||
43
pnpm-lock.yaml
generated
43
pnpm-lock.yaml
generated
|
|
@ -65,12 +65,12 @@ importers:
|
|||
'@types/yazl':
|
||||
specifier: ^2.4.5
|
||||
version: 2.4.6
|
||||
'@vencord/discord-types':
|
||||
specifier: link:packages/discord-types
|
||||
version: link:packages/discord-types
|
||||
diff:
|
||||
specifier: ^7.0.0
|
||||
version: 7.0.0
|
||||
discord-types:
|
||||
specifier: ^1.3.26
|
||||
version: 1.3.26
|
||||
esbuild:
|
||||
specifier: ^0.25.1
|
||||
version: 0.25.1
|
||||
|
|
@ -141,18 +141,6 @@ importers:
|
|||
specifier: ^0.3.5
|
||||
version: 0.3.5
|
||||
|
||||
packages/discord-types:
|
||||
dependencies:
|
||||
'@types/react':
|
||||
specifier: ^19.0.10
|
||||
version: 19.0.12
|
||||
moment:
|
||||
specifier: ^2.22.2
|
||||
version: 2.30.1
|
||||
type-fest:
|
||||
specifier: ^4.41.0
|
||||
version: 4.41.0
|
||||
|
||||
packages/vencord-types:
|
||||
dependencies:
|
||||
'@types/lodash':
|
||||
|
|
@ -167,6 +155,9 @@ importers:
|
|||
'@types/react-dom':
|
||||
specifier: 18.3.1
|
||||
version: 18.3.1
|
||||
discord-types:
|
||||
specifier: ^1.3.26
|
||||
version: 1.3.26
|
||||
standalone-electron-types:
|
||||
specifier: ^34.2.0
|
||||
version: 34.2.0
|
||||
|
|
@ -531,6 +522,9 @@ packages:
|
|||
peerDependencies:
|
||||
'@types/react': ^19.0.0
|
||||
|
||||
'@types/react@17.0.2':
|
||||
resolution: {integrity: sha512-Xt40xQsrkdvjn1EyWe1Bc0dJLcil/9x2vAuW7ya+PuQip4UYUaXyhzWmAbwRsdMgwOFHpfp7/FFZebDU6Y8VHA==}
|
||||
|
||||
'@types/react@18.3.1':
|
||||
resolution: {integrity: sha512-V0kuGBX3+prX+DQ/7r2qsv1NsdfnCLnTgnRJ1pYnxykBhGMz+qj+box5lq7XsO5mtZsBqpjwwTu/7wszPfMBcw==}
|
||||
|
||||
|
|
@ -962,6 +956,9 @@ packages:
|
|||
resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
discord-types@1.3.26:
|
||||
resolution: {integrity: sha512-ToG51AOCH+JTQf7b+8vuYQe5Iqwz7nZ7StpECAZ/VZcI1ZhQk13pvt9KkRTfRv1xNvwJ2qib4e3+RifQlo8VPQ==}
|
||||
|
||||
doctrine@2.1.0:
|
||||
resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
|
@ -2331,10 +2328,6 @@ packages:
|
|||
resolution: {integrity: sha512-2dBz5D5ycHIoliLYLi0Q2V7KRaDlH0uWIvmk7TYlAg5slqwiPv1ezJdZm1QEM0xgk29oYWMCbIG7E6gHpvChlg==}
|
||||
engines: {node: '>=16'}
|
||||
|
||||
type-fest@4.41.0:
|
||||
resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==}
|
||||
engines: {node: '>=16'}
|
||||
|
||||
typed-array-buffer@1.0.3:
|
||||
resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
|
@ -2771,6 +2764,11 @@ snapshots:
|
|||
dependencies:
|
||||
'@types/react': 19.0.12
|
||||
|
||||
'@types/react@17.0.2':
|
||||
dependencies:
|
||||
'@types/prop-types': 15.7.14
|
||||
csstype: 3.1.3
|
||||
|
||||
'@types/react@18.3.1':
|
||||
dependencies:
|
||||
'@types/prop-types': 15.7.14
|
||||
|
|
@ -3257,6 +3255,11 @@ snapshots:
|
|||
dependencies:
|
||||
path-type: 4.0.0
|
||||
|
||||
discord-types@1.3.26:
|
||||
dependencies:
|
||||
'@types/react': 17.0.2
|
||||
moment: 2.30.1
|
||||
|
||||
doctrine@2.1.0:
|
||||
dependencies:
|
||||
esutils: 2.0.3
|
||||
|
|
@ -4920,8 +4923,6 @@ snapshots:
|
|||
|
||||
type-fest@4.38.0: {}
|
||||
|
||||
type-fest@4.41.0: {}
|
||||
|
||||
typed-array-buffer@1.0.3:
|
||||
dependencies:
|
||||
call-bound: 1.0.4
|
||||
|
|
|
|||
|
|
@ -31,7 +31,6 @@ const defines = stringifyValues({
|
|||
IS_UPDATER_DISABLED,
|
||||
IS_WEB: false,
|
||||
IS_EXTENSION: false,
|
||||
IS_USERSCRIPT: false,
|
||||
VERSION,
|
||||
BUILD_TIMESTAMP
|
||||
});
|
||||
|
|
@ -51,7 +50,7 @@ const nodeCommonOpts = {
|
|||
format: "cjs",
|
||||
platform: "node",
|
||||
target: ["esnext"],
|
||||
// @ts-expect-error this is never undefined
|
||||
// @ts-ignore this is never undefined
|
||||
external: ["electron", "original-fs", "~pluginNatives", ...commonOpts.external]
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -43,7 +43,6 @@ const commonOptions = {
|
|||
define: stringifyValues({
|
||||
IS_WEB: true,
|
||||
IS_EXTENSION: false,
|
||||
IS_USERSCRIPT: false,
|
||||
IS_STANDALONE: true,
|
||||
IS_DEV,
|
||||
IS_REPORTER,
|
||||
|
|
@ -99,7 +98,6 @@ const buildConfigs = [
|
|||
inject: ["browser/GMPolyfill.js", ...(commonOptions?.inject || [])],
|
||||
define: {
|
||||
...commonOptions.define,
|
||||
IS_USERSCRIPT: "true",
|
||||
window: "unsafeWindow",
|
||||
},
|
||||
outfile: "dist/Vencord.user.js",
|
||||
|
|
|
|||
|
|
@ -363,6 +363,6 @@ export const commonRendererPlugins = [
|
|||
banImportPlugin(/^react$/, "Cannot import from react. React and hooks should be imported from @webpack/common"),
|
||||
banImportPlugin(/^electron(\/.*)?$/, "Cannot import electron in browser code. You need to use a native.ts file"),
|
||||
banImportPlugin(/^ts-pattern$/, "Cannot import from ts-pattern. match and P should be imported from @webpack/common"),
|
||||
// @ts-expect-error this is never undefined
|
||||
// @ts-ignore this is never undefined
|
||||
...commonOpts.plugins
|
||||
];
|
||||
|
|
|
|||
|
|
@ -16,9 +16,6 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// DO NOT REMOVE UNLESS YOU WISH TO FACE THE WRATH OF THE CIRCULAR DEPENDENCY DEMON!!!!!!!
|
||||
import "~plugins";
|
||||
|
||||
export * as Api from "./api";
|
||||
export * as Components from "./components";
|
||||
export * as Plugins from "./plugins";
|
||||
|
|
@ -32,8 +29,7 @@ export { PlainSettings, Settings };
|
|||
import "./utils/quickCss";
|
||||
import "./webpack/patchWebpack";
|
||||
|
||||
import { openUpdaterModal } from "@components/settings/tabs/updater";
|
||||
import { IS_WINDOWS } from "@utils/constants";
|
||||
import { openUpdaterModal } from "@components/VencordSettings/UpdaterTab";
|
||||
import { StartAt } from "@utils/types";
|
||||
|
||||
import { get as dsGet } from "./api/DataStore";
|
||||
|
|
@ -138,11 +134,7 @@ async function init() {
|
|||
|
||||
if (!IS_WEB && !IS_UPDATER_DISABLED) {
|
||||
runUpdateCheck();
|
||||
|
||||
// this tends to get really annoying, so only do this if the user has auto-update without notification enabled
|
||||
if (Settings.autoUpdate && !Settings.autoUpdateNotification) {
|
||||
setInterval(runUpdateCheck, 1000 * 60 * 30); // 30 minutes
|
||||
}
|
||||
setInterval(runUpdateCheck, 1000 * 60 * 30); // 30 minutes
|
||||
}
|
||||
|
||||
if (IS_DEV) {
|
||||
|
|
@ -165,7 +157,7 @@ init();
|
|||
document.addEventListener("DOMContentLoaded", () => {
|
||||
startAllPlugins(StartAt.DOMContentLoaded);
|
||||
|
||||
if (IS_DISCORD_DESKTOP && Settings.winNativeTitleBar && IS_WINDOWS) {
|
||||
if (IS_DISCORD_DESKTOP && Settings.winNativeTitleBar && navigator.platform.toLowerCase().startsWith("win")) {
|
||||
document.head.append(Object.assign(document.createElement("style"), {
|
||||
id: "vencord-native-titlebar-style",
|
||||
textContent: "[class*=titleBar]{display: none!important}"
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
*/
|
||||
|
||||
import type { Settings } from "@api/Settings";
|
||||
import { CspRequestResult } from "@main/csp/manager";
|
||||
import { PluginIpcMappings } from "@main/ipcPlugins";
|
||||
import type { UserThemeHeader } from "@main/themes";
|
||||
import { IpcEvents } from "@shared/IpcEvents";
|
||||
|
|
@ -34,11 +33,10 @@ export default {
|
|||
themes: {
|
||||
uploadTheme: (fileName: string, fileData: string) => invoke<void>(IpcEvents.UPLOAD_THEME, fileName, fileData),
|
||||
deleteTheme: (fileName: string) => invoke<void>(IpcEvents.DELETE_THEME, fileName),
|
||||
getThemesDir: () => invoke<string>(IpcEvents.GET_THEMES_DIR),
|
||||
getThemesList: () => invoke<Array<UserThemeHeader>>(IpcEvents.GET_THEMES_LIST),
|
||||
getThemeData: (fileName: string) => invoke<string | undefined>(IpcEvents.GET_THEME_DATA, fileName),
|
||||
getSystemValues: () => invoke<Record<string, string>>(IpcEvents.GET_THEME_SYSTEM_VALUES),
|
||||
|
||||
openFolder: () => invoke<void>(IpcEvents.OPEN_THEMES_FOLDER),
|
||||
},
|
||||
|
||||
updater: {
|
||||
|
|
@ -51,8 +49,7 @@ export default {
|
|||
settings: {
|
||||
get: () => sendSync<Settings>(IpcEvents.GET_SETTINGS),
|
||||
set: (settings: Settings, pathToNotify?: string) => invoke<void>(IpcEvents.SET_SETTINGS, settings, pathToNotify),
|
||||
|
||||
openFolder: () => invoke<void>(IpcEvents.OPEN_SETTINGS_FOLDER),
|
||||
getSettingsDir: () => invoke<string>(IpcEvents.GET_SETTINGS_DIR),
|
||||
},
|
||||
|
||||
quickCss: {
|
||||
|
|
@ -76,17 +73,5 @@ export default {
|
|||
openExternal: (url: string) => invoke<void>(IpcEvents.OPEN_EXTERNAL, url)
|
||||
},
|
||||
|
||||
csp: {
|
||||
/**
|
||||
* Note: Only supports full explicit matches, not wildcards.
|
||||
*
|
||||
* If `*.example.com` is allowed, `isDomainAllowed("https://sub.example.com")` will return false.
|
||||
*/
|
||||
isDomainAllowed: (url: string, directives: string[]) => invoke<boolean>(IpcEvents.CSP_IS_DOMAIN_ALLOWED, url, directives),
|
||||
removeOverride: (url: string) => invoke<boolean>(IpcEvents.CSP_REMOVE_OVERRIDE, url),
|
||||
requestAddOverride: (url: string, directives: string[], callerName: string) =>
|
||||
invoke<CspRequestResult>(IpcEvents.CSP_REQUEST_ADD_OVERRIDE, url, directives, callerName),
|
||||
},
|
||||
|
||||
pluginHelpers: PluginHelpers
|
||||
};
|
||||
|
|
|
|||
|
|
@ -17,9 +17,10 @@
|
|||
*/
|
||||
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import BadgeAPIPlugin from "plugins/_api/badges";
|
||||
import { ComponentType, HTMLProps } from "react";
|
||||
|
||||
import Plugins from "~plugins";
|
||||
|
||||
export const enum BadgePosition {
|
||||
START,
|
||||
END
|
||||
|
|
@ -34,9 +35,7 @@ export interface ProfileBadge {
|
|||
image?: string;
|
||||
link?: string;
|
||||
/** Action to perform when you click the badge */
|
||||
onClick?(event: React.MouseEvent, props: ProfileBadge & BadgeUserArgs): void;
|
||||
/** Action to perform when you right click the badge */
|
||||
onContextMenu?(event: React.MouseEvent, props: BadgeUserArgs & BadgeUserArgs): void;
|
||||
onClick?(event: React.MouseEvent<HTMLButtonElement, MouseEvent>, props: BadgeUserArgs): void;
|
||||
/** Should the user display this badge? */
|
||||
shouldShow?(userInfo: BadgeUserArgs): boolean;
|
||||
/** Optional props (e.g. style) for the badge, ignored for component badges */
|
||||
|
|
@ -78,34 +77,21 @@ export function removeProfileBadge(badge: ProfileBadge) {
|
|||
export function _getBadges(args: BadgeUserArgs) {
|
||||
const badges = [] as ProfileBadge[];
|
||||
for (const badge of Badges) {
|
||||
if (badge.shouldShow && !badge.shouldShow(args)) {
|
||||
continue;
|
||||
}
|
||||
if (!badge.shouldShow || badge.shouldShow(args)) {
|
||||
const b = badge.getBadges
|
||||
? badge.getBadges(args).map(b => {
|
||||
b.component &&= ErrorBoundary.wrap(b.component, { noop: true });
|
||||
return b;
|
||||
})
|
||||
: [{ ...badge, ...args }];
|
||||
|
||||
const b = badge.getBadges
|
||||
? badge.getBadges(args).map(badge => ({
|
||||
...args,
|
||||
...badge,
|
||||
component: badge.component && ErrorBoundary.wrap(badge.component, { noop: true })
|
||||
}))
|
||||
: [{ ...args, ...badge }];
|
||||
|
||||
if (badge.position === BadgePosition.START) {
|
||||
badges.unshift(...b);
|
||||
} else {
|
||||
badges.push(...b);
|
||||
badge.position === BadgePosition.START
|
||||
? badges.unshift(...b)
|
||||
: badges.push(...b);
|
||||
}
|
||||
}
|
||||
|
||||
const donorBadges = BadgeAPIPlugin.getDonorBadges(args.userId);
|
||||
if (donorBadges) {
|
||||
badges.unshift(
|
||||
...donorBadges.map(badge => ({
|
||||
...args,
|
||||
...badge,
|
||||
}))
|
||||
);
|
||||
}
|
||||
const donorBadges = (Plugins.BadgeAPI as unknown as typeof import("../plugins/_api/badges").default).getDonorBadges(args.userId);
|
||||
if (donorBadges) badges.unshift(...donorBadges);
|
||||
|
||||
return badges;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@ import "./ChatButton.css";
|
|||
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Logger } from "@utils/Logger";
|
||||
import { Channel } from "@vencord/discord-types";
|
||||
import { waitFor } from "@webpack";
|
||||
import { Button, ButtonWrapperClasses, Tooltip } from "@webpack/common";
|
||||
import { Channel } from "discord-types/general";
|
||||
import { HTMLProps, JSX, MouseEventHandler, ReactNode } from "react";
|
||||
|
||||
let ChannelTextAreaClasses: Record<"button" | "buttonContainer", string>;
|
||||
|
|
|
|||
|
|
@ -17,11 +17,13 @@
|
|||
*/
|
||||
|
||||
import { mergeDefaults } from "@utils/mergeDefaults";
|
||||
import { CommandArgument, Message } from "@vencord/discord-types";
|
||||
import { findByCodeLazy } from "@webpack";
|
||||
import { MessageActions, SnowflakeUtils } from "@webpack/common";
|
||||
import { Message } from "discord-types/general";
|
||||
import type { PartialDeep } from "type-fest";
|
||||
|
||||
import { Argument } from "./types";
|
||||
|
||||
const createBotMessage = findByCodeLazy('username:"Clyde"');
|
||||
|
||||
export function generateId() {
|
||||
|
|
@ -49,8 +51,8 @@ export function sendBotMessage(channelId: string, message: PartialDeep<Message>)
|
|||
* @param fallbackValue Fallback value in case this option wasn't passed
|
||||
* @returns Value
|
||||
*/
|
||||
export function findOption<T>(args: CommandArgument[], name: string): T & {} | undefined;
|
||||
export function findOption<T>(args: CommandArgument[], name: string, fallbackValue: T): T & {};
|
||||
export function findOption(args: CommandArgument[], name: string, fallbackValue?: any) {
|
||||
export function findOption<T>(args: Argument[], name: string): T & {} | undefined;
|
||||
export function findOption<T>(args: Argument[], name: string, fallbackValue: T): T & {};
|
||||
export function findOption(args: Argument[], name: string, fallbackValue?: any) {
|
||||
return (args.find(a => a.name === name)?.value ?? fallbackValue) as any;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,39 +18,38 @@
|
|||
|
||||
import { Logger } from "@utils/Logger";
|
||||
import { makeCodeblock } from "@utils/text";
|
||||
import { CommandArgument, CommandContext, CommandOption } from "@vencord/discord-types";
|
||||
|
||||
import { sendBotMessage } from "./commandHelpers";
|
||||
import { ApplicationCommandInputType, ApplicationCommandOptionType, ApplicationCommandType, VencordCommand } from "./types";
|
||||
import { ApplicationCommandInputType, ApplicationCommandOptionType, ApplicationCommandType, Argument, Command, CommandContext, Option } from "./types";
|
||||
|
||||
export * from "./commandHelpers";
|
||||
export * from "./types";
|
||||
|
||||
export let BUILT_IN: VencordCommand[];
|
||||
export const commands = {} as Record<string, VencordCommand>;
|
||||
export let BUILT_IN: Command[];
|
||||
export const commands = {} as Record<string, Command>;
|
||||
|
||||
// hack for plugins being evaluated before we can grab these from webpack
|
||||
const OptPlaceholder = Symbol("OptionalMessageOption") as any as CommandOption;
|
||||
const ReqPlaceholder = Symbol("RequiredMessageOption") as any as CommandOption;
|
||||
const OptPlaceholder = Symbol("OptionalMessageOption") as any as Option;
|
||||
const ReqPlaceholder = Symbol("RequiredMessageOption") as any as Option;
|
||||
|
||||
/**
|
||||
* Optional message option named "message" you can use in commands.
|
||||
* Used in "tableflip" or "shrug"
|
||||
* @see {@link RequiredMessageOption}
|
||||
*/
|
||||
export let OptionalMessageOption: CommandOption = OptPlaceholder;
|
||||
export let OptionalMessageOption: Option = OptPlaceholder;
|
||||
/**
|
||||
* Required message option named "message" you can use in commands.
|
||||
* Used in "me"
|
||||
* @see {@link OptionalMessageOption}
|
||||
*/
|
||||
export let RequiredMessageOption: CommandOption = ReqPlaceholder;
|
||||
export let RequiredMessageOption: Option = ReqPlaceholder;
|
||||
|
||||
// Discord's command list has random gaps for some reason, which can cause issues while rendering the commands
|
||||
// Add this offset to every added command to keep them unique
|
||||
let commandIdOffset: number;
|
||||
|
||||
export const _init = function (cmds: VencordCommand[]) {
|
||||
export const _init = function (cmds: Command[]) {
|
||||
try {
|
||||
BUILT_IN = cmds;
|
||||
OptionalMessageOption = cmds.find(c => (c.untranslatedName || c.displayName) === "shrug")!.options![0];
|
||||
|
|
@ -62,7 +61,7 @@ export const _init = function (cmds: VencordCommand[]) {
|
|||
return cmds;
|
||||
} as never;
|
||||
|
||||
export const _handleCommand = function (cmd: VencordCommand, args: CommandArgument[], ctx: CommandContext) {
|
||||
export const _handleCommand = function (cmd: Command, args: Argument[], ctx: CommandContext) {
|
||||
if (!cmd.isVencordCommand)
|
||||
return cmd.execute(args, ctx);
|
||||
|
||||
|
|
@ -93,7 +92,7 @@ export const _handleCommand = function (cmd: VencordCommand, args: CommandArgume
|
|||
* Prepare a Command Option for Discord by filling missing fields
|
||||
* @param opt
|
||||
*/
|
||||
export function prepareOption<O extends CommandOption | VencordCommand>(opt: O): O {
|
||||
export function prepareOption<O extends Option | Command>(opt: O): O {
|
||||
opt.displayName ||= opt.name;
|
||||
opt.displayDescription ||= opt.description;
|
||||
opt.options?.forEach((opt, i, opts) => {
|
||||
|
|
@ -110,7 +109,7 @@ export function prepareOption<O extends CommandOption | VencordCommand>(opt: O):
|
|||
// Yes, Discord registers individual commands for each subcommand
|
||||
// TODO: This probably doesn't support nested subcommands. If that is ever needed,
|
||||
// investigate
|
||||
function registerSubCommands(cmd: VencordCommand, plugin: string) {
|
||||
function registerSubCommands(cmd: Command, plugin: string) {
|
||||
cmd.options?.forEach(o => {
|
||||
if (o.type !== ApplicationCommandOptionType.SUB_COMMAND)
|
||||
throw new Error("When specifying sub-command options, all options must be sub-commands.");
|
||||
|
|
@ -133,7 +132,7 @@ function registerSubCommands(cmd: VencordCommand, plugin: string) {
|
|||
});
|
||||
}
|
||||
|
||||
export function registerCommand<C extends VencordCommand>(command: C, plugin: string) {
|
||||
export function registerCommand<C extends Command>(command: C, plugin: string) {
|
||||
if (!BUILT_IN) {
|
||||
console.warn(
|
||||
"[CommandsAPI]",
|
||||
|
|
|
|||
|
|
@ -1,12 +1,106 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2025 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
* 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 { Command } from "@vencord/discord-types";
|
||||
export { ApplicationCommandInputType, ApplicationCommandOptionType, ApplicationCommandType } from "@vencord/discord-types/enums";
|
||||
import { Channel, Guild } from "discord-types/general";
|
||||
import { Promisable } from "type-fest";
|
||||
|
||||
export interface VencordCommand extends Command {
|
||||
isVencordCommand?: boolean;
|
||||
export interface CommandContext {
|
||||
channel: Channel;
|
||||
guild?: Guild;
|
||||
}
|
||||
|
||||
export const enum ApplicationCommandOptionType {
|
||||
SUB_COMMAND = 1,
|
||||
SUB_COMMAND_GROUP = 2,
|
||||
STRING = 3,
|
||||
INTEGER = 4,
|
||||
BOOLEAN = 5,
|
||||
USER = 6,
|
||||
CHANNEL = 7,
|
||||
ROLE = 8,
|
||||
MENTIONABLE = 9,
|
||||
NUMBER = 10,
|
||||
ATTACHMENT = 11,
|
||||
}
|
||||
|
||||
export const enum ApplicationCommandInputType {
|
||||
BUILT_IN = 0,
|
||||
BUILT_IN_TEXT = 1,
|
||||
BUILT_IN_INTEGRATION = 2,
|
||||
BOT = 3,
|
||||
PLACEHOLDER = 4,
|
||||
}
|
||||
|
||||
export interface Option {
|
||||
name: string;
|
||||
displayName?: string;
|
||||
type: ApplicationCommandOptionType;
|
||||
description: string;
|
||||
displayDescription?: string;
|
||||
required?: boolean;
|
||||
options?: Option[];
|
||||
choices?: Array<ChoicesOption>;
|
||||
}
|
||||
|
||||
export interface ChoicesOption {
|
||||
label: string;
|
||||
value: string;
|
||||
name: string;
|
||||
displayName?: string;
|
||||
}
|
||||
|
||||
export const enum ApplicationCommandType {
|
||||
CHAT_INPUT = 1,
|
||||
USER = 2,
|
||||
MESSAGE = 3,
|
||||
}
|
||||
|
||||
export interface CommandReturnValue {
|
||||
content: string;
|
||||
/** TODO: implement */
|
||||
cancel?: boolean;
|
||||
}
|
||||
|
||||
export interface Argument {
|
||||
type: ApplicationCommandOptionType;
|
||||
name: string;
|
||||
value: string;
|
||||
focused: undefined;
|
||||
options: Argument[];
|
||||
}
|
||||
|
||||
export interface Command {
|
||||
id?: string;
|
||||
applicationId?: string;
|
||||
type?: ApplicationCommandType;
|
||||
inputType?: ApplicationCommandInputType;
|
||||
plugin?: string;
|
||||
isVencordCommand?: boolean;
|
||||
|
||||
name: string;
|
||||
untranslatedName?: string;
|
||||
displayName?: string;
|
||||
description: string;
|
||||
untranslatedDescription?: string;
|
||||
displayDescription?: string;
|
||||
|
||||
options?: Option[];
|
||||
predicate?(ctx: CommandContext): boolean;
|
||||
|
||||
execute(args: Argument[], ctx: CommandContext): Promisable<void | CommandReturnValue>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,9 +22,9 @@ export function promisifyRequest<T = undefined>(
|
|||
request: IDBRequest<T> | IDBTransaction,
|
||||
): Promise<T> {
|
||||
return new Promise<T>((resolve, reject) => {
|
||||
// @ts-expect-error - file size hacks
|
||||
// @ts-ignore - file size hacks
|
||||
request.oncomplete = request.onsuccess = () => resolve(request.result);
|
||||
// @ts-expect-error - file size hacks
|
||||
// @ts-ignore - file size hacks
|
||||
request.onabort = request.onerror = () => reject(request.error);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
*/
|
||||
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Channel, User } from "@vencord/discord-types";
|
||||
import { Channel, User } from "discord-types/general/index.js";
|
||||
import { JSX } from "react";
|
||||
|
||||
interface DecoratorProps {
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ export function _modifyAccessories(
|
|||
) {
|
||||
for (const [key, accessory] of accessories.entries()) {
|
||||
const res = (
|
||||
<ErrorBoundary noop message={`Failed to render ${key} Message Accessory`} key={key}>
|
||||
<ErrorBoundary message={`Failed to render ${key} Message Accessory`} key={key}>
|
||||
<accessory.render {...props} />
|
||||
</ErrorBoundary>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
*/
|
||||
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Channel, Message } from "@vencord/discord-types";
|
||||
import { Channel, Message } from "discord-types/general/index.js";
|
||||
import { JSX } from "react";
|
||||
|
||||
export interface MessageDecorationProps {
|
||||
|
|
|
|||
|
|
@ -17,8 +17,9 @@
|
|||
*/
|
||||
|
||||
import { Logger } from "@utils/Logger";
|
||||
import type { Channel, CloudUpload, CustomEmoji, Message } from "@vencord/discord-types";
|
||||
import { MessageStore } from "@webpack/common";
|
||||
import { CustomEmoji } from "@webpack/types";
|
||||
import type { Channel, Message } from "discord-types/general";
|
||||
import type { Promisable } from "type-fest";
|
||||
|
||||
const MessageEventsLogger = new Logger("MessageEvents", "#e5c890");
|
||||
|
|
@ -30,6 +31,30 @@ export interface MessageObject {
|
|||
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?: {
|
||||
|
|
@ -38,9 +63,9 @@ export interface MessageReplyOptions {
|
|||
};
|
||||
}
|
||||
|
||||
export interface MessageOptions {
|
||||
export interface MessageExtra {
|
||||
stickers?: string[];
|
||||
uploads?: CloudUpload[];
|
||||
uploads?: Upload[];
|
||||
replyOptions: MessageReplyOptions;
|
||||
content: string;
|
||||
channel: Channel;
|
||||
|
|
@ -48,17 +73,17 @@ export interface MessageOptions {
|
|||
openWarningPopout: (props: any) => any;
|
||||
}
|
||||
|
||||
export type MessageSendListener = (channelId: string, messageObj: MessageObject, options: MessageOptions) => Promisable<void | { cancel: boolean; }>;
|
||||
export type MessageSendListener = (channelId: string, messageObj: MessageObject, extra: MessageExtra) => Promisable<void | { cancel: boolean; }>;
|
||||
export type MessageEditListener = (channelId: string, messageId: string, messageObj: MessageObject) => Promisable<void | { cancel: boolean; }>;
|
||||
|
||||
const sendListeners = new Set<MessageSendListener>();
|
||||
const editListeners = new Set<MessageEditListener>();
|
||||
|
||||
export async function _handlePreSend(channelId: string, messageObj: MessageObject, options: MessageOptions, replyOptions: MessageReplyOptions) {
|
||||
options.replyOptions = replyOptions;
|
||||
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, options);
|
||||
const result = await listener(channelId, messageObj, extra);
|
||||
if (result?.cancel) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Logger } from "@utils/Logger";
|
||||
import { Channel, Message } from "@vencord/discord-types";
|
||||
import { Channel, Message } from "discord-types/general";
|
||||
import type { ComponentType, MouseEventHandler } from "react";
|
||||
|
||||
const logger = new Logger("MessagePopover");
|
||||
|
|
|
|||
|
|
@ -4,8 +4,9 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { Message } from "@vencord/discord-types";
|
||||
import { MessageCache, MessageStore } from "@webpack/common";
|
||||
import { FluxStore } from "@webpack/types";
|
||||
import { Message } from "discord-types/general";
|
||||
|
||||
/**
|
||||
* Update and re-render a message
|
||||
|
|
@ -24,5 +25,5 @@ export function updateMessage(channelId: string, messageId: string, fields?: Par
|
|||
});
|
||||
|
||||
MessageCache.commit(newChannelMessageCache);
|
||||
MessageStore.emitChange();
|
||||
(MessageStore as unknown as FluxStore).emitChange();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,10 +16,7 @@
|
|||
* 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);
|
||||
|
|
@ -39,11 +36,7 @@ export function nextNotice() {
|
|||
}
|
||||
}
|
||||
|
||||
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]);
|
||||
export function showNotice(message: string, buttonText: string, onOkClick: () => void) {
|
||||
noticesQueue.push(["GENERIC", message, buttonText, onOkClick]);
|
||||
if (!currentNotice) nextNotice();
|
||||
}
|
||||
|
|
@ -104,7 +104,9 @@ export default ErrorBoundary.wrap(function NotificationComponent({
|
|||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
{richBody ?? <p className="vc-notification-p">{body}</p>}
|
||||
<div>
|
||||
{richBody ?? <p className="vc-notification-p">{body}</p>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{image && <img className="vc-notification-img" src={image} alt="" />}
|
||||
|
|
|
|||
|
|
@ -20,10 +20,10 @@ import * as DataStore from "@api/DataStore";
|
|||
import { Settings } from "@api/Settings";
|
||||
import { classNameFactory } from "@api/Styles";
|
||||
import { Flex } from "@components/Flex";
|
||||
import { openNotificationSettingsModal } from "@components/settings/tabs/vencord/NotificationSettings";
|
||||
import { closeModal, ModalCloseButton, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
||||
import { openNotificationSettingsModal } from "@components/VencordSettings/NotificationSettings";
|
||||
import { closeModal, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
||||
import { useAwaiter } from "@utils/react";
|
||||
import { Alerts, Button, Forms, ListScrollerThin, React, Text, Timestamp, useEffect, useReducer, useState } from "@webpack/common";
|
||||
import { Alerts, Button, Forms, React, Text, Timestamp, useEffect, useReducer, useState } from "@webpack/common";
|
||||
import { nanoid } from "nanoid";
|
||||
import type { DispatchWithoutAction } from "react";
|
||||
|
||||
|
|
@ -103,9 +103,21 @@ export function useLogs() {
|
|||
|
||||
function NotificationEntry({ data }: { data: PersistentNotificationData; }) {
|
||||
const [removing, setRemoving] = useState(false);
|
||||
const ref = React.useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const div = ref.current!;
|
||||
|
||||
const setHeight = () => {
|
||||
if (div.clientHeight === 0) return requestAnimationFrame(setHeight);
|
||||
div.style.height = `${div.clientHeight}px`;
|
||||
};
|
||||
|
||||
setHeight();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className={cl("wrapper", { removing })}>
|
||||
<div className={cl("wrapper", { removing })} ref={ref}>
|
||||
<NotificationComponent
|
||||
{...data}
|
||||
permanent={true}
|
||||
|
|
@ -117,13 +129,13 @@ function NotificationEntry({ data }: { data: PersistentNotificationData; }) {
|
|||
setTimeout(() => deleteNotification(data.timestamp), 200);
|
||||
}}
|
||||
richBody={
|
||||
<div className={cl("body-wrapper")}>
|
||||
<div className={cl("body")}>{data.body}</div>
|
||||
<div className={cl("body")}>
|
||||
{data.body}
|
||||
<Timestamp timestamp={new Date(data.timestamp)} className={cl("timestamp")} />
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</div >
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -139,14 +151,9 @@ export function NotificationLog({ log, pending }: { log: PersistentNotificationD
|
|||
);
|
||||
|
||||
return (
|
||||
<ListScrollerThin
|
||||
className={cl("container")}
|
||||
sections={[log.length]}
|
||||
sectionHeight={0}
|
||||
rowHeight={120}
|
||||
renderSection={() => null}
|
||||
renderRow={item => <NotificationEntry data={log[item.row]} key={log[item.row].id} />}
|
||||
/>
|
||||
<div className={cl("container")}>
|
||||
{log.map(n => <NotificationEntry data={n} key={n.id} />)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -154,15 +161,15 @@ function LogModal({ modalProps, close }: { modalProps: ModalProps; close(): void
|
|||
const [log, pending] = useLogs();
|
||||
|
||||
return (
|
||||
<ModalRoot {...modalProps} size={ModalSize.LARGE} className={cl("modal")}>
|
||||
<ModalRoot {...modalProps} size={ModalSize.LARGE}>
|
||||
<ModalHeader>
|
||||
<Text variant="heading-lg/semibold" style={{ flexGrow: 1 }}>Notification Log</Text>
|
||||
<ModalCloseButton onClick={close} />
|
||||
</ModalHeader>
|
||||
|
||||
<div style={{ width: "100%" }}>
|
||||
<ModalContent>
|
||||
<NotificationLog log={log} pending={pending} />
|
||||
</div>
|
||||
</ModalContent>
|
||||
|
||||
<ModalFooter>
|
||||
<Flex>
|
||||
|
|
|
|||
|
|
@ -3,14 +3,18 @@
|
|||
all: unset;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
color: var(--text-default);
|
||||
background-color: var(--background-base-low);
|
||||
color: var(--text-normal);
|
||||
background-color: var(--background-secondary-alt);
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.visual-refresh .vc-notification-root {
|
||||
background-color: var(--bg-overlay-floating, var(--background-base-low));
|
||||
}
|
||||
|
||||
.vc-notification-root:not(.vc-notification-log-wrapper > .vc-notification-root) {
|
||||
position: absolute;
|
||||
z-index: 2147483647;
|
||||
|
|
@ -28,7 +32,6 @@
|
|||
|
||||
.vc-notification-content {
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.vc-notification-header {
|
||||
|
|
@ -78,11 +81,6 @@
|
|||
width: 100%;
|
||||
}
|
||||
|
||||
.vc-notification-log-modal {
|
||||
max-width: 962px;
|
||||
width: clamp(var(--modal-width-large, 800px), 962px, 85vw);
|
||||
}
|
||||
|
||||
.vc-notification-log-empty {
|
||||
height: 218px;
|
||||
background: url("/assets/b36de980b174d7b798c89f35c116e5c6.svg") center no-repeat;
|
||||
|
|
@ -90,23 +88,19 @@
|
|||
}
|
||||
|
||||
.vc-notification-log-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 1em;
|
||||
max-height: min(750px, 75vh);
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.vc-notification-log-wrapper {
|
||||
height: 120px;
|
||||
width: 100%;
|
||||
padding-bottom: 16px;
|
||||
box-sizing: border-box;
|
||||
transition: 200ms ease;
|
||||
transition-property: height, opacity;
|
||||
}
|
||||
|
||||
/* stylelint-disable-next-line no-descending-specificity */
|
||||
.vc-notification-root {
|
||||
height: 104px;
|
||||
}
|
||||
.vc-notification-log-wrapper:not(:last-child) {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.vc-notification-log-removing {
|
||||
|
|
@ -115,18 +109,9 @@
|
|||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.vc-notification-log-body-wrapper {
|
||||
.vc-notification-log-body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.vc-notification-log-body {
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
line-height: 1.2em;
|
||||
}
|
||||
|
||||
.vc-notification-log-timestamp {
|
||||
|
|
@ -138,4 +123,4 @@
|
|||
.vc-notification-log-danger-btn {
|
||||
color: var(--white-500);
|
||||
background-color: var(--button-danger-background);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -268,7 +268,7 @@ type ResolveUseSettings<T extends object> = {
|
|||
[Key in keyof T]:
|
||||
Key extends string
|
||||
? T[Key] extends Record<string, unknown>
|
||||
// @ts-expect-error "Type instantiation is excessively deep and possibly infinite"
|
||||
// @ts-ignore "Type instantiation is excessively deep and possibly infinite"
|
||||
? UseSettings<T[Key]> extends string ? `${Key}.${UseSettings<T[Key]>}` : never
|
||||
: Key
|
||||
: never;
|
||||
|
|
|
|||
|
|
@ -16,9 +16,9 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
export function AddonBadge({ text, color }) {
|
||||
export function Badge({ text, color }) {
|
||||
return (
|
||||
<div className="vc-addon-badge" style={{
|
||||
<div className="vc-plugins-badge" style={{
|
||||
backgroundColor: color,
|
||||
justifySelf: "flex-end",
|
||||
marginLeft: "auto"
|
||||
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
import { React, TextInput } from "@webpack/common";
|
||||
|
||||
// TODO: Refactor settings to use this as well
|
||||
interface TextInputProps {
|
||||
/**
|
||||
* WARNING: Changing this between renders will have no effect!
|
||||
|
|
|
|||
|
|
@ -16,9 +16,10 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Heart } from "@components/Heart";
|
||||
import { ButtonProps } from "@vencord/discord-types";
|
||||
import { Button } from "@webpack/common";
|
||||
import { ButtonProps } from "@webpack/types";
|
||||
|
||||
import { Heart } from "./Heart";
|
||||
|
||||
export default function DonateButton({
|
||||
look = Button.Looks.LINK,
|
||||
|
|
@ -75,15 +75,10 @@ const ErrorBoundary = LazyComponent(() => {
|
|||
logger.error(`${this.props.message || "A component threw an Error"}\n`, error, errorInfo.componentStack);
|
||||
}
|
||||
|
||||
get isNoop() {
|
||||
if (IS_DEV) return false;
|
||||
return this.props.noop;
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.error === NO_ERROR) return this.props.children;
|
||||
|
||||
if (this.isNoop) return null;
|
||||
if (this.props.noop) return null;
|
||||
|
||||
if (this.props.fallback)
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -3,9 +3,5 @@
|
|||
background-color: #e7828430;
|
||||
border: 1px solid #e78284;
|
||||
border-radius: 5px;
|
||||
color: var(--text-default, white);
|
||||
|
||||
& a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
color: var(--text-normal, white);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ interface BaseIconProps extends IconProps {
|
|||
}
|
||||
|
||||
type IconProps = JSX.IntrinsicElements["svg"];
|
||||
type ImageProps = JSX.IntrinsicElements["img"];
|
||||
|
||||
function Icon({ height = 24, width = 24, className, children, viewBox, ...svgProps }: PropsWithChildren<BaseIconProps>) {
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -28,9 +28,6 @@ export function Link(props: React.PropsWithChildren<Props>) {
|
|||
props.style.pointerEvents = "none";
|
||||
props["aria-disabled"] = true;
|
||||
}
|
||||
|
||||
props.rel ??= "noreferrer";
|
||||
|
||||
return (
|
||||
<a role="link" target="_blank" {...props}>
|
||||
{props.children}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import "./ContributorModal.css";
|
||||
import "./contributorModal.css";
|
||||
|
||||
import { useSettings } from "@api/Settings";
|
||||
import { classNameFactory } from "@api/Styles";
|
||||
|
|
@ -14,13 +14,13 @@ import { DevsById } from "@utils/constants";
|
|||
import { fetchUserProfile } from "@utils/discord";
|
||||
import { classes, pluralise } from "@utils/misc";
|
||||
import { ModalContent, ModalRoot, openModal } from "@utils/modal";
|
||||
import { User } from "@vencord/discord-types";
|
||||
import { Forms, showToast, useEffect, useMemo, UserProfileStore, useStateFromStores } from "@webpack/common";
|
||||
import { User } from "discord-types/general";
|
||||
|
||||
import Plugins from "~plugins";
|
||||
|
||||
import { PluginCard } from ".";
|
||||
import { GithubButton, WebsiteButton } from "./LinkIconButton";
|
||||
import { PluginCard } from "./PluginCard";
|
||||
|
||||
const cl = classNameFactory("vc-author-modal-");
|
||||
|
||||
|
|
@ -2,11 +2,11 @@
|
|||
height: 32px;
|
||||
width: 32px;
|
||||
border-radius: 50%;
|
||||
border: 4px solid var(--background-base-lowest);
|
||||
border: 4px solid var(--background-tertiary);
|
||||
box-sizing: border-box
|
||||
}
|
||||
|
||||
.vc-settings-modal-links {
|
||||
display: flex;
|
||||
gap: 0.2em;
|
||||
}
|
||||
}
|
||||
|
|
@ -6,10 +6,11 @@
|
|||
|
||||
import "./LinkIconButton.css";
|
||||
|
||||
import { GithubIcon, WebsiteIcon } from "@components/Icons";
|
||||
import { getTheme, Theme } from "@utils/discord";
|
||||
import { MaskedLink, Tooltip } from "@webpack/common";
|
||||
|
||||
import { GithubIcon, WebsiteIcon } from "..";
|
||||
|
||||
export function GithubLinkIcon() {
|
||||
const theme = getTheme() === Theme.Light ? "#000000" : "#FFFFFF";
|
||||
return <GithubIcon aria-hidden fill={theme} className={"vc-settings-modal-link-icon"} />;
|
||||
|
|
@ -23,32 +23,41 @@ import { useSettings } from "@api/Settings";
|
|||
import { classNameFactory } from "@api/Styles";
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Flex } from "@components/Flex";
|
||||
import { debounce } from "@shared/debounce";
|
||||
import { gitRemote } from "@shared/vencordUserAgent";
|
||||
import { proxyLazy } from "@utils/lazy";
|
||||
import { Margins } from "@utils/margins";
|
||||
import { classes, isObjectEmpty } from "@utils/misc";
|
||||
import { ModalCloseButton, ModalContent, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
||||
import { ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
||||
import { OptionType, Plugin } from "@utils/types";
|
||||
import { User } from "@vencord/discord-types";
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
import { Clickable, FluxDispatcher, Forms, React, Text, Tooltip, useEffect, UserStore, UserSummaryItem, UserUtils, useState } from "@webpack/common";
|
||||
import { findByPropsLazy, findComponentByCodeLazy } from "@webpack";
|
||||
import { Button, Clickable, FluxDispatcher, Forms, React, Text, Tooltip, UserStore, UserUtils } from "@webpack/common";
|
||||
import { User } from "discord-types/general";
|
||||
import { Constructor } from "type-fest";
|
||||
|
||||
import { PluginMeta } from "~plugins";
|
||||
|
||||
import { OptionComponentMap } from "./components";
|
||||
import {
|
||||
ISettingCustomElementProps,
|
||||
ISettingElementProps,
|
||||
SettingBooleanComponent,
|
||||
SettingCustomComponent,
|
||||
SettingNumericComponent,
|
||||
SettingSelectComponent,
|
||||
SettingSliderComponent,
|
||||
SettingTextComponent
|
||||
} from "./components";
|
||||
import { openContributorModal } from "./ContributorModal";
|
||||
import { GithubButton, WebsiteButton } from "./LinkIconButton";
|
||||
|
||||
const cl = classNameFactory("vc-plugin-modal-");
|
||||
|
||||
const UserSummaryItem = findComponentByCodeLazy("defaultRenderUser", "showDefaultAvatarsForNullUsers");
|
||||
const AvatarStyles = findByPropsLazy("moreUsers", "emptyUser", "avatarContainer", "clickableAvatar");
|
||||
const UserRecord: Constructor<Partial<User>> = proxyLazy(() => UserStore.getCurrentUser().constructor) as any;
|
||||
|
||||
interface PluginModalProps extends ModalProps {
|
||||
plugin: Plugin;
|
||||
onRestartNeeded(key: string): void;
|
||||
onRestartNeeded(): void;
|
||||
}
|
||||
|
||||
function makeDummyUser(user: { username: string; id?: string; avatar?: string; }) {
|
||||
|
|
@ -59,22 +68,39 @@ function makeDummyUser(user: { username: string; id?: string; avatar?: string; }
|
|||
/** To stop discord making unwanted requests... */
|
||||
bot: true,
|
||||
});
|
||||
|
||||
FluxDispatcher.dispatch({
|
||||
type: "USER_UPDATE",
|
||||
user: newUser,
|
||||
});
|
||||
|
||||
return newUser;
|
||||
}
|
||||
|
||||
const Components: Record<OptionType, React.ComponentType<ISettingElementProps<any> | ISettingCustomElementProps<any>>> = {
|
||||
[OptionType.STRING]: SettingTextComponent,
|
||||
[OptionType.NUMBER]: SettingNumericComponent,
|
||||
[OptionType.BIGINT]: SettingNumericComponent,
|
||||
[OptionType.BOOLEAN]: SettingBooleanComponent,
|
||||
[OptionType.SELECT]: SettingSelectComponent,
|
||||
[OptionType.SLIDER]: SettingSliderComponent,
|
||||
[OptionType.COMPONENT]: SettingCustomComponent,
|
||||
[OptionType.CUSTOM]: () => null,
|
||||
};
|
||||
|
||||
export default function PluginModal({ plugin, onRestartNeeded, onClose, transitionState }: PluginModalProps) {
|
||||
const [authors, setAuthors] = React.useState<Partial<User>[]>([]);
|
||||
|
||||
const pluginSettings = useSettings().plugins[plugin.name];
|
||||
|
||||
const [tempSettings, setTempSettings] = React.useState<Record<string, any>>({});
|
||||
|
||||
const [errors, setErrors] = React.useState<Record<string, boolean>>({});
|
||||
const [saveError, setSaveError] = React.useState<string | null>(null);
|
||||
|
||||
const canSubmit = () => Object.values(errors).every(e => !e);
|
||||
|
||||
const hasSettings = Boolean(pluginSettings && plugin.options && !isObjectEmpty(plugin.options));
|
||||
|
||||
const [authors, setAuthors] = useState<Partial<User>[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
React.useEffect(() => {
|
||||
(async () => {
|
||||
for (const user of plugin.authors.slice(0, 6)) {
|
||||
const author = user.id
|
||||
|
|
@ -87,40 +113,63 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti
|
|||
})();
|
||||
}, [plugin.authors]);
|
||||
|
||||
function renderSettings() {
|
||||
if (!hasSettings || !plugin.options)
|
||||
return <Forms.FormText>There are no settings for this plugin.</Forms.FormText>;
|
||||
async function saveAndClose() {
|
||||
if (!plugin.options) {
|
||||
onClose();
|
||||
return;
|
||||
}
|
||||
|
||||
const options = Object.entries(plugin.options).map(([key, setting]) => {
|
||||
if (setting.type === OptionType.CUSTOM || setting.hidden) return null;
|
||||
|
||||
function onChange(newValue: any) {
|
||||
const option = plugin.options?.[key];
|
||||
if (!option || option.type === OptionType.CUSTOM) return;
|
||||
|
||||
pluginSettings[key] = newValue;
|
||||
|
||||
if (option.restartNeeded) onRestartNeeded(key);
|
||||
if (plugin.beforeSave) {
|
||||
const result = await Promise.resolve(plugin.beforeSave(tempSettings));
|
||||
if (result !== true) {
|
||||
setSaveError(result);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const Component = OptionComponentMap[setting.type];
|
||||
return (
|
||||
<Component
|
||||
id={key}
|
||||
key={key}
|
||||
option={setting}
|
||||
onChange={debounce(onChange)}
|
||||
pluginSettings={pluginSettings}
|
||||
definedSettings={plugin.settings}
|
||||
/>
|
||||
);
|
||||
});
|
||||
let restartNeeded = false;
|
||||
for (const [key, value] of Object.entries(tempSettings)) {
|
||||
const option = plugin.options[key];
|
||||
pluginSettings[key] = value;
|
||||
|
||||
return (
|
||||
<div className="vc-plugins-settings">
|
||||
{options}
|
||||
</div>
|
||||
);
|
||||
if (option.type === OptionType.CUSTOM) continue;
|
||||
if (option?.restartNeeded) restartNeeded = true;
|
||||
}
|
||||
if (restartNeeded) onRestartNeeded();
|
||||
onClose();
|
||||
}
|
||||
|
||||
function renderSettings() {
|
||||
if (!hasSettings || !plugin.options) {
|
||||
return <Forms.FormText>There are no settings for this plugin.</Forms.FormText>;
|
||||
} else {
|
||||
const options = Object.entries(plugin.options).map(([key, setting]) => {
|
||||
if (setting.type === OptionType.CUSTOM || setting.hidden) return null;
|
||||
|
||||
function onChange(newValue: any) {
|
||||
setTempSettings(s => ({ ...s, [key]: newValue }));
|
||||
}
|
||||
|
||||
function onError(hasError: boolean) {
|
||||
setErrors(e => ({ ...e, [key]: hasError }));
|
||||
}
|
||||
|
||||
const Component = Components[setting.type];
|
||||
return (
|
||||
<Component
|
||||
id={key}
|
||||
key={key}
|
||||
option={setting}
|
||||
onChange={onChange}
|
||||
onError={onError}
|
||||
pluginSettings={pluginSettings}
|
||||
definedSettings={plugin.settings}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
return <Flex flexDirection="column" style={{ gap: 12, marginBottom: 16 }}>{options}</Flex>;
|
||||
}
|
||||
}
|
||||
|
||||
function renderMoreUsers(_label: string, count: number) {
|
||||
|
|
@ -143,16 +192,38 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti
|
|||
);
|
||||
}
|
||||
|
||||
/*
|
||||
function switchToPopout() {
|
||||
onClose();
|
||||
|
||||
const PopoutKey = `DISCORD_VENCORD_PLUGIN_SETTINGS_MODAL_${plugin.name}`;
|
||||
PopoutActions.open(
|
||||
PopoutKey,
|
||||
() => <PluginModal
|
||||
transitionState={transitionState}
|
||||
plugin={plugin}
|
||||
onRestartNeeded={onRestartNeeded}
|
||||
onClose={() => PopoutActions.close(PopoutKey)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
*/
|
||||
|
||||
const pluginMeta = PluginMeta[plugin.name];
|
||||
|
||||
return (
|
||||
<ModalRoot transitionState={transitionState} size={ModalSize.MEDIUM}>
|
||||
<ModalHeader separator={false} className={Margins.bottom8}>
|
||||
<Text variant="heading-xl/bold" style={{ flexGrow: 1 }}>{plugin.name}</Text>
|
||||
<ModalRoot transitionState={transitionState} size={ModalSize.MEDIUM} className="vc-text-selectable">
|
||||
<ModalHeader separator={false}>
|
||||
<Text variant="heading-lg/semibold" style={{ flexGrow: 1 }}>{plugin.name}</Text>
|
||||
|
||||
{/*
|
||||
<Button look={Button.Looks.BLANK} onClick={switchToPopout}>
|
||||
<OpenExternalIcon aria-label="Open in Popout" />
|
||||
</Button>
|
||||
*/}
|
||||
<ModalCloseButton onClick={onClose} />
|
||||
</ModalHeader>
|
||||
|
||||
<ModalContent className={Margins.bottom16}>
|
||||
<ModalContent>
|
||||
<Forms.FormSection>
|
||||
<Flex className={cl("info")}>
|
||||
<Forms.FormText className={cl("description")}>{plugin.description}</Forms.FormText>
|
||||
|
|
@ -169,14 +240,16 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti
|
|||
</div>
|
||||
)}
|
||||
</Flex>
|
||||
<Text variant="heading-lg/semibold" className={classes(Margins.top8, Margins.bottom8)}>Authors</Text>
|
||||
<div style={{ width: "fit-content" }}>
|
||||
<Forms.FormTitle tag="h3" style={{ marginTop: 8, marginBottom: 0 }}>Authors</Forms.FormTitle>
|
||||
<div style={{ width: "fit-content", marginBottom: 8 }}>
|
||||
<UserSummaryItem
|
||||
users={authors}
|
||||
count={plugin.authors.length}
|
||||
guildId={undefined}
|
||||
renderIcon={false}
|
||||
max={6}
|
||||
showDefaultAvatarsForNullUsers
|
||||
showUserPopout
|
||||
renderMoreUsers={renderMoreUsers}
|
||||
renderUser={(user: User) => (
|
||||
<Clickable
|
||||
|
|
@ -194,32 +267,59 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti
|
|||
/>
|
||||
</div>
|
||||
</Forms.FormSection>
|
||||
|
||||
{!!plugin.settingsAboutComponent && (
|
||||
<div className={Margins.top16}>
|
||||
<div className={classes(Margins.bottom8, "vc-text-selectable")}>
|
||||
<Forms.FormSection>
|
||||
<ErrorBoundary message="An error occurred while rendering this plugin's custom Info Component">
|
||||
<plugin.settingsAboutComponent />
|
||||
<ErrorBoundary message="An error occurred while rendering this plugin's custom InfoComponent">
|
||||
<plugin.settingsAboutComponent tempSettings={tempSettings} />
|
||||
</ErrorBoundary>
|
||||
</Forms.FormSection>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Forms.FormSection>
|
||||
<Text variant="heading-lg/semibold" className={classes(Margins.top16, Margins.bottom8)}>Settings</Text>
|
||||
<Forms.FormSection className={Margins.bottom16}>
|
||||
<Forms.FormTitle tag="h3">Settings</Forms.FormTitle>
|
||||
{renderSettings()}
|
||||
</Forms.FormSection>
|
||||
</ModalContent>
|
||||
{hasSettings && <ModalFooter>
|
||||
<Flex flexDirection="column" style={{ width: "100%" }}>
|
||||
<Flex style={{ marginLeft: "auto" }}>
|
||||
<Button
|
||||
onClick={onClose}
|
||||
size={Button.Sizes.SMALL}
|
||||
color={Button.Colors.PRIMARY}
|
||||
look={Button.Looks.LINK}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Tooltip text="You must fix all errors before saving" shouldShow={!canSubmit()}>
|
||||
{({ onMouseEnter, onMouseLeave }) => (
|
||||
<Button
|
||||
size={Button.Sizes.SMALL}
|
||||
color={Button.Colors.BRAND}
|
||||
onClick={saveAndClose}
|
||||
onMouseEnter={onMouseEnter}
|
||||
onMouseLeave={onMouseLeave}
|
||||
disabled={!canSubmit()}
|
||||
>
|
||||
Save & Close
|
||||
</Button>
|
||||
)}
|
||||
</Tooltip>
|
||||
</Flex>
|
||||
{saveError && <Text variant="text-md/semibold" style={{ color: "var(--text-danger)" }}>Error while saving: {saveError}</Text>}
|
||||
</Flex>
|
||||
</ModalFooter>}
|
||||
</ModalRoot>
|
||||
);
|
||||
}
|
||||
|
||||
export function openPluginModal(plugin: Plugin, onRestartNeeded?: (pluginName: string, key: string) => void) {
|
||||
export function openPluginModal(plugin: Plugin, onRestartNeeded?: (pluginName: string) => void) {
|
||||
openModal(modalProps => (
|
||||
<PluginModal
|
||||
{...modalProps}
|
||||
plugin={plugin}
|
||||
onRestartNeeded={(key: string) => onRestartNeeded?.(plugin.name, key)}
|
||||
onRestartNeeded={() => onRestartNeeded?.(plugin.name)}
|
||||
/>
|
||||
));
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* 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 { wordsFromCamel, wordsToTitle } from "@utils/text";
|
||||
import { PluginOptionBoolean } from "@utils/types";
|
||||
import { Forms, React, Switch } from "@webpack/common";
|
||||
|
||||
import { ISettingElementProps } from ".";
|
||||
|
||||
export function SettingBooleanComponent({ option, pluginSettings, definedSettings, id, onChange, onError }: ISettingElementProps<PluginOptionBoolean>) {
|
||||
const def = pluginSettings[id] ?? option.default;
|
||||
|
||||
const [state, setState] = React.useState(def ?? false);
|
||||
const [error, setError] = React.useState<string | null>(null);
|
||||
|
||||
React.useEffect(() => {
|
||||
onError(error !== null);
|
||||
}, [error]);
|
||||
|
||||
function handleChange(newValue: boolean): void {
|
||||
const isValid = option.isValid?.call(definedSettings, newValue) ?? true;
|
||||
if (typeof isValid === "string") setError(isValid);
|
||||
else if (!isValid) setError("Invalid input provided.");
|
||||
else {
|
||||
setError(null);
|
||||
setState(newValue);
|
||||
onChange(newValue);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Forms.FormSection>
|
||||
<Switch
|
||||
value={state}
|
||||
onChange={handleChange}
|
||||
note={option.description}
|
||||
disabled={option.disabled?.call(definedSettings) ?? false}
|
||||
{...option.componentProps}
|
||||
hideBorder
|
||||
style={{ marginBottom: "0.5em" }}
|
||||
>
|
||||
{wordsToTitle(wordsFromCamel(id))}
|
||||
</Switch>
|
||||
{error && <Forms.FormText style={{ color: "var(--text-danger)" }}>{error}</Forms.FormText>}
|
||||
</Forms.FormSection>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -18,8 +18,8 @@
|
|||
|
||||
import { PluginOptionComponent } from "@utils/types";
|
||||
|
||||
import { ComponentSettingProps } from "./Common";
|
||||
import { ISettingCustomElementProps } from ".";
|
||||
|
||||
export function ComponentSetting({ option, onChange }: ComponentSettingProps<PluginOptionComponent>) {
|
||||
return option.component({ setValue: onChange, option });
|
||||
export function SettingCustomComponent({ option, onChange, onError }: ISettingCustomElementProps<PluginOptionComponent>) {
|
||||
return option.component({ setValue: onChange, setError: onError, option });
|
||||
}
|
||||
|
|
@ -16,49 +16,58 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Margins } from "@utils/margins";
|
||||
import { wordsFromCamel, wordsToTitle } from "@utils/text";
|
||||
import { OptionType, PluginOptionNumber } from "@utils/types";
|
||||
import { React, TextInput, useState } from "@webpack/common";
|
||||
import { Forms, React, TextInput } from "@webpack/common";
|
||||
|
||||
import { resolveError, SettingProps, SettingsSection } from "./Common";
|
||||
import { ISettingElementProps } from ".";
|
||||
|
||||
const MAX_SAFE_NUMBER = BigInt(Number.MAX_SAFE_INTEGER);
|
||||
|
||||
export function NumberSetting({ option, pluginSettings, definedSettings, id, onChange }: SettingProps<PluginOptionNumber>) {
|
||||
export function SettingNumericComponent({ option, pluginSettings, definedSettings, id, onChange, onError }: ISettingElementProps<PluginOptionNumber>) {
|
||||
function serialize(value: any) {
|
||||
if (option.type === OptionType.BIGINT) return BigInt(value);
|
||||
return Number(value);
|
||||
}
|
||||
|
||||
const [state, setState] = useState<any>(`${pluginSettings[id] ?? option.default ?? 0}`);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [state, setState] = React.useState<any>(`${pluginSettings[id] ?? option.default ?? 0}`);
|
||||
const [error, setError] = React.useState<string | null>(null);
|
||||
|
||||
function handleChange(newValue: any) {
|
||||
React.useEffect(() => {
|
||||
onError(error !== null);
|
||||
}, [error]);
|
||||
|
||||
function handleChange(newValue) {
|
||||
const isValid = option.isValid?.call(definedSettings, newValue) ?? true;
|
||||
|
||||
setError(resolveError(isValid));
|
||||
|
||||
if (isValid === true) {
|
||||
onChange(serialize(newValue));
|
||||
}
|
||||
setError(null);
|
||||
if (typeof isValid === "string") setError(isValid);
|
||||
else if (!isValid) setError("Invalid input provided.");
|
||||
|
||||
if (option.type === OptionType.NUMBER && BigInt(newValue) >= MAX_SAFE_NUMBER) {
|
||||
setState(`${Number.MAX_SAFE_INTEGER}`);
|
||||
onChange(serialize(newValue));
|
||||
} else {
|
||||
setState(newValue);
|
||||
onChange(serialize(newValue));
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<SettingsSection name={id} description={option.description} error={error}>
|
||||
<Forms.FormSection>
|
||||
<Forms.FormTitle>{wordsToTitle(wordsFromCamel(id))}</Forms.FormTitle>
|
||||
<Forms.FormText className={Margins.bottom20} type="description">{option.description}</Forms.FormText>
|
||||
<TextInput
|
||||
type="number"
|
||||
pattern="-?[0-9]+"
|
||||
placeholder={option.placeholder ?? "Enter a number"}
|
||||
value={state}
|
||||
onChange={handleChange}
|
||||
placeholder={option.placeholder ?? "Enter a number"}
|
||||
disabled={option.disabled?.call(definedSettings) ?? false}
|
||||
{...option.componentProps}
|
||||
/>
|
||||
</SettingsSection>
|
||||
{error && <Forms.FormText style={{ color: "var(--text-danger)" }}>{error}</Forms.FormText>}
|
||||
</Forms.FormSection>
|
||||
);
|
||||
}
|
||||
|
|
@ -16,41 +16,50 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Margins } from "@utils/margins";
|
||||
import { wordsFromCamel, wordsToTitle } from "@utils/text";
|
||||
import { PluginOptionSelect } from "@utils/types";
|
||||
import { React, Select, useState } from "@webpack/common";
|
||||
import { Forms, React, Select } from "@webpack/common";
|
||||
|
||||
import { resolveError, SettingProps, SettingsSection } from "./Common";
|
||||
import { ISettingElementProps } from ".";
|
||||
|
||||
export function SelectSetting({ option, pluginSettings, definedSettings, onChange, id }: SettingProps<PluginOptionSelect>) {
|
||||
export function SettingSelectComponent({ option, pluginSettings, definedSettings, onChange, onError, id }: ISettingElementProps<PluginOptionSelect>) {
|
||||
const def = pluginSettings[id] ?? option.options?.find(o => o.default)?.value;
|
||||
|
||||
const [state, setState] = useState<any>(def ?? null);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [state, setState] = React.useState<any>(def ?? null);
|
||||
const [error, setError] = React.useState<string | null>(null);
|
||||
|
||||
function handleChange(newValue: any) {
|
||||
React.useEffect(() => {
|
||||
onError(error !== null);
|
||||
}, [error]);
|
||||
|
||||
function handleChange(newValue) {
|
||||
const isValid = option.isValid?.call(definedSettings, newValue) ?? true;
|
||||
|
||||
setState(newValue);
|
||||
setError(resolveError(isValid));
|
||||
|
||||
if (isValid === true) {
|
||||
if (typeof isValid === "string") setError(isValid);
|
||||
else if (!isValid) setError("Invalid input provided.");
|
||||
else {
|
||||
setError(null);
|
||||
setState(newValue);
|
||||
onChange(newValue);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<SettingsSection name={id} description={option.description} error={error}>
|
||||
<Forms.FormSection>
|
||||
<Forms.FormTitle>{wordsToTitle(wordsFromCamel(id))}</Forms.FormTitle>
|
||||
<Forms.FormText className={Margins.bottom16} type="description">{option.description}</Forms.FormText>
|
||||
<Select
|
||||
placeholder={option.placeholder ?? "Select an option"}
|
||||
isDisabled={option.disabled?.call(definedSettings) ?? false}
|
||||
options={option.options}
|
||||
placeholder={option.placeholder ?? "Select an option"}
|
||||
maxVisibleItems={5}
|
||||
closeOnSelect={true}
|
||||
select={handleChange}
|
||||
isSelected={v => v === state}
|
||||
serialize={v => String(v)}
|
||||
isDisabled={option.disabled?.call(definedSettings) ?? false}
|
||||
{...option.componentProps}
|
||||
/>
|
||||
</SettingsSection>
|
||||
{error && <Forms.FormText style={{ color: "var(--text-danger)" }}>{error}</Forms.FormText>}
|
||||
</Forms.FormSection>
|
||||
);
|
||||
}
|
||||
|
|
@ -16,29 +16,46 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Margins } from "@utils/margins";
|
||||
import { wordsFromCamel, wordsToTitle } from "@utils/text";
|
||||
import { PluginOptionSlider } from "@utils/types";
|
||||
import { React, Slider, useState } from "@webpack/common";
|
||||
import { Forms, React, Slider } from "@webpack/common";
|
||||
|
||||
import { resolveError, SettingProps, SettingsSection } from "./Common";
|
||||
import { ISettingElementProps } from ".";
|
||||
|
||||
export function SliderSetting({ option, pluginSettings, definedSettings, id, onChange }: SettingProps<PluginOptionSlider>) {
|
||||
export function makeRange(start: number, end: number, step = 1) {
|
||||
const ranges: number[] = [];
|
||||
for (let value = start; value <= end; value += step) {
|
||||
ranges.push(Math.round(value * 100) / 100);
|
||||
}
|
||||
return ranges;
|
||||
}
|
||||
|
||||
export function SettingSliderComponent({ option, pluginSettings, definedSettings, id, onChange, onError }: ISettingElementProps<PluginOptionSlider>) {
|
||||
const def = pluginSettings[id] ?? option.default;
|
||||
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [error, setError] = React.useState<string | null>(null);
|
||||
|
||||
React.useEffect(() => {
|
||||
onError(error !== null);
|
||||
}, [error]);
|
||||
|
||||
function handleChange(newValue: number): void {
|
||||
const isValid = option.isValid?.call(definedSettings, newValue) ?? true;
|
||||
|
||||
setError(resolveError(isValid));
|
||||
|
||||
if (isValid === true) {
|
||||
if (typeof isValid === "string") setError(isValid);
|
||||
else if (!isValid) setError("Invalid input provided.");
|
||||
else {
|
||||
setError(null);
|
||||
onChange(newValue);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<SettingsSection name={id} description={option.description} error={error}>
|
||||
<Forms.FormSection>
|
||||
<Forms.FormTitle>{wordsToTitle(wordsFromCamel(id))}</Forms.FormTitle>
|
||||
<Forms.FormText className={Margins.bottom20} type="description">{option.description}</Forms.FormText>
|
||||
<Slider
|
||||
disabled={option.disabled?.call(definedSettings) ?? false}
|
||||
markers={option.markers}
|
||||
minValue={option.markers[0]}
|
||||
maxValue={option.markers[option.markers.length - 1]}
|
||||
|
|
@ -46,10 +63,9 @@ export function SliderSetting({ option, pluginSettings, definedSettings, id, onC
|
|||
onValueChange={handleChange}
|
||||
onValueRender={(v: number) => String(v.toFixed(2))}
|
||||
stickToMarkers={option.stickToMarkers ?? true}
|
||||
disabled={option.disabled?.call(definedSettings) ?? false}
|
||||
{...option.componentProps}
|
||||
/>
|
||||
</SettingsSection>
|
||||
</Forms.FormSection>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -16,37 +16,45 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Margins } from "@utils/margins";
|
||||
import { wordsFromCamel, wordsToTitle } from "@utils/text";
|
||||
import { PluginOptionString } from "@utils/types";
|
||||
import { React, TextInput, useState } from "@webpack/common";
|
||||
import { Forms, React, TextInput } from "@webpack/common";
|
||||
|
||||
import { resolveError, SettingProps, SettingsSection } from "./Common";
|
||||
import { ISettingElementProps } from ".";
|
||||
|
||||
export function TextSetting({ option, pluginSettings, definedSettings, id, onChange }: SettingProps<PluginOptionString>) {
|
||||
const [state, setState] = useState(pluginSettings[id] ?? option.default ?? null);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
export function SettingTextComponent({ option, pluginSettings, definedSettings, id, onChange, onError }: ISettingElementProps<PluginOptionString>) {
|
||||
const [state, setState] = React.useState(pluginSettings[id] ?? option.default ?? null);
|
||||
const [error, setError] = React.useState<string | null>(null);
|
||||
|
||||
function handleChange(newValue: string) {
|
||||
React.useEffect(() => {
|
||||
onError(error !== null);
|
||||
}, [error]);
|
||||
|
||||
function handleChange(newValue) {
|
||||
const isValid = option.isValid?.call(definedSettings, newValue) ?? true;
|
||||
if (typeof isValid === "string") setError(isValid);
|
||||
else if (!isValid) setError("Invalid input provided.");
|
||||
else setError(null);
|
||||
|
||||
setState(newValue);
|
||||
setError(resolveError(isValid));
|
||||
|
||||
if (isValid === true) {
|
||||
onChange(newValue);
|
||||
}
|
||||
onChange(newValue);
|
||||
}
|
||||
|
||||
return (
|
||||
<SettingsSection name={id} description={option.description} error={error}>
|
||||
<Forms.FormSection>
|
||||
<Forms.FormTitle>{wordsToTitle(wordsFromCamel(id))}</Forms.FormTitle>
|
||||
<Forms.FormText className={Margins.bottom20} type="description">{option.description}</Forms.FormText>
|
||||
<TextInput
|
||||
type="text"
|
||||
placeholder={option.placeholder ?? "Enter a value"}
|
||||
value={state}
|
||||
onChange={handleChange}
|
||||
maxLength={null}
|
||||
placeholder={option.placeholder ?? "Enter a value"}
|
||||
disabled={option.disabled?.call(definedSettings) ?? false}
|
||||
maxLength={null}
|
||||
{...option.componentProps}
|
||||
/>
|
||||
</SettingsSection>
|
||||
{error && <Forms.FormText style={{ color: "var(--text-danger)" }}>{error}</Forms.FormText>}
|
||||
</Forms.FormSection>
|
||||
);
|
||||
}
|
||||
43
src/components/PluginSettings/components/index.ts
Normal file
43
src/components/PluginSettings/components/index.ts
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* 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 { DefinedSettings, PluginOptionBase } from "@utils/types";
|
||||
|
||||
interface ISettingElementPropsBase<T> {
|
||||
option: T;
|
||||
onChange(newValue: any): void;
|
||||
pluginSettings: {
|
||||
[setting: string]: any;
|
||||
enabled: boolean;
|
||||
};
|
||||
id: string;
|
||||
onError(hasError: boolean): void;
|
||||
definedSettings?: DefinedSettings;
|
||||
}
|
||||
|
||||
export type ISettingElementProps<T extends PluginOptionBase> = ISettingElementPropsBase<T>;
|
||||
export type ISettingCustomElementProps<T extends Omit<PluginOptionBase, "description" | "placeholder">> = ISettingElementPropsBase<T>;
|
||||
|
||||
export * from "../../Badge";
|
||||
export * from "./SettingBooleanComponent";
|
||||
export * from "./SettingCustomComponent";
|
||||
export * from "./SettingNumericComponent";
|
||||
export * from "./SettingSelectComponent";
|
||||
export * from "./SettingSliderComponent";
|
||||
export * from "./SettingTextComponent";
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue