Compare commits
226 Commits
v1.5.0
...
feat/telem
Author | SHA1 | Date | |
---|---|---|---|
|
5d1736d020 | ||
|
3de02708a6 | ||
|
49c331fcc9 | ||
|
ba53acdca7 | ||
|
bfb48b4faf | ||
|
9dd8e72245 | ||
|
aae790f1c1 | ||
|
7f17e70697 | ||
|
b3311c6f12 | ||
|
bc09225258 | ||
|
9ce923d4d7 | ||
|
7845af0802 | ||
|
89672882b9 | ||
|
e05c630a54 | ||
|
38834ef7ac | ||
|
98d49af728 | ||
|
0afe319141 | ||
|
a9e67e2955 | ||
|
1676956f61 | ||
|
8692109bc5 | ||
|
0847f205b8 | ||
|
2f94e167c4 | ||
|
589c070773 | ||
|
8567ff6239 | ||
|
e4701769a5 | ||
|
25f101602d | ||
|
85bfa1e719 | ||
|
b48998d485 | ||
|
6d605050e1 | ||
|
07c4a097e0 | ||
|
c1de41436a | ||
|
64c6f5740f | ||
|
03523446c1 | ||
|
25dc25c707 | ||
|
8ac8048845 | ||
|
ffe6bb1c5d | ||
|
74faaa216f | ||
|
a6b8b59d5c | ||
|
9eaeb24196 | ||
|
6b28e5ad85 | ||
|
d852393cfb | ||
|
0c12500c0a | ||
|
1502024440 | ||
|
45fa4f89c6 | ||
|
96dff84ed8 | ||
|
c1b8739104 | ||
|
2183d2d29d | ||
|
722dcb033e | ||
|
53cd14844f | ||
|
38daf6ec2b | ||
|
635b80c58b | ||
|
ec9e111047 | ||
|
d54f8b5e8e | ||
|
09a922a01c | ||
|
23963f40c2 | ||
|
a41abfef31 | ||
|
ddee3e1264 | ||
|
6c4afa52a3 | ||
|
ce6081c39b | ||
|
123853e848 | ||
|
0c6445b66b | ||
|
7094345516 | ||
|
148a32096c | ||
|
ab1b002ed1 | ||
|
c7a20769f9 | ||
|
9c13befcb6 | ||
|
3fdd0edf14 | ||
|
2169090ce5 | ||
|
5e13de72c2 | ||
|
3a7c27253b | ||
|
465a87f66b | ||
|
13c59f47cb | ||
|
0d7157dd20 | ||
|
88c6e2a0e9 | ||
|
535d510d3b | ||
|
cbc23b1bdd | ||
|
aa2a57b733 | ||
|
c3ccbbfa99 | ||
|
685f44d40f | ||
|
8f0009778a | ||
|
f385dc380e | ||
|
bb900785ed | ||
|
61fac0a8f1 | ||
|
1a607639cc | ||
|
c90440f031 | ||
|
baf952512c | ||
|
fbc5038306 | ||
|
ddc39fe84d | ||
|
4f8c75372c | ||
|
024a77c577 | ||
|
1130521e4b | ||
|
3bd657611c | ||
|
b659d4e9c1 | ||
|
9c60b38acc | ||
|
c5dd50ad8f | ||
|
131e91a37c | ||
|
06b4dffa62 | ||
|
3917193e8f | ||
|
8d1561aed4 | ||
|
cf3c28e1ff | ||
|
af1aa39647 | ||
|
7de1b5dcb6 | ||
|
cd06980016 | ||
|
e69236c5f8 | ||
|
2478ffb695 | ||
|
788d22f9e9 | ||
|
b69ac1cdad | ||
|
892a79b2a7 | ||
|
9895540943 | ||
|
7e0fc1d0ea | ||
|
bb771153e3 | ||
|
65d39dcf21 | ||
|
cf5e93ee52 | ||
|
4d8e4e62ca | ||
|
cb2532d22c | ||
|
8c998b9330 | ||
|
fb22c57da1 | ||
|
554bb18e9b | ||
|
44c9675795 | ||
|
322ecc5e88 | ||
|
7ee9a8bb99 | ||
|
69b54535c3 | ||
|
2a56081bc2 | ||
|
baa7d8c078 | ||
|
940193c30b | ||
|
09b646b860 | ||
|
922e3ce6fe | ||
|
cb93c11e16 | ||
|
4beef9f73b | ||
|
4da79abb21 | ||
|
4e27722b54 | ||
|
97c0face2f | ||
|
1a1d9b07e8 | ||
|
4a2def03e7 | ||
|
544edce9f9 | ||
|
e4485165d0 | ||
|
fada76ec81 | ||
|
f659c46031 | ||
|
ae1dc4eab0 | ||
|
fe60a72b80 | ||
|
5a0b2ee3f5 | ||
|
6c1b8b0d8a | ||
|
b2a1410a96 | ||
|
c25c95eecd | ||
|
d94418f42f | ||
|
da1a8cdd67 | ||
|
5d7ede34d8 | ||
|
cd61354998 | ||
|
a452945ac8 | ||
|
b577660800 | ||
|
4f57c7eded | ||
|
e3e5da10a9 | ||
|
998ce72f3b | ||
|
188d12d1a3 | ||
|
a522eab40d | ||
|
c2721f158f | ||
|
61cd7b4d99 | ||
|
926af0d1cd | ||
|
dcaf4aec97 | ||
|
5a97adb435 | ||
|
925d709335 | ||
|
1a36dbbc9b | ||
|
b59db2f8c2 | ||
|
d81302f64c | ||
|
390987e4a9 | ||
|
377cf60055 | ||
|
34ac718705 | ||
|
e4659ed7c3 | ||
|
c33d59b45d | ||
|
ac1b67ccbd | ||
|
c0f2c97458 | ||
|
664dd0a992 | ||
|
f66e35b658 | ||
|
df214e1e93 | ||
|
47a39a062e | ||
|
9e63da6d78 | ||
|
79295683ee | ||
|
5eb9dd04df | ||
|
03b5dc9c27 | ||
|
726a1b5d96 | ||
|
581fe252a4 | ||
|
30b2e88e77 | ||
|
1f38a8eeab | ||
|
6db9721c06 | ||
|
9891791fa7 | ||
|
8dd5eeead2 | ||
|
abf8667a5d | ||
|
e33ac900bc | ||
|
8a026060c7 | ||
|
c8b77bb187 | ||
|
88b06191b9 | ||
|
62277770a8 | ||
|
4facc3cad7 | ||
|
837d1fc083 | ||
|
608a67c9ae | ||
|
f32d25b641 | ||
|
4c7a2ba340 | ||
|
da7f0cfff6 | ||
|
ae6584da7c | ||
|
c6b1b9463c | ||
|
376aaf39ce | ||
|
5454a41243 | ||
|
044f64e446 | ||
|
0b7fca864a | ||
|
30ac256070 | ||
|
d0e2a32471 | ||
|
ec026ca34c | ||
|
4baaa9bd91 | ||
|
d9933c5793 | ||
|
2735037a67 | ||
|
83bfe28fa4 | ||
|
08c5d23636 | ||
|
ac0f834155 | ||
|
fa16e1b56f | ||
|
cfca393f2b | ||
|
eacc673bcc | ||
|
dba6c4cea6 | ||
|
6b10947d06 | ||
|
97b6699afe | ||
|
7e91edc757 | ||
|
a82544e93e | ||
|
d8c8b74ed7 | ||
|
fadd1598f5 | ||
|
e5c0898dd6 | ||
|
9550b74b2a | ||
|
6cfb67a52e |
@ -51,7 +51,10 @@
|
|||||||
"eqeqeq": ["error", "always", { "null": "ignore" }],
|
"eqeqeq": ["error", "always", { "null": "ignore" }],
|
||||||
"spaced-comment": ["error", "always", { "markers": ["!"] }],
|
"spaced-comment": ["error", "always", { "markers": ["!"] }],
|
||||||
"yoda": "error",
|
"yoda": "error",
|
||||||
"prefer-destructuring": ["error", { "object": true, "array": false }],
|
"prefer-destructuring": ["error", {
|
||||||
|
"VariableDeclarator": { "array": false, "object": true },
|
||||||
|
"AssignmentExpression": { "array": false, "object": false }
|
||||||
|
}],
|
||||||
"operator-assignment": ["error", "always"],
|
"operator-assignment": ["error", "always"],
|
||||||
"no-useless-computed-key": "error",
|
"no-useless-computed-key": "error",
|
||||||
"no-unneeded-ternary": ["error", { "defaultAssignment": false }],
|
"no-unneeded-ternary": ["error", { "defaultAssignment": false }],
|
||||||
|
30
.github/ISSUE_TEMPLATE/blank.yml
vendored
Normal file
30
.github/ISSUE_TEMPLATE/blank.yml
vendored
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
name: Blank Issue
|
||||||
|
description: Create a blank issue. ALWAYS FIRST USE OUR SUPPORT CHANNEL! ONLY USE THIS FORM IF YOU ARE A CONTRIBUTOR OR WERE TOLD TO DO SO IN THE SUPPORT CHANNEL.
|
||||||
|
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
# READ THIS BEFORE OPENING AN ISSUE
|
||||||
|
|
||||||
|
This form is ONLY FOR DEVELOPERS. YOUR ISSUE WILL BE CLOSED AND YOU WILL POSSIBLY BE BLOCKED FROM THE REPOSITORY IF YOU IGNORE THIS.
|
||||||
|
|
||||||
|
DO NOT USE THIS FORM, unless
|
||||||
|
- you are a vencord contributor
|
||||||
|
- you were given explicit permission to use this form by a moderator in our support server
|
||||||
|
- you are filing a security related report
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: content
|
||||||
|
attributes:
|
||||||
|
label: Content
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: checkboxes
|
||||||
|
id: agreement-check
|
||||||
|
attributes:
|
||||||
|
label: Request Agreement
|
||||||
|
options:
|
||||||
|
- label: I have read the requirements for opening an issue above
|
||||||
|
required: true
|
16
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
16
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@ -1,9 +1,21 @@
|
|||||||
name: Bug/Crash Report
|
name: Bug/Crash Report
|
||||||
description: Create a bug or crash report for Vencord
|
description: Create a bug or crash report for Vencord. ALWAYS FIRST USE OUR SUPPORT CHANNEL! ONLY USE THIS FORM IF YOU ARE A CONTRIBUTOR OR WERE TOLD TO DO SO IN THE SUPPORT CHANNEL.
|
||||||
labels: [bug]
|
labels: [bug]
|
||||||
title: "[Bug] <title>"
|
title: "[Bug] <title>"
|
||||||
|
|
||||||
body:
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
# READ THIS BEFORE OPENING AN ISSUE
|
||||||
|
|
||||||
|
This form is ONLY FOR DEVELOPERS. YOUR ISSUE WILL BE CLOSED AND YOU WILL POSSIBLY BE BLOCKED FROM THE REPOSITORY IF YOU IGNORE THIS.
|
||||||
|
|
||||||
|
DO NOT USE THIS FORM, unless
|
||||||
|
- you are a vencord contributor
|
||||||
|
- you were given explicit permission to use this form by a moderator in our support server
|
||||||
|
- you are filing a security related report
|
||||||
|
|
||||||
- type: input
|
- type: input
|
||||||
id: discord
|
id: discord
|
||||||
attributes:
|
attributes:
|
||||||
@ -64,3 +76,5 @@ body:
|
|||||||
options:
|
options:
|
||||||
- label: I am using Discord Stable or tried on Stable and this bug happens there as well
|
- label: I am using Discord Stable or tried on Stable and this bug happens there as well
|
||||||
required: true
|
required: true
|
||||||
|
- label: I have read the requirements for opening an issue above
|
||||||
|
required: true
|
||||||
|
2
.github/ISSUE_TEMPLATE/config.yml
vendored
2
.github/ISSUE_TEMPLATE/config.yml
vendored
@ -1,4 +1,4 @@
|
|||||||
blank_issues_enabled: true
|
blank_issues_enabled: false
|
||||||
contact_links:
|
contact_links:
|
||||||
- name: Vencord Support Server
|
- name: Vencord Support Server
|
||||||
url: https://discord.gg/D9uwnFnqmd
|
url: https://discord.gg/D9uwnFnqmd
|
||||||
|
32
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
32
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@ -1,32 +0,0 @@
|
|||||||
name: Feature Request
|
|
||||||
description: Create a feature request for Vencord. To request new plugins, please use the Discussions tab
|
|
||||||
labels: [enhancement]
|
|
||||||
title: "[Feature Request] <title>"
|
|
||||||
|
|
||||||
body:
|
|
||||||
- type: input
|
|
||||||
id: discord
|
|
||||||
attributes:
|
|
||||||
label: Discord Account
|
|
||||||
description: Who on Discord is making this request? Not required but encouraged for easier follow-up
|
|
||||||
placeholder: username#0000
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
id: feature-basic-description
|
|
||||||
attributes:
|
|
||||||
label: What is it that you'd like to see?
|
|
||||||
description: Describe the feature you want added as detailed as possible
|
|
||||||
placeholder: I think ... would be a cool feature to add. This would be awesome, thanks!
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: checkboxes
|
|
||||||
id: agreement-check
|
|
||||||
attributes:
|
|
||||||
label: Request Agreement
|
|
||||||
description: DO NOT USE THIS TEMPLATE FOR PLUGIN REQUESTS!!! For plugin requests, **use discussions**
|
|
||||||
options:
|
|
||||||
- label: This is not a plugin request
|
|
||||||
required: true
|
|
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@ -42,7 +42,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Clean up obsolete files
|
- name: Clean up obsolete files
|
||||||
run: |
|
run: |
|
||||||
rm -rf dist/*-unpacked Vencord.user.css vencordDesktopRenderer.css vencordDesktopRenderer.css.map
|
rm -rf dist/*-unpacked dist/monaco Vencord.user.css vencordDesktopRenderer.css vencordDesktopRenderer.css.map
|
||||||
|
|
||||||
- name: Get some values needed for the release
|
- name: Get some values needed for the release
|
||||||
id: release_values
|
id: release_values
|
||||||
|
18
.github/workflows/publish.yml
vendored
18
.github/workflows/publish.yml
vendored
@ -36,26 +36,10 @@ jobs:
|
|||||||
|
|
||||||
- name: Publish extension
|
- name: Publish extension
|
||||||
run: |
|
run: |
|
||||||
# Do not fail so that even if chrome fails, firefox gets a shot. But also store exit code to fail workflow later
|
|
||||||
EXIT_CODE=0
|
|
||||||
|
|
||||||
# Chrome
|
|
||||||
cd dist/chromium-unpacked
|
cd dist/chromium-unpacked
|
||||||
pnpx chrome-webstore-upload-cli@2.1.0 upload --auto-publish || EXIT_CODE=$?
|
pnpx chrome-webstore-upload-cli@2.1.0 upload --auto-publish
|
||||||
|
|
||||||
# Firefox
|
|
||||||
cd ../firefox-unpacked
|
|
||||||
npm i -g web-ext@7.4.0 web-ext-submit@7.4.0
|
|
||||||
web-ext-submit || EXIT_CODE=$?
|
|
||||||
|
|
||||||
exit $EXIT_CODE
|
|
||||||
env:
|
env:
|
||||||
# Chrome
|
|
||||||
EXTENSION_ID: ${{ secrets.CHROME_EXTENSION_ID }}
|
EXTENSION_ID: ${{ secrets.CHROME_EXTENSION_ID }}
|
||||||
CLIENT_ID: ${{ secrets.CHROME_CLIENT_ID }}
|
CLIENT_ID: ${{ secrets.CHROME_CLIENT_ID }}
|
||||||
CLIENT_SECRET: ${{ secrets.CHROME_CLIENT_SECRET }}
|
CLIENT_SECRET: ${{ secrets.CHROME_CLIENT_SECRET }}
|
||||||
REFRESH_TOKEN: ${{ secrets.CHROME_REFRESH_TOKEN }}
|
REFRESH_TOKEN: ${{ secrets.CHROME_REFRESH_TOKEN }}
|
||||||
|
|
||||||
# Firefox
|
|
||||||
WEB_EXT_API_KEY: ${{ secrets.WEBEXT_USER }}
|
|
||||||
WEB_EXT_API_SECRET: ${{ secrets.WEBEXT_SECRET }}
|
|
||||||
|
24
README.md
24
README.md
@ -22,29 +22,7 @@ The cutest Discord client mod
|
|||||||
|
|
||||||
## Installing / Uninstalling
|
## Installing / Uninstalling
|
||||||
|
|
||||||
Click the below button to install Vencord to the Discord Desktop app
|
Visit https://vencord.dev/download
|
||||||
|
|
||||||
[![Download and run the Installer](https://img.shields.io/github/v/release/Vencord/Installer?label=Download%20Vencord%20Installer&style=for-the-badge)](https://github.com/Vencord/Installer#vencord-installer)
|
|
||||||
|
|
||||||
## Installing on Browser
|
|
||||||
|
|
||||||
[![Get it on the Firefox Webstore](https://blog.mozilla.org/addons/files/2015/11/get-the-addon.png)](https://addons.mozilla.org/en-GB/firefox/addon/vencord-web/) [![Get it on the Chrome Webstore](https://storage.googleapis.com/web-dev-uploads/image/WlD8wC6g8khYWPJUsQceQkhXSlv1/UV4C4ybeBTsZt43U4xis.png)](https://chrome.google.com/webstore/detail/vencord-web/cbghhgpcnddeihccjmnadmkaejncjndb)
|
|
||||||
|
|
||||||
Or use the [UserScript](https://raw.githubusercontent.com/Vencord/builds/main/Vencord.user.js) - Please note that the CSS Editor, Themes loaded from remote sources and co. will not work in the UserScript. Use the extension if you need any of those
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>Alternative Downloads</summary>
|
|
||||||
|
|
||||||
## Vencord Desktop
|
|
||||||
|
|
||||||
> **Warning**
|
|
||||||
> This is an alternative app. It currently doesn't support keybinds and possibly some more features. If you just want to install to the normal Discord Desktop app, scroll up
|
|
||||||
|
|
||||||
As an alternative to the Discord Desktop app, Vencord also has its own standalone Desktop app that is snappier and lighter than Discord's official Desktop app
|
|
||||||
|
|
||||||
[![Download Vencord Desktop](https://img.shields.io/github/v/release/Vencord/Desktop?label=Download%20Vencord%20Desktop&style=for-the-badge)](https://github.com/Vencord/Desktop#vencord-desktop)
|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|
||||||
## Join our Support/Community Server
|
## Join our Support/Community Server
|
||||||
|
|
||||||
|
@ -48,7 +48,8 @@ window.VencordNative = {
|
|||||||
getThemesList: () => DataStore.entries(themeStore).then(entries =>
|
getThemesList: () => DataStore.entries(themeStore).then(entries =>
|
||||||
entries.map(([name, css]) => getThemeInfo(css, name.toString()))
|
entries.map(([name, css]) => getThemeInfo(css, name.toString()))
|
||||||
),
|
),
|
||||||
getThemeData: (fileName: string) => DataStore.get(fileName, themeStore)
|
getThemeData: (fileName: string) => DataStore.get(fileName, themeStore),
|
||||||
|
getSystemValues: async () => ({}),
|
||||||
},
|
},
|
||||||
|
|
||||||
native: {
|
native: {
|
||||||
|
21
browser/third-party/rnnoise/LICENSE
vendored
21
browser/third-party/rnnoise/LICENSE
vendored
@ -1,21 +0,0 @@
|
|||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2022 翠 / green
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
BIN
browser/third-party/rnnoise/rnnoise.wasm
vendored
BIN
browser/third-party/rnnoise/rnnoise.wasm
vendored
Binary file not shown.
File diff suppressed because one or more lines are too long
BIN
browser/third-party/rnnoise/rnnoise_simd.wasm
vendored
BIN
browser/third-party/rnnoise/rnnoise_simd.wasm
vendored
Binary file not shown.
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "vencord",
|
"name": "vencord",
|
||||||
"private": "true",
|
"private": "true",
|
||||||
"version": "1.5.0",
|
"version": "1.6.1",
|
||||||
"description": "The cutest Discord client mod",
|
"description": "The cutest Discord client mod",
|
||||||
"homepage": "https://github.com/Vendicated/Vencord#readme",
|
"homepage": "https://github.com/Vendicated/Vencord#readme",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
@ -94,7 +94,7 @@
|
|||||||
"build": {
|
"build": {
|
||||||
"overwriteDest": true
|
"overwriteDest": true
|
||||||
},
|
},
|
||||||
"sourceDir": "./dist/extension-v2-unpacked"
|
"sourceDir": "./dist/firefox-unpacked"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18",
|
"node": ">=18",
|
||||||
|
@ -25,6 +25,8 @@ const defines = {
|
|||||||
IS_STANDALONE: isStandalone,
|
IS_STANDALONE: isStandalone,
|
||||||
IS_DEV: JSON.stringify(watch),
|
IS_DEV: JSON.stringify(watch),
|
||||||
IS_UPDATER_DISABLED: updaterDisabled,
|
IS_UPDATER_DISABLED: updaterDisabled,
|
||||||
|
IS_WEB: false,
|
||||||
|
IS_EXTENSION: false,
|
||||||
VERSION: JSON.stringify(VERSION),
|
VERSION: JSON.stringify(VERSION),
|
||||||
BUILD_TIMESTAMP,
|
BUILD_TIMESTAMP,
|
||||||
};
|
};
|
||||||
@ -41,7 +43,7 @@ const nodeCommonOpts = {
|
|||||||
format: "cjs",
|
format: "cjs",
|
||||||
platform: "node",
|
platform: "node",
|
||||||
target: ["esnext"],
|
target: ["esnext"],
|
||||||
external: ["electron", ...commonOpts.external],
|
external: ["electron", "original-fs", ...commonOpts.external],
|
||||||
define: defines,
|
define: defines,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -77,8 +79,6 @@ await Promise.all([
|
|||||||
],
|
],
|
||||||
define: {
|
define: {
|
||||||
...defines,
|
...defines,
|
||||||
IS_WEB: false,
|
|
||||||
IS_EXTENSION: false,
|
|
||||||
IS_DISCORD_DESKTOP: true,
|
IS_DISCORD_DESKTOP: true,
|
||||||
IS_VESKTOP: false
|
IS_VESKTOP: false
|
||||||
}
|
}
|
||||||
@ -124,8 +124,6 @@ await Promise.all([
|
|||||||
],
|
],
|
||||||
define: {
|
define: {
|
||||||
...defines,
|
...defines,
|
||||||
IS_WEB: false,
|
|
||||||
IS_EXTENSION: false,
|
|
||||||
IS_DISCORD_DESKTOP: false,
|
IS_DISCORD_DESKTOP: false,
|
||||||
IS_VESKTOP: true
|
IS_VESKTOP: true
|
||||||
}
|
}
|
||||||
|
@ -57,6 +57,13 @@ const MonacoWorkerEntryPoints = [
|
|||||||
"vs/editor/editor.worker.js"
|
"vs/editor/editor.worker.js"
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const RnNoiseFiles = [
|
||||||
|
"dist/rnnoise.wasm",
|
||||||
|
"dist/rnnoise_simd.wasm",
|
||||||
|
"dist/rnnoise/workletProcessor.js",
|
||||||
|
"LICENSE"
|
||||||
|
];
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
[
|
[
|
||||||
esbuild.build({
|
esbuild.build({
|
||||||
@ -143,7 +150,9 @@ async function buildExtension(target, files) {
|
|||||||
"dist/Vencord.js": await readFile("dist/extension.js"),
|
"dist/Vencord.js": await readFile("dist/extension.js"),
|
||||||
"dist/Vencord.css": await readFile("dist/extension.css"),
|
"dist/Vencord.css": await readFile("dist/extension.css"),
|
||||||
...await loadDir("dist/monaco"),
|
...await loadDir("dist/monaco"),
|
||||||
...await loadDir("browser/third-party", "browser/"),
|
...Object.fromEntries(await Promise.all(RnNoiseFiles.map(async file =>
|
||||||
|
[`third-party/rnnoise/${file.replace(/^dist\//, "")}`, await readFile(`node_modules/@sapphi-red/web-noise-suppressor/${file}`)]
|
||||||
|
))),
|
||||||
...Object.fromEntries(await Promise.all(files.map(async f => {
|
...Object.fromEntries(await Promise.all(files.map(async f => {
|
||||||
let content = await readFile(join("browser", f));
|
let content = await readFile(join("browser", f));
|
||||||
if (f.startsWith("manifest")) {
|
if (f.startsWith("manifest")) {
|
||||||
@ -189,5 +198,8 @@ await Promise.all([
|
|||||||
buildExtension("firefox-unpacked", ["background.js", "content.js", "manifestv2.json", "icon.png"]),
|
buildExtension("firefox-unpacked", ["background.js", "content.js", "manifestv2.json", "icon.png"]),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
Zip.sync.zip("dist/chromium-unpacked").compress().save("dist/extension.zip");
|
Zip.sync.zip("dist/chromium-unpacked").compress().save("dist/extension-chrome.zip");
|
||||||
console.info("Packed Chromium Extension written to dist/extension.zip");
|
console.info("Packed Chromium Extension written to dist/extension-chrome.zip");
|
||||||
|
|
||||||
|
Zip.sync.zip("dist/firefox-unpacked").compress().save("dist/extension-firefox.zip");
|
||||||
|
console.info("Packed Firefox Extension written to dist/extension-firefox.zip");
|
||||||
|
@ -18,7 +18,8 @@
|
|||||||
|
|
||||||
import { Dirent, readdirSync, readFileSync, writeFileSync } from "fs";
|
import { Dirent, readdirSync, readFileSync, writeFileSync } from "fs";
|
||||||
import { access, readFile } from "fs/promises";
|
import { access, readFile } from "fs/promises";
|
||||||
import { join } from "path";
|
import { join, sep } from "path";
|
||||||
|
import { normalize as posixNormalize, sep as posixSep } from "path/posix";
|
||||||
import { BigIntLiteral, createSourceFile, Identifier, isArrayLiteralExpression, isCallExpression, isExportAssignment, isIdentifier, isObjectLiteralExpression, isPropertyAccessExpression, isPropertyAssignment, isSatisfiesExpression, isStringLiteral, isVariableStatement, NamedDeclaration, NodeArray, ObjectLiteralExpression, ScriptTarget, StringLiteral, SyntaxKind } from "typescript";
|
import { BigIntLiteral, createSourceFile, Identifier, isArrayLiteralExpression, isCallExpression, isExportAssignment, isIdentifier, isObjectLiteralExpression, isPropertyAccessExpression, isPropertyAssignment, isSatisfiesExpression, isStringLiteral, isVariableStatement, NamedDeclaration, NodeArray, ObjectLiteralExpression, ScriptTarget, StringLiteral, SyntaxKind } from "typescript";
|
||||||
|
|
||||||
import { getPluginTarget } from "./utils.mjs";
|
import { getPluginTarget } from "./utils.mjs";
|
||||||
@ -39,6 +40,7 @@ interface PluginData {
|
|||||||
required: boolean;
|
required: boolean;
|
||||||
enabledByDefault: boolean;
|
enabledByDefault: boolean;
|
||||||
target: "discordDesktop" | "vencordDesktop" | "web" | "dev";
|
target: "discordDesktop" | "vencordDesktop" | "web" | "dev";
|
||||||
|
filePath: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const devs = {} as Record<string, Dev>;
|
const devs = {} as Record<string, Dev>;
|
||||||
@ -165,6 +167,12 @@ async function parseFile(fileName: string) {
|
|||||||
data.target = target as any;
|
data.target = target as any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data.filePath = posixNormalize(fileName)
|
||||||
|
.split(sep)
|
||||||
|
.join(posixSep)
|
||||||
|
.replace(/\/index\.([jt]sx?)$/, "")
|
||||||
|
.replace(/^src\/plugins\//, "");
|
||||||
|
|
||||||
let readme = "";
|
let readme = "";
|
||||||
try {
|
try {
|
||||||
readme = readFileSync(join(fileName, "..", "README.md"), "utf-8");
|
readme = readFileSync(join(fileName, "..", "README.md"), "utf-8");
|
||||||
|
@ -61,6 +61,13 @@ const report = {
|
|||||||
otherErrors: [] as string[]
|
otherErrors: [] as string[]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const IGNORED_DISCORD_ERRORS = [
|
||||||
|
"KeybindStore: Looking for callback action",
|
||||||
|
"Unable to process domain list delta: Client revision number is null",
|
||||||
|
"Downloading the full bad domains file",
|
||||||
|
/\[GatewaySocket\].{0,110}Cannot access '/
|
||||||
|
] as Array<string | RegExp>;
|
||||||
|
|
||||||
function toCodeBlock(s: string) {
|
function toCodeBlock(s: string) {
|
||||||
s = s.replace(/```/g, "`\u200B`\u200B`");
|
s = s.replace(/```/g, "`\u200B`\u200B`");
|
||||||
return "```" + s + " ```";
|
return "```" + s + " ```";
|
||||||
@ -86,6 +93,8 @@ async function printReport() {
|
|||||||
console.log(` - Error: ${toCodeBlock(p.error)}`);
|
console.log(` - Error: ${toCodeBlock(p.error)}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
report.otherErrors = report.otherErrors.filter(e => !IGNORED_DISCORD_ERRORS.some(regex => e.match(regex)));
|
||||||
|
|
||||||
console.log("## Discord Errors");
|
console.log("## Discord Errors");
|
||||||
report.otherErrors.forEach(e => {
|
report.otherErrors.forEach(e => {
|
||||||
console.log(`- ${toCodeBlock(e)}`);
|
console.log(`- ${toCodeBlock(e)}`);
|
||||||
@ -259,7 +268,7 @@ function runTime(token: string) {
|
|||||||
const { wreq } = Vencord.Webpack;
|
const { wreq } = Vencord.Webpack;
|
||||||
|
|
||||||
console.error("[PUP_DEBUG]", "Loading all chunks...");
|
console.error("[PUP_DEBUG]", "Loading all chunks...");
|
||||||
const ids = Function("return" + wreq.u.toString().match(/\{.+\}/s)![0])();
|
const ids = Function("return" + wreq.u.toString().match(/(?<=\()\{.+?\}/s)![0])();
|
||||||
for (const id in ids) {
|
for (const id in ids) {
|
||||||
const isWasm = await fetch(wreq.p + wreq.u(id))
|
const isWasm = await fetch(wreq.p + wreq.u(id))
|
||||||
.then(r => r.text())
|
.then(r => r.text())
|
||||||
@ -280,7 +289,7 @@ function runTime(token: string) {
|
|||||||
setTimeout(() => console.log("PUPPETEER_TEST_DONE_SIGNAL"), 1000);
|
setTimeout(() => console.log("PUPPETEER_TEST_DONE_SIGNAL"), 1000);
|
||||||
}, 1000));
|
}, 1000));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("[PUP_DEBUG]", "A fatal error occured");
|
console.error("[PUP_DEBUG]", "A fatal error occurred");
|
||||||
console.error("[PUP_DEBUG]", e);
|
console.error("[PUP_DEBUG]", e);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
@ -34,6 +34,7 @@ import { patches, PMLogger, startAllPlugins } from "./plugins";
|
|||||||
import { localStorage } from "./utils/localStorage";
|
import { localStorage } from "./utils/localStorage";
|
||||||
import { relaunch } from "./utils/native";
|
import { relaunch } from "./utils/native";
|
||||||
import { getCloudSettings, putCloudSettings } from "./utils/settingsSync";
|
import { getCloudSettings, putCloudSettings } from "./utils/settingsSync";
|
||||||
|
import { sendTelemetry } from "./utils/telemetry";
|
||||||
import { checkForUpdates, update, UpdateLogger } from "./utils/updater";
|
import { checkForUpdates, update, UpdateLogger } from "./utils/updater";
|
||||||
import { onceReady } from "./webpack";
|
import { onceReady } from "./webpack";
|
||||||
import { SettingsRouter } from "./webpack/common";
|
import { SettingsRouter } from "./webpack/common";
|
||||||
@ -83,6 +84,8 @@ async function init() {
|
|||||||
|
|
||||||
syncSettings();
|
syncSettings();
|
||||||
|
|
||||||
|
sendTelemetry();
|
||||||
|
|
||||||
if (!IS_WEB) {
|
if (!IS_WEB) {
|
||||||
try {
|
try {
|
||||||
const isOutdated = await checkForUpdates();
|
const isOutdated = await checkForUpdates();
|
||||||
@ -136,7 +139,7 @@ if (IS_DISCORD_DESKTOP && Settings.winNativeTitleBar && navigator.platform.toLow
|
|||||||
document.addEventListener("DOMContentLoaded", () => {
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
document.head.append(Object.assign(document.createElement("style"), {
|
document.head.append(Object.assign(document.createElement("style"), {
|
||||||
id: "vencord-native-titlebar-style",
|
id: "vencord-native-titlebar-style",
|
||||||
textContent: "[class*=titleBar-]{display: none!important}"
|
textContent: "[class*=titleBar]{display: none!important}"
|
||||||
}));
|
}));
|
||||||
}, { once: true });
|
}, { once: true });
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,8 @@ export default {
|
|||||||
deleteTheme: (fileName: string) => invoke<void>(IpcEvents.DELETE_THEME, fileName),
|
deleteTheme: (fileName: string) => invoke<void>(IpcEvents.DELETE_THEME, fileName),
|
||||||
getThemesDir: () => invoke<string>(IpcEvents.GET_THEMES_DIR),
|
getThemesDir: () => invoke<string>(IpcEvents.GET_THEMES_DIR),
|
||||||
getThemesList: () => invoke<Array<UserThemeHeader>>(IpcEvents.GET_THEMES_LIST),
|
getThemesList: () => invoke<Array<UserThemeHeader>>(IpcEvents.GET_THEMES_LIST),
|
||||||
getThemeData: (fileName: string) => invoke<string | undefined>(IpcEvents.GET_THEME_DATA, fileName)
|
getThemeData: (fileName: string) => invoke<string | undefined>(IpcEvents.GET_THEME_DATA, fileName),
|
||||||
|
getSystemValues: () => invoke<Record<string, string>>(IpcEvents.GET_THEME_SYSTEM_VALUES),
|
||||||
},
|
},
|
||||||
|
|
||||||
updater: {
|
updater: {
|
||||||
|
@ -17,14 +17,14 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { mergeDefaults } from "@utils/misc";
|
import { mergeDefaults } from "@utils/misc";
|
||||||
import { findByCodeLazy, findByPropsLazy } from "@webpack";
|
import { findByPropsLazy } from "@webpack";
|
||||||
import { SnowflakeUtils } from "@webpack/common";
|
import { SnowflakeUtils } from "@webpack/common";
|
||||||
import { Message } from "discord-types/general";
|
import { Message } from "discord-types/general";
|
||||||
import type { PartialDeep } from "type-fest";
|
import type { PartialDeep } from "type-fest";
|
||||||
|
|
||||||
import { Argument } from "./types";
|
import { Argument } from "./types";
|
||||||
|
|
||||||
const createBotMessage = findByCodeLazy('username:"Clyde"');
|
const MessageCreator = findByPropsLazy("createBotMessage");
|
||||||
const MessageSender = findByPropsLazy("receiveMessage");
|
const MessageSender = findByPropsLazy("receiveMessage");
|
||||||
|
|
||||||
export function generateId() {
|
export function generateId() {
|
||||||
@ -38,7 +38,7 @@ export function generateId() {
|
|||||||
* @returns {Message}
|
* @returns {Message}
|
||||||
*/
|
*/
|
||||||
export function sendBotMessage(channelId: string, message: PartialDeep<Message>): Message {
|
export function sendBotMessage(channelId: string, message: PartialDeep<Message>): Message {
|
||||||
const botMessage = createBotMessage({ channelId, content: "", embeds: [] });
|
const botMessage = MessageCreator.createBotMessage({ channelId, content: "", embeds: [] });
|
||||||
|
|
||||||
MessageSender.receiveMessage(channelId, mergeDefaults(message, botMessage));
|
MessageSender.receiveMessage(channelId, mergeDefaults(message, botMessage));
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@ export function addGlobalContextMenuPatch(patch: GlobalContextMenuPatchCallback)
|
|||||||
* Remove a context menu patch
|
* Remove a context menu patch
|
||||||
* @param navId The navId(s) for the context menu(s) to remove the patch
|
* @param navId The navId(s) for the context menu(s) to remove the patch
|
||||||
* @param patch The patch to be removed
|
* @param patch The patch to be removed
|
||||||
* @returns Wheter the patch was sucessfully removed from the context menu(s)
|
* @returns Whether the patch was successfully removed from the context menu(s)
|
||||||
*/
|
*/
|
||||||
export function removeContextMenuPatch<T extends string | Array<string>>(navId: T, patch: NavContextMenuPatchCallback): T extends string ? boolean : Array<boolean> {
|
export function removeContextMenuPatch<T extends string | Array<string>>(navId: T, patch: NavContextMenuPatchCallback): T extends string ? boolean : Array<boolean> {
|
||||||
const navIds = Array.isArray(navId) ? navId : [navId as string];
|
const navIds = Array.isArray(navId) ? navId : [navId as string];
|
||||||
@ -82,7 +82,7 @@ export function removeContextMenuPatch<T extends string | Array<string>>(navId:
|
|||||||
/**
|
/**
|
||||||
* Remove a global context menu patch
|
* Remove a global context menu patch
|
||||||
* @param patch The patch to be removed
|
* @param patch The patch to be removed
|
||||||
* @returns Wheter the patch was sucessfully removed
|
* @returns Whether the patch was successfully removed
|
||||||
*/
|
*/
|
||||||
export function removeGlobalContextMenuPatch(patch: GlobalContextMenuPatchCallback): boolean {
|
export function removeGlobalContextMenuPatch(patch: GlobalContextMenuPatchCallback): boolean {
|
||||||
return globalPatches.delete(patch);
|
return globalPatches.delete(patch);
|
||||||
|
@ -20,7 +20,6 @@ import { Channel, User } from "discord-types/general/index.js";
|
|||||||
|
|
||||||
interface DecoratorProps {
|
interface DecoratorProps {
|
||||||
activities: any[];
|
activities: any[];
|
||||||
canUseAvatarDecorations: boolean;
|
|
||||||
channel: Channel;
|
channel: Channel;
|
||||||
/**
|
/**
|
||||||
* Only for DM members
|
* Only for DM members
|
||||||
@ -52,9 +51,9 @@ export function removeDecorator(identifier: string) {
|
|||||||
decorators.delete(identifier);
|
decorators.delete(identifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function __addDecoratorsToList(props: DecoratorProps): (JSX.Element | null)[] {
|
export function __getDecorators(props: DecoratorProps): (JSX.Element | null)[] {
|
||||||
const isInGuild = !!(props.guildId);
|
const isInGuild = !!(props.guildId);
|
||||||
return [...decorators.values()].map(decoratorObj => {
|
return Array.from(decorators.values(), decoratorObj => {
|
||||||
const { decorator, onlyIn } = decoratorObj;
|
const { decorator, onlyIn } = decoratorObj;
|
||||||
// this can most likely be done cleaner
|
// this can most likely be done cleaner
|
||||||
if (!onlyIn || (onlyIn === "guilds" && isInGuild) || (onlyIn === "dms" && !isInGuild)) {
|
if (!onlyIn || (onlyIn === "guilds" && isInGuild) || (onlyIn === "dms" && !isInGuild)) {
|
||||||
|
@ -61,6 +61,8 @@ export interface Settings {
|
|||||||
settingsSync: boolean;
|
settingsSync: boolean;
|
||||||
settingsSyncVersion: number;
|
settingsSyncVersion: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
telemetry?: boolean; // tri-state, undefined = ask
|
||||||
}
|
}
|
||||||
|
|
||||||
const DefaultSettings: Settings = {
|
const DefaultSettings: Settings = {
|
||||||
@ -91,7 +93,9 @@ const DefaultSettings: Settings = {
|
|||||||
url: "https://api.vencord.dev/",
|
url: "https://api.vencord.dev/",
|
||||||
settingsSync: false,
|
settingsSync: false,
|
||||||
settingsSyncVersion: 0
|
settingsSyncVersion: 0
|
||||||
}
|
},
|
||||||
|
|
||||||
|
telemetry: undefined
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -237,6 +241,7 @@ type ResolvePropDeep<T, P> = P extends "" ? T :
|
|||||||
export function addSettingsListener<Path extends keyof Settings>(path: Path, onUpdate: (newValue: Settings[Path], path: Path) => void): void;
|
export function addSettingsListener<Path extends keyof Settings>(path: Path, onUpdate: (newValue: Settings[Path], path: Path) => void): void;
|
||||||
export function addSettingsListener<Path extends string>(path: Path, onUpdate: (newValue: Path extends "" ? any : ResolvePropDeep<Settings, Path>, path: Path extends "" ? string : Path) => void): void;
|
export function addSettingsListener<Path extends string>(path: Path, onUpdate: (newValue: Path extends "" ? any : ResolvePropDeep<Settings, Path>, path: Path extends "" ? string : Path) => void): void;
|
||||||
export function addSettingsListener(path: string, onUpdate: (newValue: any, path: string) => void) {
|
export function addSettingsListener(path: string, onUpdate: (newValue: any, path: string) => void) {
|
||||||
|
if (path)
|
||||||
((onUpdate as SubscriptionCallback)._paths ??= []).push(path);
|
((onUpdate as SubscriptionCallback)._paths ??= []).push(path);
|
||||||
subscriptions.add(onUpdate);
|
subscriptions.add(onUpdate);
|
||||||
}
|
}
|
||||||
|
@ -1,69 +0,0 @@
|
|||||||
/*
|
|
||||||
* Vencord, a modification for Discord's desktop app
|
|
||||||
* Copyright (c) 2023 Vendicated and contributors
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { proxyLazy } from "@utils/lazy";
|
|
||||||
import { Logger } from "@utils/Logger";
|
|
||||||
import { findModuleId, wreq } from "@webpack";
|
|
||||||
|
|
||||||
import { Settings } from "./Settings";
|
|
||||||
|
|
||||||
interface Setting<T> {
|
|
||||||
/**
|
|
||||||
* Get the setting value
|
|
||||||
*/
|
|
||||||
getSetting(): T;
|
|
||||||
/**
|
|
||||||
* Update the setting value
|
|
||||||
* @param value The new value
|
|
||||||
*/
|
|
||||||
updateSetting(value: T | ((old: T) => T)): Promise<void>;
|
|
||||||
/**
|
|
||||||
* React hook for automatically updating components when the setting is updated
|
|
||||||
*/
|
|
||||||
useSetting(): T;
|
|
||||||
settingsStoreApiGroup: string;
|
|
||||||
settingsStoreApiName: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const SettingsStores: Array<Setting<any>> | undefined = proxyLazy(() => {
|
|
||||||
const modId = findModuleId('"textAndImages","renderSpoilers"');
|
|
||||||
if (modId == null) return new Logger("SettingsStoreAPI").error("Didn't find stores module.");
|
|
||||||
|
|
||||||
const mod = wreq(modId);
|
|
||||||
if (mod == null) return;
|
|
||||||
|
|
||||||
return Object.values(mod).filter((s: any) => s?.settingsStoreApiGroup) as any;
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the store for a setting
|
|
||||||
* @param group The setting group
|
|
||||||
* @param name The name of the setting
|
|
||||||
*/
|
|
||||||
export function getSettingStore<T = any>(group: string, name: string): Setting<T> | undefined {
|
|
||||||
if (!Settings.plugins.SettingsStoreAPI.enabled) throw new Error("Cannot use SettingsStoreAPI without setting as dependency.");
|
|
||||||
|
|
||||||
return SettingsStores?.find(s => s?.settingsStoreApiGroup === group && s?.settingsStoreApiName === name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* getSettingStore but lazy
|
|
||||||
*/
|
|
||||||
export function getSettingStoreLazy<T = any>(group: string, name: string) {
|
|
||||||
return proxyLazy(() => getSettingStore<T>(group, name));
|
|
||||||
}
|
|
@ -29,7 +29,6 @@ import * as $Notices from "./Notices";
|
|||||||
import * as $Notifications from "./Notifications";
|
import * as $Notifications from "./Notifications";
|
||||||
import * as $ServerList from "./ServerList";
|
import * as $ServerList from "./ServerList";
|
||||||
import * as $Settings from "./Settings";
|
import * as $Settings from "./Settings";
|
||||||
import * as $SettingsStore from "./SettingsStore";
|
|
||||||
import * as $Styles from "./Styles";
|
import * as $Styles from "./Styles";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -91,10 +90,6 @@ export const MemberListDecorators = $MemberListDecorators;
|
|||||||
* An API allowing you to persist data
|
* An API allowing you to persist data
|
||||||
*/
|
*/
|
||||||
export const Settings = $Settings;
|
export const Settings = $Settings;
|
||||||
/**
|
|
||||||
* An API allowing you to read, manipulate and automatically update components based on Discord settings
|
|
||||||
*/
|
|
||||||
export const SettingsStore = $SettingsStore;
|
|
||||||
/**
|
/**
|
||||||
* An API allowing you to dynamically load styles
|
* An API allowing you to dynamically load styles
|
||||||
* a
|
* a
|
||||||
|
@ -28,8 +28,8 @@ interface BaseIconProps extends IconProps {
|
|||||||
|
|
||||||
interface IconProps extends SVGProps<SVGSVGElement> {
|
interface IconProps extends SVGProps<SVGSVGElement> {
|
||||||
className?: string;
|
className?: string;
|
||||||
height?: number;
|
height?: string | number;
|
||||||
width?: number;
|
width?: string | number;
|
||||||
}
|
}
|
||||||
|
|
||||||
function Icon({ height = 24, width = 24, className, children, viewBox, ...svgProps }: PropsWithChildren<BaseIconProps>) {
|
function Icon({ height = 24, width = 24, className, children, viewBox, ...svgProps }: PropsWithChildren<BaseIconProps>) {
|
||||||
@ -97,7 +97,7 @@ export function OpenExternalIcon(props: IconProps) {
|
|||||||
>
|
>
|
||||||
<polygon
|
<polygon
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
fill-rule="nonzero"
|
fillRule="nonzero"
|
||||||
points="13 20 11 20 11 8 5.5 13.5 4.08 12.08 12 4.16 19.92 12.08 18.5 13.5 13 8"
|
points="13 20 11 20 11 8 5.5 13.5 4.08 12.08 12 4.16 19.92 12.08 18.5 13.5 13 8"
|
||||||
/>
|
/>
|
||||||
</Icon>
|
</Icon>
|
||||||
@ -121,9 +121,13 @@ export function InfoIcon(props: IconProps) {
|
|||||||
<Icon
|
<Icon
|
||||||
{...props}
|
{...props}
|
||||||
className={classes(props.className, "vc-info-icon")}
|
className={classes(props.className, "vc-info-icon")}
|
||||||
viewBox="0 0 12 12"
|
viewBox="0 0 24 24"
|
||||||
>
|
>
|
||||||
<path fill="currentColor" d="M6 1C3.243 1 1 3.244 1 6c0 2.758 2.243 5 5 5s5-2.242 5-5c0-2.756-2.243-5-5-5zm0 2.376a.625.625 0 110 1.25.625.625 0 010-1.25zM7.5 8.5h-3v-1h1V6H5V5h1a.5.5 0 01.5.5v2h1v1z" />
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
transform="translate(2 2)"
|
||||||
|
d="M9,7 L11,7 L11,5 L9,5 L9,7 Z M10,18 C5.59,18 2,14.41 2,10 C2,5.59 5.59,2 10,2 C14.41,2 18,5.59 18,10 C18,14.41 14.41,18 10,18 L10,18 Z M10,4.4408921e-16 C4.4771525,-1.77635684e-15 4.4408921e-16,4.4771525 0,10 C-1.33226763e-15,12.6521649 1.0535684,15.195704 2.92893219,17.0710678 C4.80429597,18.9464316 7.3478351,20 10,20 C12.6521649,20 15.195704,18.9464316 17.0710678,17.0710678 C18.9464316,15.195704 20,12.6521649 20,10 C20,7.3478351 18.9464316,4.80429597 17.0710678,2.92893219 C15.195704,1.0535684 12.6521649,2.22044605e-16 10,0 L10,4.4408921e-16 Z M9,15 L11,15 L11,9 L9,9 L9,15 L9,15 Z"
|
||||||
|
/>
|
||||||
</Icon>
|
</Icon>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -139,8 +143,8 @@ export function OwnerCrownIcon(props: IconProps) {
|
|||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
fill-rule="evenodd"
|
fillRule="evenodd"
|
||||||
clip-rule="evenodd"
|
clipRule="evenodd"
|
||||||
d="M13.6572 5.42868C13.8879 5.29002 14.1806 5.30402 14.3973 5.46468C14.6133 5.62602 14.7119 5.90068 14.6473 6.16202L13.3139 11.4954C13.2393 11.7927 12.9726 12.0007 12.6666 12.0007H3.33325C3.02725 12.0007 2.76058 11.792 2.68592 11.4954L1.35258 6.16202C1.28792 5.90068 1.38658 5.62602 1.60258 5.46468C1.81992 5.30468 2.11192 5.29068 2.34325 5.42868L5.13192 7.10202L7.44592 3.63068C7.46173 3.60697 7.48377 3.5913 7.50588 3.57559C7.5192 3.56612 7.53255 3.55663 7.54458 3.54535L6.90258 2.90268C6.77325 2.77335 6.77325 2.56068 6.90258 2.43135L7.76458 1.56935C7.89392 1.44002 8.10658 1.44002 8.23592 1.56935L9.09792 2.43135C9.22725 2.56068 9.22725 2.77335 9.09792 2.90268L8.45592 3.54535C8.46794 3.55686 8.48154 3.56651 8.49516 3.57618C8.51703 3.5917 8.53897 3.60727 8.55458 3.63068L10.8686 7.10202L13.6572 5.42868ZM2.66667 12.6673H13.3333V14.0007H2.66667V12.6673Z"
|
d="M13.6572 5.42868C13.8879 5.29002 14.1806 5.30402 14.3973 5.46468C14.6133 5.62602 14.7119 5.90068 14.6473 6.16202L13.3139 11.4954C13.2393 11.7927 12.9726 12.0007 12.6666 12.0007H3.33325C3.02725 12.0007 2.76058 11.792 2.68592 11.4954L1.35258 6.16202C1.28792 5.90068 1.38658 5.62602 1.60258 5.46468C1.81992 5.30468 2.11192 5.29068 2.34325 5.42868L5.13192 7.10202L7.44592 3.63068C7.46173 3.60697 7.48377 3.5913 7.50588 3.57559C7.5192 3.56612 7.53255 3.55663 7.54458 3.54535L6.90258 2.90268C6.77325 2.77335 6.77325 2.56068 6.90258 2.43135L7.76458 1.56935C7.89392 1.44002 8.10658 1.44002 8.23592 1.56935L9.09792 2.43135C9.22725 2.56068 9.22725 2.77335 9.09792 2.90268L8.45592 3.54535C8.46794 3.55686 8.48154 3.56651 8.49516 3.57618C8.51703 3.5917 8.53897 3.60727 8.55458 3.63068L10.8686 7.10202L13.6572 5.42868ZM2.66667 12.6673H13.3333V14.0007H2.66667V12.6673Z"
|
||||||
/>
|
/>
|
||||||
</Icon>
|
</Icon>
|
||||||
@ -159,8 +163,6 @@ export function ScreenshareIcon(props: IconProps) {
|
|||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
fill-rule="evenodd"
|
|
||||||
clip-rule="evenodd"
|
|
||||||
d="M2 4.5C2 3.397 2.897 2.5 4 2.5H20C21.103 2.5 22 3.397 22 4.5V15.5C22 16.604 21.103 17.5 20 17.5H13V19.5H17V21.5H7V19.5H11V17.5H4C2.897 17.5 2 16.604 2 15.5V4.5ZM13.2 14.3375V11.6C9.864 11.6 7.668 12.6625 6 15C6.672 11.6625 8.532 8.3375 13.2 7.6625V5L18 9.6625L13.2 14.3375Z"
|
d="M2 4.5C2 3.397 2.897 2.5 4 2.5H20C21.103 2.5 22 3.397 22 4.5V15.5C22 16.604 21.103 17.5 20 17.5H13V19.5H17V21.5H7V19.5H11V17.5H4C2.897 17.5 2 16.604 2 15.5V4.5ZM13.2 14.3375V11.6C9.864 11.6 7.668 12.6625 6 15C6.672 11.6625 8.532 8.3375 13.2 7.6625V5L18 9.6625L13.2 14.3375Z"
|
||||||
/>
|
/>
|
||||||
</Icon>
|
</Icon>
|
||||||
@ -198,8 +200,58 @@ export function Microphone(props: IconProps) {
|
|||||||
className={classes(props.className, "vc-microphone")}
|
className={classes(props.className, "vc-microphone")}
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
>
|
>
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.99 11C14.99 12.66 13.66 14 12 14C10.34 14 9 12.66 9 11V5C9 3.34 10.34 2 12 2C13.66 2 15 3.34 15 5L14.99 11ZM12 16.1C14.76 16.1 17.3 14 17.3 11H19C19 14.42 16.28 17.24 13 17.72V21H11V17.72C7.72 17.23 5 14.41 5 11H6.7C6.7 14 9.24 16.1 12 16.1ZM12 4C11.2 4 11 4.66667 11 5V11C11 11.3333 11.2 12 12 12C12.8 12 13 11.3333 13 11V5C13 4.66667 12.8 4 12 4Z" fill="currentColor" />
|
<path fillRule="evenodd" clipRule="evenodd" d="M14.99 11C14.99 12.66 13.66 14 12 14C10.34 14 9 12.66 9 11V5C9 3.34 10.34 2 12 2C13.66 2 15 3.34 15 5L14.99 11ZM12 16.1C14.76 16.1 17.3 14 17.3 11H19C19 14.42 16.28 17.24 13 17.72V21H11V17.72C7.72 17.23 5 14.41 5 11H6.7C6.7 14 9.24 16.1 12 16.1ZM12 4C11.2 4 11 4.66667 11 5V11C11 11.3333 11.2 12 12 12C12.8 12 13 11.3333 13 11V5C13 4.66667 12.8 4 12 4Z" fill="currentColor" />
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.99 11C14.99 12.66 13.66 14 12 14C10.34 14 9 12.66 9 11V5C9 3.34 10.34 2 12 2C13.66 2 15 3.34 15 5L14.99 11ZM12 16.1C14.76 16.1 17.3 14 17.3 11H19C19 14.42 16.28 17.24 13 17.72V22H11V17.72C7.72 17.23 5 14.41 5 11H6.7C6.7 14 9.24 16.1 12 16.1Z" fill="currentColor" />
|
<path fillRule="evenodd" clipRule="evenodd" d="M14.99 11C14.99 12.66 13.66 14 12 14C10.34 14 9 12.66 9 11V5C9 3.34 10.34 2 12 2C13.66 2 15 3.34 15 5L14.99 11ZM12 16.1C14.76 16.1 17.3 14 17.3 11H19C19 14.42 16.28 17.24 13 17.72V22H11V17.72C7.72 17.23 5 14.41 5 11H6.7C6.7 14 9.24 16.1 12 16.1Z" fill="currentColor" />
|
||||||
|
</Icon >
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function CogWheel(props: IconProps) {
|
||||||
|
return (
|
||||||
|
<Icon
|
||||||
|
{...props}
|
||||||
|
className={classes(props.className, "vc-cog-wheel")}
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
clipRule="evenodd"
|
||||||
|
fill="currentColor"
|
||||||
|
d="M19.738 10H22V14H19.739C19.498 14.931 19.1 15.798 18.565 16.564L20 18L18 20L16.565 18.564C15.797 19.099 14.932 19.498 14 19.738V22H10V19.738C9.069 19.498 8.203 19.099 7.436 18.564L6 20L4 18L5.436 16.564C4.901 15.799 4.502 14.932 4.262 14H2V10H4.262C4.502 9.068 4.9 8.202 5.436 7.436L4 6L6 4L7.436 5.436C8.202 4.9 9.068 4.502 10 4.262V2H14V4.261C14.932 4.502 15.797 4.9 16.565 5.435L18 3.999L20 5.999L18.564 7.436C19.099 8.202 19.498 9.069 19.738 10ZM12 16C14.2091 16 16 14.2091 16 12C16 9.79086 14.2091 8 12 8C9.79086 8 8 9.79086 8 12C8 14.2091 9.79086 16 12 16Z"
|
||||||
|
/>
|
||||||
|
</Icon>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ReplyIcon(props: IconProps) {
|
||||||
|
return (
|
||||||
|
<Icon
|
||||||
|
{...props}
|
||||||
|
className={classes(props.className, "vc-reply-icon")}
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M10 8.26667V4L3 11.4667L10 18.9333V14.56C15 14.56 18.5 16.2667 21 20C20 14.6667 17 9.33333 10 8.26667Z"
|
||||||
|
/>
|
||||||
|
</Icon>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function DeleteIcon(props: IconProps) {
|
||||||
|
return (
|
||||||
|
<Icon
|
||||||
|
{...props}
|
||||||
|
className={classes(props.className, "vc-delete-icon")}
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M15 3.999V2H9V3.999H3V5.999H21V3.999H15Z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M5 6.99902V18.999C5 20.101 5.897 20.999 7 20.999H17C18.103 20.999 19 20.101 19 18.999V6.99902H5ZM11 17H9V11H11V17ZM15 17H13V11H15V17Z"
|
||||||
|
/>
|
||||||
</Icon>
|
</Icon>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -94,7 +94,7 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti
|
|||||||
(async () => {
|
(async () => {
|
||||||
for (const user of plugin.authors.slice(0, 6)) {
|
for (const user of plugin.authors.slice(0, 6)) {
|
||||||
const author = user.id
|
const author = user.id
|
||||||
? await UserUtils.fetchUser(`${user.id}`)
|
? await UserUtils.getUser(`${user.id}`)
|
||||||
.catch(() => makeDummyUser({ username: user.name }))
|
.catch(() => makeDummyUser({ username: user.name }))
|
||||||
: makeDummyUser({ username: user.name });
|
: makeDummyUser({ username: user.name });
|
||||||
|
|
||||||
@ -238,7 +238,7 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti
|
|||||||
<Button
|
<Button
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
size={Button.Sizes.SMALL}
|
size={Button.Sizes.SMALL}
|
||||||
color={Button.Colors.WHITE}
|
color={Button.Colors.PRIMARY}
|
||||||
look={Button.Looks.LINK}
|
look={Button.Looks.LINK}
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
text-wrap: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vc-author-modal-name::before {
|
.vc-author-modal-name::before {
|
||||||
|
@ -22,6 +22,7 @@ import * as DataStore from "@api/DataStore";
|
|||||||
import { showNotice } from "@api/Notices";
|
import { showNotice } from "@api/Notices";
|
||||||
import { Settings, useSettings } from "@api/Settings";
|
import { Settings, useSettings } from "@api/Settings";
|
||||||
import { classNameFactory } from "@api/Styles";
|
import { classNameFactory } from "@api/Styles";
|
||||||
|
import { CogWheel, InfoIcon } from "@components/Icons";
|
||||||
import PluginModal from "@components/PluginSettings/PluginModal";
|
import PluginModal from "@components/PluginSettings/PluginModal";
|
||||||
import { AddonCard } from "@components/VencordSettings/AddonCard";
|
import { AddonCard } from "@components/VencordSettings/AddonCard";
|
||||||
import { SettingsTab } from "@components/VencordSettings/shared";
|
import { SettingsTab } from "@components/VencordSettings/shared";
|
||||||
@ -30,10 +31,10 @@ import { Logger } from "@utils/Logger";
|
|||||||
import { Margins } from "@utils/margins";
|
import { Margins } from "@utils/margins";
|
||||||
import { classes, isObjectEmpty } from "@utils/misc";
|
import { classes, isObjectEmpty } from "@utils/misc";
|
||||||
import { openModalLazy } from "@utils/modal";
|
import { openModalLazy } from "@utils/modal";
|
||||||
import { LazyComponent, useAwaiter } from "@utils/react";
|
import { useAwaiter } from "@utils/react";
|
||||||
import { Plugin } from "@utils/types";
|
import { Plugin } from "@utils/types";
|
||||||
import { findByCode, findByPropsLazy } from "@webpack";
|
import { findByPropsLazy } from "@webpack";
|
||||||
import { Alerts, Button, Card, Forms, Parser, React, Select, Text, TextInput, Toasts, Tooltip } from "@webpack/common";
|
import { Alerts, Button, Card, Forms, lodash, Parser, React, Select, Text, TextInput, Toasts, Tooltip } from "@webpack/common";
|
||||||
|
|
||||||
import Plugins from "~plugins";
|
import Plugins from "~plugins";
|
||||||
|
|
||||||
@ -46,8 +47,6 @@ const logger = new Logger("PluginSettings", "#a6d189");
|
|||||||
const InputStyles = findByPropsLazy("inputDefault", "inputWrapper");
|
const InputStyles = findByPropsLazy("inputDefault", "inputWrapper");
|
||||||
const ButtonClasses = findByPropsLazy("button", "disabled", "enabled");
|
const ButtonClasses = findByPropsLazy("button", "disabled", "enabled");
|
||||||
|
|
||||||
const CogWheel = LazyComponent(() => findByCode("18.564C15.797 19.099 14.932 19.498 14 19.738V22H10V19.738C9.069"));
|
|
||||||
const InfoIcon = LazyComponent(() => findByCode("4.4408921e-16 C4.4771525,-1.77635684e-15 4.4408921e-16"));
|
|
||||||
|
|
||||||
function showErrorToast(message: string) {
|
function showErrorToast(message: string) {
|
||||||
Toasts.show({
|
Toasts.show({
|
||||||
@ -163,7 +162,7 @@ export function PluginCard({ plugin, disabled, onRestartNeeded, onMouseEnter, on
|
|||||||
<button role="switch" onClick={() => openModal()} className={classes(ButtonClasses.button, cl("info-button"))}>
|
<button role="switch" onClick={() => openModal()} className={classes(ButtonClasses.button, cl("info-button"))}>
|
||||||
{plugin.options && !isObjectEmpty(plugin.options)
|
{plugin.options && !isObjectEmpty(plugin.options)
|
||||||
? <CogWheel />
|
? <CogWheel />
|
||||||
: <InfoIcon width="24" height="24" />}
|
: <InfoIcon />}
|
||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
@ -252,7 +251,7 @@ export default function PluginSettings() {
|
|||||||
}
|
}
|
||||||
DataStore.set("Vencord_existingPlugins", existingTimestamps);
|
DataStore.set("Vencord_existingPlugins", existingTimestamps);
|
||||||
|
|
||||||
return window._.isEqual(newPlugins, sortedPluginNames) ? [] : newPlugins;
|
return lodash.isEqual(newPlugins, sortedPluginNames) ? [] : newPlugins;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
type P = JSX.Element | JSX.Element[];
|
type P = JSX.Element | JSX.Element[];
|
||||||
|
@ -19,12 +19,13 @@
|
|||||||
import { useSettings } from "@api/Settings";
|
import { useSettings } from "@api/Settings";
|
||||||
import { classNameFactory } from "@api/Styles";
|
import { classNameFactory } from "@api/Styles";
|
||||||
import { Flex } from "@components/Flex";
|
import { Flex } from "@components/Flex";
|
||||||
|
import { DeleteIcon } from "@components/Icons";
|
||||||
import { Link } from "@components/Link";
|
import { Link } from "@components/Link";
|
||||||
import { Margins } from "@utils/margins";
|
import { Margins } from "@utils/margins";
|
||||||
import { classes } from "@utils/misc";
|
import { classes } from "@utils/misc";
|
||||||
import { showItemInFolder } from "@utils/native";
|
import { showItemInFolder } from "@utils/native";
|
||||||
import { useAwaiter } from "@utils/react";
|
import { useAwaiter } from "@utils/react";
|
||||||
import { findByCodeLazy, findByPropsLazy, findLazy } from "@webpack";
|
import { findByPropsLazy, findLazy } from "@webpack";
|
||||||
import { Button, Card, FluxDispatcher, Forms, React, showToast, TabBar, TextArea, useEffect, useRef, useState } from "@webpack/common";
|
import { Button, Card, FluxDispatcher, Forms, React, showToast, TabBar, TextArea, useEffect, useRef, useState } from "@webpack/common";
|
||||||
import { UserThemeHeader } from "main/themes";
|
import { UserThemeHeader } from "main/themes";
|
||||||
import type { ComponentType, Ref, SyntheticEvent } from "react";
|
import type { ComponentType, Ref, SyntheticEvent } from "react";
|
||||||
@ -40,8 +41,7 @@ type FileInput = ComponentType<{
|
|||||||
}>;
|
}>;
|
||||||
|
|
||||||
const InviteActions = findByPropsLazy("resolveInvite");
|
const InviteActions = findByPropsLazy("resolveInvite");
|
||||||
const TrashIcon = findByCodeLazy("M5 6.99902V18.999C5 20.101 5.897 20.999");
|
const FileInput: FileInput = findLazy(m => m.prototype?.activateUploadDialogue && m.prototype.setRef);
|
||||||
const FileInput: FileInput = findByCodeLazy("activateUploadDialogue=");
|
|
||||||
const TextAreaProps = findLazy(m => typeof m.textarea === "string");
|
const TextAreaProps = findLazy(m => typeof m.textarea === "string");
|
||||||
|
|
||||||
const cl = classNameFactory("vc-settings-theme-");
|
const cl = classNameFactory("vc-settings-theme-");
|
||||||
@ -112,7 +112,7 @@ function ThemeCard({ theme, enabled, onChange, onDelete }: ThemeCardProps) {
|
|||||||
infoButton={
|
infoButton={
|
||||||
IS_WEB && (
|
IS_WEB && (
|
||||||
<div style={{ cursor: "pointer", color: "var(--status-danger" }} onClick={onDelete}>
|
<div style={{ cursor: "pointer", color: "var(--status-danger" }} onClick={onDelete}>
|
||||||
<TrashIcon />
|
<DeleteIcon />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -46,7 +46,7 @@ function withDispatcher(dispatcher: React.Dispatch<React.SetStateAction<boolean>
|
|||||||
if (code === "ENOENT")
|
if (code === "ENOENT")
|
||||||
var err = `Command \`${path}\` not found.\nPlease install it and try again`;
|
var err = `Command \`${path}\` not found.\nPlease install it and try again`;
|
||||||
else {
|
else {
|
||||||
var err = `An error occured while running \`${cmd}\`:\n`;
|
var err = `An error occurred while running \`${cmd}\`:\n`;
|
||||||
err += stderr || `Code \`${code}\`. See the console for more info`;
|
err += stderr || `Code \`${code}\`. See the console for more info`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,6 +21,7 @@ import { Settings, useSettings } from "@api/Settings";
|
|||||||
import { classNameFactory } from "@api/Styles";
|
import { classNameFactory } from "@api/Styles";
|
||||||
import DonateButton from "@components/DonateButton";
|
import DonateButton from "@components/DonateButton";
|
||||||
import { ErrorCard } from "@components/ErrorCard";
|
import { ErrorCard } from "@components/ErrorCard";
|
||||||
|
import { isMac, isWindows } from "@utils/constants";
|
||||||
import { Margins } from "@utils/margins";
|
import { Margins } from "@utils/margins";
|
||||||
import { identity } from "@utils/misc";
|
import { identity } from "@utils/misc";
|
||||||
import { relaunch, showItemInFolder } from "@utils/native";
|
import { relaunch, showItemInFolder } from "@utils/native";
|
||||||
@ -46,9 +47,6 @@ function VencordSettings() {
|
|||||||
|
|
||||||
const donateImage = React.useMemo(() => Math.random() > 0.5 ? DEFAULT_DONATE_IMAGE : SHIGGY_DONATE_IMAGE, []);
|
const donateImage = React.useMemo(() => Math.random() > 0.5 ? DEFAULT_DONATE_IMAGE : SHIGGY_DONATE_IMAGE, []);
|
||||||
|
|
||||||
const isWindows = navigator.platform.toLowerCase().startsWith("win");
|
|
||||||
const isMac = navigator.platform.toLowerCase().startsWith("mac");
|
|
||||||
|
|
||||||
const Switches: Array<false | {
|
const Switches: Array<false | {
|
||||||
key: KeysOfType<typeof settings, boolean>;
|
key: KeysOfType<typeof settings, boolean>;
|
||||||
title: string;
|
title: string;
|
||||||
@ -93,6 +91,11 @@ function VencordSettings() {
|
|||||||
key: "macosTranslucency",
|
key: "macosTranslucency",
|
||||||
title: "Enable translucent window",
|
title: "Enable translucent window",
|
||||||
note: "Requires a full restart"
|
note: "Requires a full restart"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "telemetry",
|
||||||
|
title: "Enable Telemetry",
|
||||||
|
note: "We only gather anonymous telemetry data. All data deleted after 3 days if you opt out."
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -257,7 +260,11 @@ function DonateCard({ image }: DonateCardProps) {
|
|||||||
src={image}
|
src={image}
|
||||||
alt=""
|
alt=""
|
||||||
height={128}
|
height={128}
|
||||||
style={{ marginLeft: "auto", transform: image === DEFAULT_DONATE_IMAGE ? "rotate(10deg)" : "" }}
|
style={{
|
||||||
|
imageRendering: image === SHIGGY_DONATE_IMAGE ? "pixelated" : void 0,
|
||||||
|
marginLeft: "auto",
|
||||||
|
transform: image === DEFAULT_DONATE_IMAGE ? "rotate(10deg)" : void 0
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
|
@ -62,6 +62,10 @@ if (IS_VESKTOP || !IS_VANILLA) {
|
|||||||
} catch { }
|
} catch { }
|
||||||
|
|
||||||
|
|
||||||
|
const findHeader = (headers: Record<string, string[]>, headerName: Lowercase<string>) => {
|
||||||
|
return Object.keys(headers).find(h => h.toLowerCase() === headerName);
|
||||||
|
};
|
||||||
|
|
||||||
// Remove CSP
|
// Remove CSP
|
||||||
type PolicyResult = Record<string, string[]>;
|
type PolicyResult = Record<string, string[]>;
|
||||||
|
|
||||||
@ -73,6 +77,7 @@ if (IS_VESKTOP || !IS_VANILLA) {
|
|||||||
result[directiveKey] = directiveValue;
|
result[directiveKey] = directiveValue;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
const stringifyPolicy = (policy: PolicyResult): string =>
|
const stringifyPolicy = (policy: PolicyResult): string =>
|
||||||
@ -81,31 +86,39 @@ if (IS_VESKTOP || !IS_VANILLA) {
|
|||||||
.map(directive => directive.flat().join(" "))
|
.map(directive => directive.flat().join(" "))
|
||||||
.join("; ");
|
.join("; ");
|
||||||
|
|
||||||
function patchCsp(headers: Record<string, string[]>, header: string) {
|
const patchCsp = (headers: Record<string, string[]>) => {
|
||||||
if (header in headers) {
|
const header = findHeader(headers, "content-security-policy");
|
||||||
|
|
||||||
|
if (header) {
|
||||||
const csp = parsePolicy(headers[header][0]);
|
const csp = parsePolicy(headers[header][0]);
|
||||||
|
|
||||||
for (const directive of ["style-src", "connect-src", "img-src", "font-src", "media-src", "worker-src"]) {
|
for (const directive of ["style-src", "connect-src", "img-src", "font-src", "media-src", "worker-src"]) {
|
||||||
csp[directive] = ["*", "blob:", "data:", "vencord:", "'unsafe-inline'"];
|
csp[directive] ??= [];
|
||||||
|
csp[directive].push("*", "blob:", "data:", "vencord:", "'unsafe-inline'");
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Restrict this to only imported packages with fixed version.
|
// TODO: Restrict this to only imported packages with fixed version.
|
||||||
// Perhaps auto generate with esbuild
|
// Perhaps auto generate with esbuild
|
||||||
csp["script-src"] ??= [];
|
csp["script-src"] ??= [];
|
||||||
csp["script-src"].push("'unsafe-eval'", "https://unpkg.com", "https://cdnjs.cloudflare.com");
|
csp["script-src"].push("'unsafe-eval'", "https://unpkg.com", "https://cdnjs.cloudflare.com");
|
||||||
headers[header] = [stringifyPolicy(csp)];
|
headers[header] = [stringifyPolicy(csp)];
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
session.defaultSession.webRequest.onHeadersReceived(({ responseHeaders, resourceType }, cb) => {
|
session.defaultSession.webRequest.onHeadersReceived(({ responseHeaders, resourceType }, cb) => {
|
||||||
if (responseHeaders) {
|
if (responseHeaders) {
|
||||||
if (resourceType === "mainFrame")
|
if (resourceType === "mainFrame")
|
||||||
patchCsp(responseHeaders, "content-security-policy");
|
patchCsp(responseHeaders);
|
||||||
|
|
||||||
// Fix hosts that don't properly set the css content type, such as
|
// Fix hosts that don't properly set the css content type, such as
|
||||||
// raw.githubusercontent.com
|
// raw.githubusercontent.com
|
||||||
if (resourceType === "stylesheet")
|
if (resourceType === "stylesheet") {
|
||||||
responseHeaders["content-type"] = ["text/css"];
|
const header = findHeader(responseHeaders, "content-type");
|
||||||
|
if (header)
|
||||||
|
responseHeaders[header] = ["text/css"];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
cb({ cancel: false, responseHeaders });
|
cb({ cancel: false, responseHeaders });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ import "./ipcPlugins";
|
|||||||
import { debounce } from "@utils/debounce";
|
import { debounce } from "@utils/debounce";
|
||||||
import { IpcEvents } from "@utils/IpcEvents";
|
import { IpcEvents } from "@utils/IpcEvents";
|
||||||
import { Queue } from "@utils/Queue";
|
import { Queue } from "@utils/Queue";
|
||||||
import { BrowserWindow, ipcMain, shell } from "electron";
|
import { BrowserWindow, ipcMain, shell, systemPreferences } from "electron";
|
||||||
import { mkdirSync, readFileSync, watch } from "fs";
|
import { mkdirSync, readFileSync, watch } from "fs";
|
||||||
import { open, readdir, readFile, writeFile } from "fs/promises";
|
import { open, readdir, readFile, writeFile } from "fs/promises";
|
||||||
import { join, normalize } from "path";
|
import { join, normalize } from "path";
|
||||||
@ -112,6 +112,10 @@ ipcMain.handle(IpcEvents.SET_QUICK_CSS, (_, css) =>
|
|||||||
ipcMain.handle(IpcEvents.GET_THEMES_DIR, () => THEMES_DIR);
|
ipcMain.handle(IpcEvents.GET_THEMES_DIR, () => THEMES_DIR);
|
||||||
ipcMain.handle(IpcEvents.GET_THEMES_LIST, () => listThemes());
|
ipcMain.handle(IpcEvents.GET_THEMES_LIST, () => listThemes());
|
||||||
ipcMain.handle(IpcEvents.GET_THEME_DATA, (_, fileName) => getThemeData(fileName));
|
ipcMain.handle(IpcEvents.GET_THEME_DATA, (_, fileName) => getThemeData(fileName));
|
||||||
|
ipcMain.handle(IpcEvents.GET_THEME_SYSTEM_VALUES, () => ({
|
||||||
|
// win & mac only
|
||||||
|
"os-accent-color": `#${systemPreferences.getAccentColor?.() || ""}`
|
||||||
|
}));
|
||||||
|
|
||||||
ipcMain.handle(IpcEvents.GET_SETTINGS_DIR, () => SETTINGS_DIR);
|
ipcMain.handle(IpcEvents.GET_SETTINGS_DIR, () => SETTINGS_DIR);
|
||||||
ipcMain.on(IpcEvents.GET_SETTINGS, e => e.returnValue = readSettings());
|
ipcMain.on(IpcEvents.GET_SETTINGS, e => e.returnValue = readSettings());
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { app } from "electron";
|
import { app } from "electron";
|
||||||
import { existsSync, mkdirSync, readdirSync, renameSync, statSync, writeFileSync } from "fs";
|
import { existsSync, mkdirSync, readdirSync, renameSync, statSync, writeFileSync } from "original-fs";
|
||||||
import { basename, dirname, join } from "path";
|
import { basename, dirname, join } from "path";
|
||||||
|
|
||||||
function isNewer($new: string, old: string) {
|
function isNewer($new: string, old: string) {
|
||||||
|
@ -49,7 +49,9 @@ async function getRepo() {
|
|||||||
async function calculateGitChanges() {
|
async function calculateGitChanges() {
|
||||||
await git("fetch");
|
await git("fetch");
|
||||||
|
|
||||||
const res = await git("log", "HEAD...origin/main", "--pretty=format:%an/%h/%s");
|
const branch = await git("branch", "--show-current");
|
||||||
|
|
||||||
|
const res = await git("log", `HEAD...origin/${branch.stdout.trim()}`, "--pretty=format:%an/%h/%s");
|
||||||
|
|
||||||
const commits = res.stdout.trim();
|
const commits = res.stdout.trim();
|
||||||
return commits ? commits.split("\n").map(line => {
|
return commits ? commits.split("\n").map(line => {
|
||||||
|
@ -85,17 +85,19 @@ export default definePlugin({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
// alt: "", aria-hidden: false, src: originalSrc
|
// alt: "", aria-hidden: false, src: originalSrc
|
||||||
match: /alt:" ","aria-hidden":!0,src:(?=(\i)\.src)/g,
|
match: /alt:" ","aria-hidden":!0,src:(?=(\i)\.src)/,
|
||||||
// ...badge.props, ..., src: badge.image ?? ...
|
// ...badge.props, ..., src: badge.image ?? ...
|
||||||
replace: "...$1.props,$& $1.image??"
|
replace: "...$1.props,$& $1.image??"
|
||||||
},
|
},
|
||||||
|
// replace their component with ours if applicable
|
||||||
{
|
{
|
||||||
match: /children:function(?<=(\i)\.(?:tooltip|description),spacing:\d.+?)/g,
|
match: /(?<=text:(\i)\.description,spacing:12,)children:/,
|
||||||
replace: "children:$1.component ? () => $self.renderBadgeComponent($1) : function"
|
replace: "children:$1.component ? () => $self.renderBadgeComponent($1) :"
|
||||||
},
|
},
|
||||||
|
// conditionally override their onClick with badge.onClick if it exists
|
||||||
{
|
{
|
||||||
match: /onClick:function(?=.{0,200}href:(\i)\.link)/,
|
match: /href:(\i)\.link/,
|
||||||
replace: "onClick:$1.onClick??function"
|
replace: "...($1.onClick && { onClick: $1.onClick }),$&"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ export default definePlugin({
|
|||||||
patches: [
|
patches: [
|
||||||
// obtain BUILT_IN_COMMANDS instance
|
// obtain BUILT_IN_COMMANDS instance
|
||||||
{
|
{
|
||||||
find: '"giphy","tenor"',
|
find: ',"tenor"',
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
// Matches BUILT_IN_COMMANDS. This is not exported so this is
|
// Matches BUILT_IN_COMMANDS. This is not exported so this is
|
||||||
@ -34,7 +34,7 @@ export default definePlugin({
|
|||||||
// patch simpler
|
// patch simpler
|
||||||
|
|
||||||
// textCommands = builtInCommands.filter(...)
|
// textCommands = builtInCommands.filter(...)
|
||||||
match: /(?<=\w=)(\w)(\.filter\(.{0,30}giphy)/,
|
match: /(?<=\w=)(\w)(\.filter\(.{0,60}tenor)/,
|
||||||
replace: "Vencord.Api.Commands._init($1)$2",
|
replace: "Vencord.Api.Commands._init($1)$2",
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@ -44,8 +44,8 @@ export default definePlugin({
|
|||||||
find: "Unexpected value for option",
|
find: "Unexpected value for option",
|
||||||
replacement: {
|
replacement: {
|
||||||
// return [2, cmd.execute(args, ctx)]
|
// return [2, cmd.execute(args, ctx)]
|
||||||
match: /,(.{1,2})\.execute\((.{1,2}),(.{1,2})\)]/,
|
match: /,(\i)\.execute\((\i),(\i)\)/,
|
||||||
replace: (_, cmd, args, ctx) => `,Vencord.Api.Commands._handleCommand(${cmd}, ${args}, ${ctx})]`
|
replace: (_, cmd, args, ctx) => `,Vencord.Api.Commands._handleCommand(${cmd}, ${args}, ${ctx})`
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// Show plugin name instead of "Built-In"
|
// Show plugin name instead of "Built-In"
|
||||||
|
@ -29,8 +29,8 @@ export default definePlugin({
|
|||||||
{
|
{
|
||||||
find: "♫ (つ。◕‿‿◕。)つ ♪",
|
find: "♫ (つ。◕‿‿◕。)つ ♪",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(?<=function \i\((\i)\){)(?=var \i,\i=\i\.navId)/,
|
match: /let{navId:/,
|
||||||
replace: (_, props) => `Vencord.Api.ContextMenu._patchContextMenu(${props});`
|
replace: "Vencord.Api.ContextMenu._patchContextMenu(arguments[0]);$&"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -22,20 +22,25 @@ import definePlugin from "@utils/types";
|
|||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "MemberListDecoratorsAPI",
|
name: "MemberListDecoratorsAPI",
|
||||||
description: "API to add decorators to member list (both in servers and DMs)",
|
description: "API to add decorators to member list (both in servers and DMs)",
|
||||||
authors: [Devs.TheSun],
|
authors: [Devs.TheSun, Devs.Ven],
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
find: "lostPermissionTooltipText,",
|
find: ".lostPermission)",
|
||||||
replacement: {
|
replacement: [
|
||||||
match: /Fragment,{children:\[(.{30,80})\]/,
|
{
|
||||||
replace: "Fragment,{children:Vencord.Api.MemberListDecorators.__addDecoratorsToList(this.props).concat($1)"
|
match: /let\{[^}]*lostPermissionTooltipText:\i[^}]*\}=(\i),/,
|
||||||
|
replace: "$&vencordProps=$1,"
|
||||||
|
}, {
|
||||||
|
match: /decorators:.{0,100}?children:\[/,
|
||||||
|
replace: "$&...(typeof vencordProps=='undefined'?[]:Vencord.Api.MemberListDecorators.__getDecorators(vencordProps)),"
|
||||||
}
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
find: "PrivateChannel.renderAvatar",
|
find: "PrivateChannel.renderAvatar",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(subText:(.{1,2})\.renderSubtitle\(\).{1,50}decorators):(.{30,100}:null)/,
|
match: /decorators:(\i\.isSystemDM\(\))\?(.+?):null/,
|
||||||
replace: "$1:Vencord.Api.MemberListDecorators.__addDecoratorsToList($2.props).concat($3)"
|
replace: "decorators:[...Vencord.Api.MemberListDecorators.__getDecorators(arguments[0]), $1?$2:null]"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -27,9 +27,8 @@ export default definePlugin({
|
|||||||
{
|
{
|
||||||
find: ".Messages.REMOVE_ATTACHMENT_BODY",
|
find: ".Messages.REMOVE_ATTACHMENT_BODY",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(.container\)?,children:)(\[[^\]]+\])(}\)\};return)/,
|
match: /(?<=.container\)?,children:)(\[.+?\])/,
|
||||||
replace: (_, pre, accessories, post) =>
|
replace: "Vencord.Api.MessageAccessories._modifyAccessories($1,this.props)",
|
||||||
`${pre}Vencord.Api.MessageAccessories._modifyAccessories(${accessories},this.props)${post}`,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -25,10 +25,10 @@ export default definePlugin({
|
|||||||
authors: [Devs.TheSun],
|
authors: [Devs.TheSun],
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
find: ".withMentionPrefix",
|
find: '"Message Username"',
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(.roleDot.{10,50}{children:.{1,2})}\)/,
|
match: /currentUserIsPremium:.{0,70}{children:\i(?=}\))/,
|
||||||
replace: "$1.concat(Vencord.Api.MessageDecorations.__addDecorationsToMessage(arguments[0]))})"
|
replace: "$&.concat(Vencord.Api.MessageDecorations.__addDecorationsToMessage(arguments[0]))"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -27,10 +27,8 @@ export default definePlugin({
|
|||||||
{
|
{
|
||||||
find: '"MessageActionCreators"',
|
find: '"MessageActionCreators"',
|
||||||
replacement: {
|
replacement: {
|
||||||
// editMessage: function (...) {
|
match: /async editMessage\(.+?\)\{/,
|
||||||
match: /\beditMessage:(function\(.+?\))\{/,
|
replace: "$&await Vencord.Api.MessageEvents._handlePreEdit(...arguments);"
|
||||||
// editMessage: async function (...) { await handlePreEdit(...); ...
|
|
||||||
replace: "editMessage:async $1{await Vencord.Api.MessageEvents._handlePreEdit(...arguments);"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -38,7 +36,7 @@ export default definePlugin({
|
|||||||
replacement: {
|
replacement: {
|
||||||
// props.chatInputType...then((function(isMessageValid)... var parsedMessage = b.c.parse(channel,... var replyOptions = f.g.getSendMessageOptionsForReply(pendingReply);
|
// props.chatInputType...then((function(isMessageValid)... var parsedMessage = b.c.parse(channel,... var replyOptions = f.g.getSendMessageOptionsForReply(pendingReply);
|
||||||
// Lookbehind: validateMessage)({openWarningPopout:..., type: i.props.chatInputType, content: t, stickers: r, ...}).then((function(isMessageValid)
|
// Lookbehind: validateMessage)({openWarningPopout:..., type: i.props.chatInputType, content: t, stickers: r, ...}).then((function(isMessageValid)
|
||||||
match: /(props\.chatInputType.+?\.then\(\()(function.+?var (\i)=\i\.\i\.parse\((\i),.+?var (\i)=\i\.\i\.getSendMessageOptionsForReply\(\i\);)(?<=\)\(({.+?})\)\.then.+?)/,
|
match: /(type:this\.props\.chatInputType.+?\.then\()(\i=>\{.+?let (\i)=\i\.\i\.parse\((\i),.+?let (\i)=\i\.\i\.getSendMessageOptionsForReply\(\i\);)(?<=\)\(({.+?})\)\.then.+?)/,
|
||||||
// props.chatInputType...then((async function(isMessageValid)... var replyOptions = f.g.getSendMessageOptionsForReply(pendingReply); if(await Vencord.api...) return { shoudClear:true, shouldRefocus:true };
|
// props.chatInputType...then((async function(isMessageValid)... var replyOptions = f.g.getSendMessageOptionsForReply(pendingReply); if(await Vencord.api...) return { shoudClear:true, shouldRefocus:true };
|
||||||
replace: (_, rest1, rest2, parsedMessage, channel, replyOptions, extra) => "" +
|
replace: (_, rest1, rest2, parsedMessage, channel, replyOptions, extra) => "" +
|
||||||
`${rest1}async ${rest2}` +
|
`${rest1}async ${rest2}` +
|
||||||
@ -49,10 +47,10 @@ export default definePlugin({
|
|||||||
{
|
{
|
||||||
find: '("interactionUsernameProfile',
|
find: '("interactionUsernameProfile',
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /var \i=(\i)\.id,\i=(\i)\.id;return \i\.useCallback\(\(?function\((\i)\){/,
|
match: /let\{id:\i}=(\i),{id:\i}=(\i);return \i\.useCallback\((\i)=>\{/,
|
||||||
replace: (m, message, channel, event) =>
|
replace: (m, message, channel, event) =>
|
||||||
// the message param is shadowed by the event param, so need to alias them
|
// the message param is shadowed by the event param, so need to alias them
|
||||||
`var _msg=${message},_chan=${channel};${m}Vencord.Api.MessageEvents._handleClick(_msg, _chan, ${event});`
|
`const vcMsg=${message},vcChan=${channel};${m}Vencord.Api.MessageEvents._handleClick(vcMsg, vcChan, ${event});`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -29,12 +29,12 @@ export default definePlugin({
|
|||||||
find: 'displayName="NoticeStore"',
|
find: 'displayName="NoticeStore"',
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
match: /(?=;\i=null;.{0,70}getPremiumSubscription)/g,
|
match: /\i=null;(?=.{0,80}getPremiumSubscription\(\))/g,
|
||||||
replace: ";if(Vencord.Api.Notices.currentNotice)return false"
|
replace: "if(Vencord.Api.Notices.currentNotice)return false;$&"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
match: /(?<=,NOTICE_DISMISS:function\(\i\){)(?=if\(null==(\i)\))/,
|
match: /(?<=,NOTICE_DISMISS:function\(\i\){)return null!=(\i)/,
|
||||||
replace: (_, notice) => `if(${notice}.id=="VencordNotice")return(${notice}=null,Vencord.Api.Notices.nextNotice(),true);`
|
replace: "if($1.id==\"VencordNotice\")return($1=null,Vencord.Api.Notices.nextNotice(),true);$&"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -27,15 +27,15 @@ export default definePlugin({
|
|||||||
{
|
{
|
||||||
find: "Messages.DISCODO_DISABLED",
|
find: "Messages.DISCODO_DISABLED",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(Messages\.DISCODO_DISABLED.+?return)(\(.{0,75}?tutorialContainer.+?}\))(?=}function)/,
|
match: /(?<=Messages\.DISCODO_DISABLED.+?return)(\(.{0,75}?tutorialContainer.+?}\))(?=}function)/,
|
||||||
replace: "$1[$2].concat(Vencord.Api.ServerList.renderAll(Vencord.Api.ServerList.ServerListRenderPosition.Above))"
|
replace: "[$1].concat(Vencord.Api.ServerList.renderAll(Vencord.Api.ServerList.ServerListRenderPosition.Above))"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
find: "Messages.SERVERS,children",
|
find: "Messages.SERVERS,children",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(Messages\.SERVERS,children:)(.+?default:return null\}\}\)\))/,
|
match: /(?<=Messages\.SERVERS,children:).+?default:return null\}\}\)/,
|
||||||
replace: "$1Vencord.Api.ServerList.renderAll(Vencord.Api.ServerList.ServerListRenderPosition.In).concat($2)"
|
replace: "Vencord.Api.ServerList.renderAll(Vencord.Api.ServerList.ServerListRenderPosition.In).concat($&)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -1,38 +0,0 @@
|
|||||||
/*
|
|
||||||
* Vencord, a modification for Discord's desktop app
|
|
||||||
* Copyright (c) 2022 Vendicated and contributors
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { Devs } from "@utils/constants";
|
|
||||||
import definePlugin from "@utils/types";
|
|
||||||
|
|
||||||
export default definePlugin({
|
|
||||||
name: "SettingsStoreAPI",
|
|
||||||
description: "Patches Discord's SettingsStores to expose their group and name",
|
|
||||||
authors: [Devs.Nuckyz],
|
|
||||||
|
|
||||||
patches: [
|
|
||||||
{
|
|
||||||
find: '"textAndImages","renderSpoilers"',
|
|
||||||
replacement: [
|
|
||||||
{
|
|
||||||
match: /(?<=INFREQUENT_USER_ACTION.{0,20}),useSetting:function/,
|
|
||||||
replace: ",settingsStoreApiGroup:arguments[0],settingsStoreApiName:arguments[1]$&"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
@ -26,7 +26,7 @@ export default definePlugin({
|
|||||||
required: true,
|
required: true,
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
find: "TRACKING_URL:",
|
find: "AnalyticsActionHandlers.handle",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /^.+$/,
|
match: /^.+$/,
|
||||||
replace: "()=>{}",
|
replace: "()=>{}",
|
||||||
@ -43,20 +43,21 @@ export default definePlugin({
|
|||||||
find: ".METRICS,",
|
find: ".METRICS,",
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
match: /this\._intervalId.+?12e4\)/,
|
match: /this\._intervalId=/,
|
||||||
replace: ""
|
replace: "this._intervalId=undefined&&"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
match: /(?<=increment=function\(\i\){)/,
|
match: /(increment\(\i\){)/,
|
||||||
replace: "return;"
|
replace: "$1return;"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
find: ".installedLogHooks)",
|
find: ".installedLogHooks)",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /if\(\i\.getDebugLogging\(\)&&!\i\.installedLogHooks\)/,
|
// if getDebugLogging() returns false, the hooks don't get installed.
|
||||||
replace: "if(false)"
|
match: "getDebugLogging(){",
|
||||||
|
replace: "getDebugLogging(){return false;"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
@ -39,8 +39,9 @@ export default definePlugin({
|
|||||||
addContextMenuPatch("user-settings-cog", children => () => {
|
addContextMenuPatch("user-settings-cog", children => () => {
|
||||||
const section = children.find(c => Array.isArray(c) && c.some(it => it?.props?.id === "VencordSettings")) as any;
|
const section = children.find(c => Array.isArray(c) && c.some(it => it?.props?.id === "VencordSettings")) as any;
|
||||||
section?.forEach(c => {
|
section?.forEach(c => {
|
||||||
if (c?.props?.id?.startsWith("Vencord")) {
|
const id = c?.props?.id;
|
||||||
c.props.action = () => SettingsRouter.open(c.props.id);
|
if (id?.startsWith("Vencord") || id?.startsWith("Vesktop")) {
|
||||||
|
c.props.action = () => SettingsRouter.open(id);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -62,26 +63,26 @@ export default definePlugin({
|
|||||||
replacement: {
|
replacement: {
|
||||||
get match() {
|
get match() {
|
||||||
switch (Settings.plugins.Settings.settingsLocation) {
|
switch (Settings.plugins.Settings.settingsLocation) {
|
||||||
case "top": return /\{section:(\i)\.ID\.HEADER,\s*label:(\i)\.\i\.Messages\.USER_SETTINGS\}/;
|
case "top": return /\{section:(\i\.\i)\.HEADER,\s*label:(\i)\.\i\.Messages\.USER_SETTINGS\}/;
|
||||||
case "aboveNitro": return /\{section:(\i)\.ID\.HEADER,\s*label:(\i)\.\i\.Messages\.BILLING_SETTINGS\}/;
|
case "aboveNitro": return /\{section:(\i\.\i)\.HEADER,\s*label:(\i)\.\i\.Messages\.BILLING_SETTINGS\}/;
|
||||||
case "belowNitro": return /\{section:(\i)\.ID\.HEADER,\s*label:(\i)\.\i\.Messages\.APP_SETTINGS\}/;
|
case "belowNitro": return /\{section:(\i\.\i)\.HEADER,\s*label:(\i)\.\i\.Messages\.APP_SETTINGS\}/;
|
||||||
case "belowActivity": return /(?<=\{section:(\i)\.ID\.DIVIDER},)\{section:"changelog"/;
|
case "belowActivity": return /(?<=\{section:(\i\.\i)\.DIVIDER},)\{section:"changelog"/;
|
||||||
case "bottom": return /\{section:(\i)\.ID\.CUSTOM,\s*element:.+?}/;
|
case "bottom": return /\{section:(\i\.\i)\.CUSTOM,\s*element:.+?}/;
|
||||||
case "aboveActivity":
|
case "aboveActivity":
|
||||||
default:
|
default:
|
||||||
return /\{section:(\i)\.ID\.HEADER,\s*label:(\i)\.\i\.Messages\.ACTIVITY_SETTINGS\}/;
|
return /\{section:(\i\.\i)\.HEADER,\s*label:(\i)\.\i\.Messages\.ACTIVITY_SETTINGS\}/;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
replace: "...$self.makeSettingsCategories($1),$&"
|
replace: "...$self.makeSettingsCategories($1),$&"
|
||||||
}
|
}
|
||||||
}],
|
}],
|
||||||
|
|
||||||
customSections: [] as ((ID: Record<string, unknown>) => any)[],
|
customSections: [] as ((SectionTypes: Record<string, unknown>) => any)[],
|
||||||
|
|
||||||
makeSettingsCategories({ ID }: { ID: Record<string, unknown>; }) {
|
makeSettingsCategories(SectionTypes: Record<string, unknown>) {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
section: ID.HEADER,
|
section: SectionTypes.HEADER,
|
||||||
label: "Vencord",
|
label: "Vencord",
|
||||||
className: "vc-settings-header"
|
className: "vc-settings-header"
|
||||||
},
|
},
|
||||||
@ -127,9 +128,9 @@ export default definePlugin({
|
|||||||
element: require("@components/VencordSettings/PatchHelperTab").default,
|
element: require("@components/VencordSettings/PatchHelperTab").default,
|
||||||
className: "vc-patch-helper"
|
className: "vc-patch-helper"
|
||||||
},
|
},
|
||||||
...this.customSections.map(func => func(ID)),
|
...this.customSections.map(func => func(SectionTypes)),
|
||||||
{
|
{
|
||||||
section: ID.DIVIDER
|
section: SectionTypes.DIVIDER
|
||||||
}
|
}
|
||||||
].filter(Boolean);
|
].filter(Boolean);
|
||||||
},
|
},
|
||||||
|
@ -27,15 +27,15 @@ export default definePlugin({
|
|||||||
{
|
{
|
||||||
find: ".displayName=\"MaskedLinkStore\"",
|
find: ".displayName=\"MaskedLinkStore\"",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /\.isTrustedDomain=function\(.\){return.+?};/,
|
match: /(?<=isTrustedDomain\(\i\){)return \i\(\i\)/,
|
||||||
replace: ".isTrustedDomain=function(){return true};"
|
replace: "return true"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
find: '"7z","ade","adp"',
|
find: "isSuspiciousDownload:",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /JSON\.parse\('\[.+?'\)/,
|
match: /function \i\(\i\){(?=.{0,60}\.parse\(\i\))/,
|
||||||
replace: "[]"
|
replace: "$&return null;"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
@ -20,26 +20,19 @@ import { popNotice, showNotice } from "@api/Notices";
|
|||||||
import { Link } from "@components/Link";
|
import { Link } from "@components/Link";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import definePlugin from "@utils/types";
|
import definePlugin from "@utils/types";
|
||||||
import { filters, findByCodeLazy, mapMangledModuleLazy } from "@webpack";
|
import { findByPropsLazy } from "@webpack";
|
||||||
import { FluxDispatcher, Forms, Toasts } from "@webpack/common";
|
import { ApplicationAssetUtils, FluxDispatcher, Forms, Toasts } from "@webpack/common";
|
||||||
|
|
||||||
const assetManager = mapMangledModuleLazy(
|
const RpcUtils = findByPropsLazy("fetchApplicationsRPC", "getRemoteIconURL");
|
||||||
"getAssetImage: size must === [number, number] for Twitch",
|
|
||||||
{
|
|
||||||
getAsset: filters.byCode("apply("),
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const lookupRpcApp = findByCodeLazy(".APPLICATION_RPC(");
|
|
||||||
|
|
||||||
async function lookupAsset(applicationId: string, key: string): Promise<string> {
|
async function lookupAsset(applicationId: string, key: string): Promise<string> {
|
||||||
return (await assetManager.getAsset(applicationId, [key, undefined]))[0];
|
return (await ApplicationAssetUtils.fetchAssetIds(applicationId, [key]))[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
const apps: any = {};
|
const apps: any = {};
|
||||||
async function lookupApp(applicationId: string): Promise<string> {
|
async function lookupApp(applicationId: string): Promise<string> {
|
||||||
const socket: any = {};
|
const socket: any = {};
|
||||||
await lookupRpcApp(socket, applicationId);
|
await RpcUtils.fetchApplicationsRPC(socket, applicationId);
|
||||||
return socket.application;
|
return socket.application;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,6 +51,26 @@ export default definePlugin({
|
|||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
|
|
||||||
|
async handleEvent(e: MessageEvent<any>) {
|
||||||
|
const data = JSON.parse(e.data);
|
||||||
|
|
||||||
|
const { activity } = data;
|
||||||
|
const assets = activity?.assets;
|
||||||
|
|
||||||
|
if (assets?.large_image) assets.large_image = await lookupAsset(activity.application_id, assets.large_image);
|
||||||
|
if (assets?.small_image) assets.small_image = await lookupAsset(activity.application_id, assets.small_image);
|
||||||
|
|
||||||
|
if (activity) {
|
||||||
|
const appId = activity.application_id;
|
||||||
|
apps[appId] ||= await lookupApp(appId);
|
||||||
|
|
||||||
|
const app = apps[appId];
|
||||||
|
activity.name ||= app.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
FluxDispatcher.dispatch({ type: "LOCAL_ACTIVITY_UPDATE", ...data });
|
||||||
|
},
|
||||||
|
|
||||||
async start() {
|
async start() {
|
||||||
// ArmCord comes with its own arRPC implementation, so this plugin just confuses users
|
// ArmCord comes with its own arRPC implementation, so this plugin just confuses users
|
||||||
if ("armcord" in window) return;
|
if ("armcord" in window) return;
|
||||||
@ -65,22 +78,7 @@ export default definePlugin({
|
|||||||
if (ws) ws.close();
|
if (ws) ws.close();
|
||||||
ws = new WebSocket("ws://127.0.0.1:1337"); // try to open WebSocket
|
ws = new WebSocket("ws://127.0.0.1:1337"); // try to open WebSocket
|
||||||
|
|
||||||
ws.onmessage = async e => { // on message, set status to data
|
ws.onmessage = this.handleEvent;
|
||||||
const data = JSON.parse(e.data);
|
|
||||||
|
|
||||||
if (data.activity?.assets?.large_image) data.activity.assets.large_image = await lookupAsset(data.activity.application_id, data.activity.assets.large_image);
|
|
||||||
if (data.activity?.assets?.small_image) data.activity.assets.small_image = await lookupAsset(data.activity.application_id, data.activity.assets.small_image);
|
|
||||||
|
|
||||||
if (data.activity) {
|
|
||||||
const appId = data.activity.application_id;
|
|
||||||
apps[appId] ||= await lookupApp(appId);
|
|
||||||
|
|
||||||
const app = apps[appId];
|
|
||||||
data.activity.name ||= app.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
FluxDispatcher.dispatch({ type: "LOCAL_ACTIVITY_UPDATE", ...data });
|
|
||||||
};
|
|
||||||
|
|
||||||
const connectionSuccessful = await new Promise(res => setTimeout(() => res(ws.readyState === WebSocket.OPEN), 1000)); // check if open after 1s
|
const connectionSuccessful = await new Promise(res => setTimeout(() => res(ws.readyState === WebSocket.OPEN), 1000)); // check if open after 1s
|
||||||
if (!connectionSuccessful) {
|
if (!connectionSuccessful) {
|
@ -27,7 +27,7 @@ export default definePlugin({
|
|||||||
{
|
{
|
||||||
find: "BAN_CONFIRM_TITLE.",
|
find: "BAN_CONFIRM_TITLE.",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /src:\w\(\d+\)/g,
|
match: /src:\i\("\d+"\)/g,
|
||||||
replace: "src: Vencord.Settings.plugins.BANger.source"
|
replace: "src: Vencord.Settings.plugins.BANger.source"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -16,56 +16,34 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Settings } from "@api/Settings";
|
|
||||||
import { classNameFactory } from "@api/Styles";
|
|
||||||
import ErrorBoundary from "@components/ErrorBoundary";
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { findByPropsLazy, findStoreLazy } from "@webpack";
|
import { LazyComponent } from "@utils/react";
|
||||||
import { i18n, React, useStateFromStores } from "@webpack/common";
|
import { find, findByPropsLazy } from "@webpack";
|
||||||
|
import { React, useStateFromStores } from "@webpack/common";
|
||||||
|
|
||||||
const cl = classNameFactory("vc-bf-");
|
import { ExpandedGuildFolderStore, settings } from ".";
|
||||||
const classes = findByPropsLazy("sidebar", "guilds");
|
|
||||||
|
|
||||||
const Animations = findByPropsLazy("a", "animated", "useTransition");
|
const Animations = findByPropsLazy("a", "animated", "useTransition");
|
||||||
const ChannelRTCStore = findStoreLazy("ChannelRTCStore");
|
const GuildsBar = LazyComponent(() => find(m => m.type?.toString().includes('("guildsnav")')));
|
||||||
const ExpandedGuildFolderStore = findStoreLazy("ExpandedGuildFolderStore");
|
|
||||||
|
|
||||||
function Guilds(props: {
|
export default ErrorBoundary.wrap(guildsBarProps => {
|
||||||
className: string;
|
|
||||||
bfGuildFolders: any[];
|
|
||||||
}) {
|
|
||||||
// @ts-expect-error
|
|
||||||
const res = Vencord.Plugins.plugins.BetterFolders.Guilds(props);
|
|
||||||
|
|
||||||
// TODO: Make this better
|
|
||||||
const scrollerProps = res.props.children?.props?.children?.props?.children?.[1]?.props;
|
|
||||||
if (scrollerProps?.children) {
|
|
||||||
const servers = scrollerProps.children.find(c => c?.props?.["aria-label"] === i18n.Messages.SERVERS);
|
|
||||||
if (servers) scrollerProps.children = servers;
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ErrorBoundary.wrap(() => {
|
|
||||||
const expandedFolders = useStateFromStores([ExpandedGuildFolderStore], () => ExpandedGuildFolderStore.getExpandedFolders());
|
const expandedFolders = useStateFromStores([ExpandedGuildFolderStore], () => ExpandedGuildFolderStore.getExpandedFolders());
|
||||||
const fullscreen = useStateFromStores([ChannelRTCStore], () => ChannelRTCStore.isFullscreenInContext());
|
|
||||||
|
|
||||||
const guilds = document.querySelector(`.${classes.guilds}`);
|
|
||||||
|
|
||||||
const visible = !!expandedFolders.size;
|
|
||||||
const className = cl("folder-sidebar", { fullscreen });
|
|
||||||
|
|
||||||
const Sidebar = (
|
const Sidebar = (
|
||||||
<Guilds
|
<GuildsBar
|
||||||
className={classes.guilds}
|
{...guildsBarProps}
|
||||||
bfGuildFolders={Array.from(expandedFolders)}
|
isBetterFolders={true}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!guilds || !Settings.plugins.BetterFolders.sidebarAnim)
|
const visible = !!expandedFolders.size;
|
||||||
|
const guilds = document.querySelector(guildsBarProps.className.split(" ").map(c => `.${c}`).join(""));
|
||||||
|
|
||||||
|
if (!guilds || !settings.store.sidebarAnim) {
|
||||||
return visible
|
return visible
|
||||||
? <div className={className}>{Sidebar}</div>
|
? <div style={{ display: "flex " }}>{Sidebar}</div>
|
||||||
: null;
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Animations.Transition
|
<Animations.Transition
|
||||||
@ -75,11 +53,13 @@ export default ErrorBoundary.wrap(() => {
|
|||||||
leave={{ width: 0 }}
|
leave={{ width: 0 }}
|
||||||
config={{ duration: 200 }}
|
config={{ duration: 200 }}
|
||||||
>
|
>
|
||||||
{(style, show) => show && (
|
{(style, show) =>
|
||||||
<Animations.animated.div style={style} className={className}>
|
show && (
|
||||||
|
<Animations.animated.div style={{ ...style, display: "flex" }}>
|
||||||
{Sidebar}
|
{Sidebar}
|
||||||
</Animations.animated.div>
|
</Animations.animated.div>
|
||||||
)}
|
)
|
||||||
|
}
|
||||||
</Animations.Transition>
|
</Animations.Transition>
|
||||||
);
|
);
|
||||||
}, { noop: true });
|
}, { noop: true });
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
.vc-bf-folder-sidebar [class*="wrapper-"] > [class*="listItem-"]:first-of-type,
|
|
||||||
.vc-bf-folder-sidebar [class*="unreadMentionsIndicator"] {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.vc-bf-folder-sidebar [class*="expandedFolderBackground-"] {
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.vc-bf-folder-sidebar {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.vc-bf-fullscreen {
|
|
||||||
width: 0 !important;
|
|
||||||
visibility: hidden;
|
|
||||||
}
|
|
@ -1,177 +0,0 @@
|
|||||||
/*
|
|
||||||
* Vencord, a modification for Discord's desktop app
|
|
||||||
* Copyright (c) 2023 Vendicated and contributors
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import "./betterFolders.css";
|
|
||||||
|
|
||||||
import { definePluginSettings } from "@api/Settings";
|
|
||||||
import { Devs } from "@utils/constants";
|
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
|
||||||
import { findByPropsLazy, findLazy, findStoreLazy } from "@webpack";
|
|
||||||
import { FluxDispatcher } from "@webpack/common";
|
|
||||||
|
|
||||||
import FolderSideBar from "./FolderSideBar";
|
|
||||||
|
|
||||||
const GuildsTree = findLazy(m => m.prototype?.convertToFolder);
|
|
||||||
const GuildFolderStore = findStoreLazy("SortedGuildStore");
|
|
||||||
const ExpandedFolderStore = findStoreLazy("ExpandedGuildFolderStore");
|
|
||||||
const FolderUtils = findByPropsLazy("move", "toggleGuildFolderExpand");
|
|
||||||
|
|
||||||
const settings = definePluginSettings({
|
|
||||||
sidebar: {
|
|
||||||
type: OptionType.BOOLEAN,
|
|
||||||
description: "Display servers from folder on dedicated sidebar",
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
sidebarAnim: {
|
|
||||||
type: OptionType.BOOLEAN,
|
|
||||||
description: "Animate opening the folder sidebar",
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
closeAllFolders: {
|
|
||||||
type: OptionType.BOOLEAN,
|
|
||||||
description: "Close all folders when selecting a server not in a folder",
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
closeAllHomeButton: {
|
|
||||||
type: OptionType.BOOLEAN,
|
|
||||||
description: "Close all folders when clicking on the home button",
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
closeOthers: {
|
|
||||||
type: OptionType.BOOLEAN,
|
|
||||||
description: "Close other folders when opening a folder",
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
forceOpen: {
|
|
||||||
type: OptionType.BOOLEAN,
|
|
||||||
description: "Force a folder to open when switching to a server of that folder",
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export default definePlugin({
|
|
||||||
name: "BetterFolders",
|
|
||||||
description: "Shows server folders on dedicated sidebar and adds folder related improvements",
|
|
||||||
authors: [Devs.juby, Devs.AutumnVN],
|
|
||||||
patches: [
|
|
||||||
{
|
|
||||||
find: '("guildsnav")',
|
|
||||||
predicate: () => settings.store.sidebar,
|
|
||||||
replacement: [
|
|
||||||
{
|
|
||||||
match: /(\i)\(\){return \i\(\(0,\i\.jsx\)\("div",{className:\i\(\)\.guildSeparator}\)\)}/,
|
|
||||||
replace: "$&$self.Separator=$1;"
|
|
||||||
},
|
|
||||||
|
|
||||||
// Folder component patch
|
|
||||||
{
|
|
||||||
match: /\i\(\(function\(\i,\i,\i\){var \i=\i\.key;return.+\(\i\)},\i\)}\)\)/,
|
|
||||||
replace: "arguments[0].bfHideServers?null:$&"
|
|
||||||
},
|
|
||||||
|
|
||||||
// BEGIN Guilds component patch
|
|
||||||
{
|
|
||||||
match: /(\i)\.themeOverride,(.{15,25}\(function\(\){var \i=)(\i\.\i\.getGuildsTree\(\))/,
|
|
||||||
replace: "$1.themeOverride,bfPatch=$1.bfGuildFolders,$2bfPatch?$self.getGuildsTree(bfPatch,$3):$3"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
match: /return(\(0,\i\.jsx\))(\(\i,{)(folderNode:\i,setNodeRef:\i\.setNodeRef,draggable:!0,.+},\i\.id\));case/,
|
|
||||||
replace: "var bfHideServers=typeof bfPatch==='undefined',folder=$1$2bfHideServers,$3;return !bfHideServers&&arguments[1]?[$1($self.Separator,{}),folder]:folder;case"
|
|
||||||
},
|
|
||||||
// END
|
|
||||||
|
|
||||||
{
|
|
||||||
match: /\("guildsnav"\);return\(0,\i\.jsx\)\(.{1,6},{navigator:\i,children:\(0,\i\.jsx\)\(/,
|
|
||||||
replace: "$&$self.Guilds="
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
find: "APPLICATION_LIBRARY,render",
|
|
||||||
predicate: () => settings.store.sidebar,
|
|
||||||
replacement: {
|
|
||||||
match: /(\(0,\i\.jsx\))\(\i\..,{className:\i\(\)\.guilds,themeOverride:\i}\)/,
|
|
||||||
replace: "$&,$1($self.FolderSideBar,{})"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
find: '("guildsnav")',
|
|
||||||
predicate: () => settings.store.closeAllHomeButton,
|
|
||||||
replacement: {
|
|
||||||
match: ",onClick:function(){if(!__OVERLAY__){",
|
|
||||||
replace: "$&$self.closeFolders();"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
|
|
||||||
settings,
|
|
||||||
|
|
||||||
start() {
|
|
||||||
const getGuildFolder = (id: string) => GuildFolderStore.getGuildFolders().find(f => f.guildIds.includes(id));
|
|
||||||
|
|
||||||
FluxDispatcher.subscribe("CHANNEL_SELECT", this.onSwitch = data => {
|
|
||||||
if (!settings.store.closeAllFolders && !settings.store.forceOpen)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (this.lastGuildId !== data.guildId) {
|
|
||||||
this.lastGuildId = data.guildId;
|
|
||||||
|
|
||||||
const guildFolder = getGuildFolder(data.guildId);
|
|
||||||
if (guildFolder?.folderId) {
|
|
||||||
if (settings.store.forceOpen && !ExpandedFolderStore.isFolderExpanded(guildFolder.folderId))
|
|
||||||
FolderUtils.toggleGuildFolderExpand(guildFolder.folderId);
|
|
||||||
} else if (settings.store.closeAllFolders)
|
|
||||||
this.closeFolders();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
FluxDispatcher.subscribe("TOGGLE_GUILD_FOLDER_EXPAND", this.onToggleFolder = e => {
|
|
||||||
if (settings.store.closeOthers && !this.dispatching)
|
|
||||||
FluxDispatcher.wait(() => {
|
|
||||||
const expandedFolders = ExpandedFolderStore.getExpandedFolders();
|
|
||||||
if (expandedFolders.size > 1) {
|
|
||||||
this.dispatching = true;
|
|
||||||
|
|
||||||
for (const id of expandedFolders) if (id !== e.folderId)
|
|
||||||
FolderUtils.toggleGuildFolderExpand(id);
|
|
||||||
|
|
||||||
this.dispatching = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
stop() {
|
|
||||||
FluxDispatcher.unsubscribe("CHANNEL_SELECT", this.onSwitch);
|
|
||||||
FluxDispatcher.unsubscribe("TOGGLE_GUILD_FOLDER_EXPAND", this.onToggleFolder);
|
|
||||||
},
|
|
||||||
|
|
||||||
FolderSideBar,
|
|
||||||
|
|
||||||
getGuildsTree(folders, oldTree) {
|
|
||||||
const tree = new GuildsTree();
|
|
||||||
tree.root.children = oldTree.root.children.filter(e => folders.includes(e.id));
|
|
||||||
tree.nodes = folders.map(id => oldTree.nodes[id]);
|
|
||||||
return tree;
|
|
||||||
},
|
|
||||||
|
|
||||||
closeFolders() {
|
|
||||||
for (const id of ExpandedFolderStore.getExpandedFolders())
|
|
||||||
FolderUtils.toggleGuildFolderExpand(id);
|
|
||||||
},
|
|
||||||
});
|
|
236
src/plugins/betterFolders/index.tsx
Normal file
236
src/plugins/betterFolders/index.tsx
Normal file
@ -0,0 +1,236 @@
|
|||||||
|
/*
|
||||||
|
* Vencord, a modification for Discord's desktop app
|
||||||
|
* Copyright (c) 2023 Vendicated and contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { definePluginSettings } from "@api/Settings";
|
||||||
|
import { Devs } from "@utils/constants";
|
||||||
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
|
import { findByPropsLazy, findStoreLazy } from "@webpack";
|
||||||
|
import { FluxDispatcher, i18n } from "@webpack/common";
|
||||||
|
|
||||||
|
import FolderSideBar from "./FolderSideBar";
|
||||||
|
|
||||||
|
const GuildFolderStore = findStoreLazy("SortedGuildStore");
|
||||||
|
export const ExpandedGuildFolderStore = findStoreLazy("ExpandedGuildFolderStore");
|
||||||
|
const FolderUtils = findByPropsLazy("move", "toggleGuildFolderExpand");
|
||||||
|
|
||||||
|
let lastGuildId = null as string | null;
|
||||||
|
let dispatchingFoldersClose = false;
|
||||||
|
|
||||||
|
function getGuildFolder(id: string) {
|
||||||
|
return GuildFolderStore.getGuildFolders().find(folder => folder.guildIds.includes(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeFolders() {
|
||||||
|
for (const id of ExpandedGuildFolderStore.getExpandedFolders())
|
||||||
|
FolderUtils.toggleGuildFolderExpand(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const settings = definePluginSettings({
|
||||||
|
sidebar: {
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
description: "Display servers from folder on dedicated sidebar",
|
||||||
|
restartNeeded: true,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
sidebarAnim: {
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
description: "Animate opening the folder sidebar",
|
||||||
|
restartNeeded: true,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
closeAllFolders: {
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
description: "Close all folders when selecting a server not in a folder",
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
closeAllHomeButton: {
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
description: "Close all folders when clicking on the home button",
|
||||||
|
restartNeeded: true,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
closeOthers: {
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
description: "Close other folders when opening a folder",
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
forceOpen: {
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
description: "Force a folder to open when switching to a server of that folder",
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
keepIcons: {
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
description: "Keep showing guild icons in the primary guild bar folder when it's open in the BetterFolders sidebar",
|
||||||
|
restartNeeded: true,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default definePlugin({
|
||||||
|
name: "BetterFolders",
|
||||||
|
description: "Shows server folders on dedicated sidebar and adds folder related improvements",
|
||||||
|
authors: [Devs.juby, Devs.AutumnVN, Devs.Nuckyz],
|
||||||
|
|
||||||
|
settings,
|
||||||
|
|
||||||
|
patches: [
|
||||||
|
{
|
||||||
|
find: '("guildsnav")',
|
||||||
|
predicate: () => settings.store.sidebar,
|
||||||
|
replacement: [
|
||||||
|
// Create the isBetterFolders variable in the GuildsBar component
|
||||||
|
{
|
||||||
|
match: /(?<=let{disableAppDownload:\i=\i\.isPlatformEmbedded,isOverlay:.+?)(?=}=\i,)/,
|
||||||
|
replace: ",isBetterFolders"
|
||||||
|
},
|
||||||
|
// If we are rendering the Better Folders sidebar, we filter out everything but the servers and folders from the GuildsBar Guild List children
|
||||||
|
{
|
||||||
|
match: /lastTargetNode:\i\[\i\.length-1\].+?Fragment.+?\]}\)\]/,
|
||||||
|
replace: '$&.filter($self.makeGuildsBarGuildListFilter(typeof isBetterFolders!=="undefined"?isBetterFolders:false))'
|
||||||
|
},
|
||||||
|
// If we are rendering the Better Folders sidebar, we filter out everything but the scroller for the guild list from the GuildsBar Tree children
|
||||||
|
{
|
||||||
|
match: /unreadMentionsIndicatorBottom,barClassName.+?}\)\]/,
|
||||||
|
replace: '$&.filter($self.makeGuildsBarTreeFilter(typeof isBetterFolders!=="undefined"?isBetterFolders:false))'
|
||||||
|
},
|
||||||
|
// Export the isBetterFolders variable to the folders component
|
||||||
|
{
|
||||||
|
match: /(?<=\.Messages\.SERVERS.+?switch\((\i)\.type\){case \i\.\i\.FOLDER:.+?folderNode:\i,)/,
|
||||||
|
replace: 'isBetterFolders:typeof isBetterFolders!=="undefined"?isBetterFolders:false,'
|
||||||
|
},
|
||||||
|
// Avoid rendering servers that are not in folders in the Better Folders sidebar
|
||||||
|
{
|
||||||
|
match: /(?<=\.Messages\.SERVERS.+?switch\((\i)\.type\){case \i\.\i\.FOLDER:.+?GUILD:)/,
|
||||||
|
replace: 'if((typeof isBetterFolders!=="undefined"?isBetterFolders:false)&&$1.parentId==null)return null;'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
find: ".FOLDER_ITEM_GUILD_ICON_MARGIN);",
|
||||||
|
predicate: () => settings.store.sidebar,
|
||||||
|
replacement: [
|
||||||
|
// Create the isBetterFolders variable in the nested folders component (the parent exports all the props so we don't have to patch it)
|
||||||
|
{
|
||||||
|
match: /(?<=let{folderNode:\i,setNodeRef:\i,)/,
|
||||||
|
replace: "isBetterFolders,"
|
||||||
|
},
|
||||||
|
// If we are rendering the normal GuildsBar sidebar, we make Discord think the folder is always collapsed to show better icons (the mini guild icons) and avoid transitions
|
||||||
|
{
|
||||||
|
predicate: () => settings.store.keepIcons,
|
||||||
|
match: /(?<=let{folderNode:\i,setNodeRef:\i,.+?expanded:(\i).+?;)(?=let)/,
|
||||||
|
replace: '$1=(typeof isBetterFolders!=="undefined"?isBetterFolders:false)?$1:false;'
|
||||||
|
},
|
||||||
|
// If we are rendering the Better Folders sidebar, we filter out folders that are not expanded
|
||||||
|
{
|
||||||
|
match: /(?=return\(0,\i.\i\)\("div")(?<=selected:\i,expanded:(\i),.+?)/,
|
||||||
|
replace: (_, expanded) => `if((typeof isBetterFolders!=="undefined"?isBetterFolders:false)&&!${expanded})return null;`
|
||||||
|
},
|
||||||
|
// Disable expanding and collapsing folders transition in the normal GuildsBar sidebar
|
||||||
|
{
|
||||||
|
predicate: () => !settings.store.keepIcons,
|
||||||
|
match: /(?<=\.Messages\.SERVER_FOLDER_PLACEHOLDER.+?useTransition\)\()/,
|
||||||
|
replace: '(typeof isBetterFolders!=="undefined"?isBetterFolders:false)&&'
|
||||||
|
},
|
||||||
|
// If we are rendering the normal GuildsBar sidebar, we avoid rendering guilds from folders that are expanded
|
||||||
|
{
|
||||||
|
predicate: () => !settings.store.keepIcons,
|
||||||
|
match: /expandedFolderBackground,.+?,(?=\i\(\(\i,\i,\i\)=>{let{key.{0,45}ul)(?<=selected:\i,expanded:(\i),.+?)/,
|
||||||
|
replace: (m, expanded) => `${m}((typeof isBetterFolders!=="undefined"?isBetterFolders:false)||!${expanded})&&`
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
find: "APPLICATION_LIBRARY,render",
|
||||||
|
predicate: () => settings.store.sidebar,
|
||||||
|
replacement: {
|
||||||
|
// Render the Better Folders sidebar
|
||||||
|
match: /(?<=({className:\i\.guilds,themeOverride:\i})\))/,
|
||||||
|
replace: ",$self.FolderSideBar($1)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
find: ".Messages.DISCODO_DISABLED",
|
||||||
|
predicate: () => settings.store.closeAllHomeButton,
|
||||||
|
replacement: {
|
||||||
|
// Close all folders when clicking the home button
|
||||||
|
match: /(?<=onClick:\(\)=>{)(?=.{0,200}"discodo")/,
|
||||||
|
replace: "$self.closeFolders();"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
flux: {
|
||||||
|
CHANNEL_SELECT(data) {
|
||||||
|
if (!settings.store.closeAllFolders && !settings.store.forceOpen)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (lastGuildId !== data.guildId) {
|
||||||
|
lastGuildId = data.guildId;
|
||||||
|
const guildFolder = getGuildFolder(data.guildId);
|
||||||
|
|
||||||
|
if (guildFolder?.folderId) {
|
||||||
|
if (settings.store.forceOpen && !ExpandedGuildFolderStore.isFolderExpanded(guildFolder.folderId)) {
|
||||||
|
FolderUtils.toggleGuildFolderExpand(guildFolder.folderId);
|
||||||
|
}
|
||||||
|
} else if (settings.store.closeAllFolders) {
|
||||||
|
closeFolders();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
TOGGLE_GUILD_FOLDER_EXPAND(data) {
|
||||||
|
if (settings.store.closeOthers && !dispatchingFoldersClose) {
|
||||||
|
dispatchingFoldersClose = true;
|
||||||
|
|
||||||
|
FluxDispatcher.wait(() => {
|
||||||
|
const expandedFolders = ExpandedGuildFolderStore.getExpandedFolders();
|
||||||
|
|
||||||
|
if (expandedFolders.size > 1) {
|
||||||
|
for (const id of expandedFolders) if (id !== data.folderId)
|
||||||
|
FolderUtils.toggleGuildFolderExpand(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatchingFoldersClose = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
makeGuildsBarGuildListFilter(isBetterFolders: boolean) {
|
||||||
|
return child => {
|
||||||
|
if (isBetterFolders) {
|
||||||
|
return child?.props?.["aria-label"] === i18n.Messages.SERVERS;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
makeGuildsBarTreeFilter(isBetterFolders: boolean) {
|
||||||
|
return child => {
|
||||||
|
if (isBetterFolders) {
|
||||||
|
return "onScroll" in child.props;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
FolderSideBar: guildsBarProps => <FolderSideBar {...guildsBarProps} />,
|
||||||
|
|
||||||
|
closeFolders
|
||||||
|
});
|
@ -34,16 +34,18 @@ export default definePlugin({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
find: ".embedGallerySide",
|
find: ".Messages.GIF,",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(?<==(.{1,3})\.alt.{0,20})\?.{0,5}\.Messages\.GIF/,
|
match: /alt:(\i)=(\i\.default\.Messages\.GIF)(?=,[^}]*\}=(\i))/,
|
||||||
replace:
|
replace:
|
||||||
"?($1.alt='GIF',$self.altify($1))",
|
// rename prop so we can always use default value
|
||||||
|
"alt_$$:$1=$self.altify($3)||$2",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
||||||
altify(props: any) {
|
altify(props: any) {
|
||||||
|
props.alt ??= "GIF";
|
||||||
if (props.alt !== "GIF") return props.alt;
|
if (props.alt !== "GIF") return props.alt;
|
||||||
|
|
||||||
let url: string = props.original || props.src;
|
let url: string = props.original || props.src;
|
@ -19,6 +19,9 @@
|
|||||||
import { Settings } from "@api/Settings";
|
import { Settings } from "@api/Settings";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
|
import { findByPropsLazy } from "@webpack";
|
||||||
|
|
||||||
|
const UserPopoutSectionCssClasses = findByPropsLazy("section", "lastSection");
|
||||||
|
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "BetterNotesBox",
|
name: "BetterNotesBox",
|
||||||
@ -29,17 +32,31 @@ export default definePlugin({
|
|||||||
{
|
{
|
||||||
find: "hideNote:",
|
find: "hideNote:",
|
||||||
all: true,
|
all: true,
|
||||||
|
// Some modules match the find but the replacement is returned untouched
|
||||||
|
noWarn: true,
|
||||||
predicate: () => Vencord.Settings.plugins.BetterNotesBox.hide,
|
predicate: () => Vencord.Settings.plugins.BetterNotesBox.hide,
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /hideNote:.+?(?=[,}])/g,
|
match: /hideNote:.+?(?=([,}].*?\)))/g,
|
||||||
replace: "hideNote:true",
|
replace: (m, rest) => {
|
||||||
|
const destructuringMatch = rest.match(/}=.+/);
|
||||||
|
if (destructuringMatch == null) return "hideNote:!0";
|
||||||
|
return m;
|
||||||
}
|
}
|
||||||
}, {
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
find: "Messages.NOTE_PLACEHOLDER",
|
find: "Messages.NOTE_PLACEHOLDER",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /\.NOTE_PLACEHOLDER,/,
|
match: /\.NOTE_PLACEHOLDER,/,
|
||||||
replace: "$&spellCheck:!Vencord.Settings.plugins.BetterNotesBox.noSpellCheck,"
|
replace: "$&spellCheck:!Vencord.Settings.plugins.BetterNotesBox.noSpellCheck,"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
find: ".Messages.NOTE}",
|
||||||
|
replacement: {
|
||||||
|
match: /(?<=return \i\?)null(?=:\(0,\i\.jsxs)/,
|
||||||
|
replace: "$self.patchPadding(arguments[0])"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
@ -56,5 +73,12 @@ export default definePlugin({
|
|||||||
disabled: () => Settings.plugins.BetterNotesBox.hide,
|
disabled: () => Settings.plugins.BetterNotesBox.hide,
|
||||||
default: false
|
default: false
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
patchPadding(e: any) {
|
||||||
|
if (!e.lastSection) return;
|
||||||
|
return (
|
||||||
|
<div className={UserPopoutSectionCssClasses.lastSection}></div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
@ -23,7 +23,7 @@ import { Clipboard, Toasts } from "@webpack/common";
|
|||||||
|
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "BetterRoleDot",
|
name: "BetterRoleDot",
|
||||||
authors: [Devs.Ven],
|
authors: [Devs.Ven, Devs.AutumnVN],
|
||||||
description:
|
description:
|
||||||
"Copy role colour on RoleDot (accessibility setting) click. Also allows using both RoleDot and coloured names simultaneously",
|
"Copy role colour on RoleDot (accessibility setting) click. Also allows using both RoleDot and coloured names simultaneously",
|
||||||
|
|
||||||
@ -38,11 +38,31 @@ export default definePlugin({
|
|||||||
{
|
{
|
||||||
find: '"dot"===',
|
find: '"dot"===',
|
||||||
all: true,
|
all: true,
|
||||||
|
noWarn: true,
|
||||||
predicate: () => Settings.plugins.BetterRoleDot.bothStyles,
|
predicate: () => Settings.plugins.BetterRoleDot.bothStyles,
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /"(?:username|dot)"===\i(?!\.\i)/g,
|
match: /"(?:username|dot)"===\i(?!\.\i)/g,
|
||||||
replace: "true",
|
replace: "true",
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
find: ".ADD_ROLE_A11Y_LABEL",
|
||||||
|
predicate: () => Settings.plugins.BetterRoleDot.copyRoleColorInProfilePopout && !Settings.plugins.BetterRoleDot.bothStyles,
|
||||||
|
noWarn: true,
|
||||||
|
replacement: {
|
||||||
|
match: /"dot"===\i/,
|
||||||
|
replace: "true"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
find: ".roleVerifiedIcon",
|
||||||
|
predicate: () => Settings.plugins.BetterRoleDot.copyRoleColorInProfilePopout && !Settings.plugins.BetterRoleDot.bothStyles,
|
||||||
|
noWarn: true,
|
||||||
|
replacement: {
|
||||||
|
match: /"dot"===\i/,
|
||||||
|
replace: "true"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
@ -50,7 +70,14 @@ export default definePlugin({
|
|||||||
bothStyles: {
|
bothStyles: {
|
||||||
type: OptionType.BOOLEAN,
|
type: OptionType.BOOLEAN,
|
||||||
description: "Show both role dot and coloured names",
|
description: "Show both role dot and coloured names",
|
||||||
|
restartNeeded: true,
|
||||||
default: false,
|
default: false,
|
||||||
|
},
|
||||||
|
copyRoleColorInProfilePopout: {
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
description: "Allow click on role dot in profile popout to copy role color",
|
||||||
|
restartNeeded: true,
|
||||||
|
default: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -29,9 +29,8 @@ export default definePlugin({
|
|||||||
replacement: {
|
replacement: {
|
||||||
// Discord merges multiple props here with Object.assign()
|
// Discord merges multiple props here with Object.assign()
|
||||||
// This patch passes a third object to it with which we override onClick and onContextMenu
|
// This patch passes a third object to it with which we override onClick and onContextMenu
|
||||||
match: /CHAT_ATTACH_UPLOAD_OR_INVITE,onDoubleClick:(.+?:void 0)\},(.{1,3})\)/,
|
match: /CHAT_ATTACH_UPLOAD_OR_INVITE,onDoubleClick:(.+?:void 0),\.\.\.(\i),/,
|
||||||
replace: (m, onDblClick, otherProps) =>
|
replace: "$&onClick:$1,onContextMenu:$2.onClick,",
|
||||||
`${m.slice(0, -1)},{onClick:${onDblClick},onContextMenu:${otherProps}.onClick})`,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
@ -45,11 +45,8 @@ export default definePlugin({
|
|||||||
{
|
{
|
||||||
find: ".embedWrapper,embed",
|
find: ".embedWrapper,embed",
|
||||||
replacement: [{
|
replacement: [{
|
||||||
match: /(\.renderEmbed=.+?(.)=.\.props)(.+?\.embedWrapper)/g,
|
match: /\.embedWrapper/g,
|
||||||
replace: "$1,vcProps=$2$3+(vcProps.channel.nsfw?' vc-nsfw-img':'')"
|
replace: "$&+(this.props.channel.nsfw?' vc-nsfw-img':'')"
|
||||||
}, {
|
|
||||||
match: /(\.renderAttachments=.+?(.)=this\.props)(.+?\.embedWrapper)/g,
|
|
||||||
replace: "$1,vcProps=$2$3+(vcProps.nsfw?' vc-nsfw-img':'')"
|
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
],
|
],
|
@ -73,9 +73,9 @@ export default definePlugin({
|
|||||||
},
|
},
|
||||||
|
|
||||||
patches: [{
|
patches: [{
|
||||||
find: ".renderConnectionStatus=",
|
find: "renderConnectionStatus(){",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(?<=renderConnectionStatus=.+\.channel,children:)\w/,
|
match: /(?<=renderConnectionStatus\(\)\{.+\.channel,children:)\i/,
|
||||||
replace: "[$&, $self.renderTimer(this.props.channel.id)]"
|
replace: "[$&, $self.renderTimer(this.props.channel.id)]"
|
||||||
}
|
}
|
||||||
}],
|
}],
|
@ -34,8 +34,9 @@ export default definePlugin({
|
|||||||
{
|
{
|
||||||
find: ".AVATAR_STATUS_MOBILE_16;",
|
find: ".AVATAR_STATUS_MOBILE_16;",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(\.fromIsMobile,.+?)\i.status/,
|
match: /(?<=fromIsMobile:\i=!0,.+?)status:(\i)/,
|
||||||
replace: (_, rest) => `${rest}"online"`
|
// Rename field to force it to always use "online"
|
||||||
|
replace: 'status_$:$1="online"'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
@ -22,23 +22,15 @@ import { Devs } from "@utils/constants";
|
|||||||
import { isTruthy } from "@utils/guards";
|
import { isTruthy } from "@utils/guards";
|
||||||
import { useAwaiter } from "@utils/react";
|
import { useAwaiter } from "@utils/react";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { filters, findByCodeLazy, findByPropsLazy, mapMangledModuleLazy } from "@webpack";
|
import { findByCodeLazy, findByPropsLazy } from "@webpack";
|
||||||
import { FluxDispatcher, Forms, GuildStore, React, SelectedChannelStore, SelectedGuildStore, UserStore } from "@webpack/common";
|
import { ApplicationAssetUtils, FluxDispatcher, Forms, GuildStore, React, SelectedChannelStore, SelectedGuildStore, UserStore } from "@webpack/common";
|
||||||
|
|
||||||
const ActivityComponent = findByCodeLazy("onOpenGameProfile");
|
const ActivityComponent = findByCodeLazy("onOpenGameProfile");
|
||||||
const ActivityClassName = findByPropsLazy("activity", "buttonColor");
|
const ActivityClassName = findByPropsLazy("activity", "buttonColor");
|
||||||
const Colors = findByPropsLazy("profileColors");
|
const Colors = findByPropsLazy("profileColors");
|
||||||
|
|
||||||
const assetManager = mapMangledModuleLazy(
|
|
||||||
"getAssetImage: size must === [number, number] for Twitch",
|
|
||||||
{
|
|
||||||
getAsset: filters.byCode("apply("),
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
async function getApplicationAsset(key: string): Promise<string> {
|
async function getApplicationAsset(key: string): Promise<string> {
|
||||||
if (/https?:\/\/(cdn|media)\.discordapp\.(com|net)\/attachments\//.test(key)) return "mp:" + key.replace(/https?:\/\/(cdn|media)\.discordapp\.(com|net)\//, "");
|
return (await ApplicationAssetUtils.fetchAssetIds(settings.store.appID!, [key]))[0];
|
||||||
return (await assetManager.getAsset(settings.store.appID, [key, undefined]))[0];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ActivityAssets {
|
interface ActivityAssets {
|
||||||
@ -403,7 +395,7 @@ export default definePlugin({
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Forms.FormText>
|
<Forms.FormText>
|
||||||
Go to <Link href="https://discord.com/developers/applications">Discord Deverloper Portal</Link> to create an application and
|
Go to <Link href="https://discord.com/developers/applications">Discord Developer Portal</Link> to create an application and
|
||||||
get the application ID.
|
get the application ID.
|
||||||
</Forms.FormText>
|
</Forms.FormText>
|
||||||
<Forms.FormText>
|
<Forms.FormText>
|
@ -147,8 +147,8 @@ export default definePlugin({
|
|||||||
replacement: [
|
replacement: [
|
||||||
// patch componentDidMount to replace embed thumbnail and title
|
// patch componentDidMount to replace embed thumbnail and title
|
||||||
{
|
{
|
||||||
match: /(\i).render=function.{0,50}\i\.embed/,
|
match: /render\(\)\{let\{embed:/,
|
||||||
replace: "$1.componentDidMount=$self.embedDidMount,$&"
|
replace: "componentDidMount=$self.embedDidMount;$&"
|
||||||
},
|
},
|
||||||
|
|
||||||
// add dearrow button
|
// add dearrow button
|
||||||
|
@ -27,7 +27,7 @@ export default definePlugin({
|
|||||||
{
|
{
|
||||||
find: ".Messages.BOT_CALL_IDLE_DISCONNECT",
|
find: ".Messages.BOT_CALL_IDLE_DISCONNECT",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(?<=function \i\(\){)(?=.{1,100}\.Messages\.BOT_CALL_IDLE_DISCONNECT)/,
|
match: /(?<=function \i\(\){)(?=.{1,120}\.Messages\.BOT_CALL_IDLE_DISCONNECT)/,
|
||||||
replace: "return;"
|
replace: "return;"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -23,14 +23,12 @@ import { Logger } from "@utils/Logger";
|
|||||||
import { Margins } from "@utils/margins";
|
import { Margins } from "@utils/margins";
|
||||||
import { ModalContent, ModalHeader, ModalRoot, openModalLazy } from "@utils/modal";
|
import { ModalContent, ModalHeader, ModalRoot, openModalLazy } from "@utils/modal";
|
||||||
import definePlugin from "@utils/types";
|
import definePlugin from "@utils/types";
|
||||||
import { findByCodeLazy, findStoreLazy } from "@webpack";
|
import { findByPropsLazy, findStoreLazy } from "@webpack";
|
||||||
import { EmojiStore, FluxDispatcher, Forms, GuildStore, Menu, PermissionStore, React, RestAPI, Toasts, Tooltip, UserStore } from "@webpack/common";
|
import { EmojiStore, FluxDispatcher, Forms, GuildStore, Menu, PermissionsBits, PermissionStore, React, RestAPI, Toasts, Tooltip, UserStore } from "@webpack/common";
|
||||||
import { Promisable } from "type-fest";
|
import { Promisable } from "type-fest";
|
||||||
|
|
||||||
const MANAGE_EMOJIS_AND_STICKERS = 1n << 30n;
|
|
||||||
|
|
||||||
const StickersStore = findStoreLazy("StickersStore");
|
const StickersStore = findStoreLazy("StickersStore");
|
||||||
const uploadEmoji = findByCodeLazy('"EMOJI_UPLOAD_START"', "GUILD_EMOJIS(");
|
const EmojiManager = findByPropsLazy("fetchEmoji", "uploadEmoji", "deleteEmoji");
|
||||||
|
|
||||||
interface Sticker {
|
interface Sticker {
|
||||||
t: "Sticker";
|
t: "Sticker";
|
||||||
@ -108,7 +106,7 @@ async function cloneEmoji(guildId: string, emoji: Emoji) {
|
|||||||
reader.readAsDataURL(data);
|
reader.readAsDataURL(data);
|
||||||
});
|
});
|
||||||
|
|
||||||
return uploadEmoji({
|
return EmojiManager.uploadEmoji({
|
||||||
guildId,
|
guildId,
|
||||||
name: emoji.name.split("~")[0],
|
name: emoji.name.split("~")[0],
|
||||||
image: dataUrl
|
image: dataUrl
|
||||||
@ -120,7 +118,7 @@ function getGuildCandidates(data: Data) {
|
|||||||
|
|
||||||
return Object.values(GuildStore.getGuilds()).filter(g => {
|
return Object.values(GuildStore.getGuilds()).filter(g => {
|
||||||
const canCreate = g.ownerId === meId ||
|
const canCreate = g.ownerId === meId ||
|
||||||
BigInt(PermissionStore.getGuildPermissions({ id: g.id }) & MANAGE_EMOJIS_AND_STICKERS) === MANAGE_EMOJIS_AND_STICKERS;
|
(PermissionStore.getGuildPermissions({ id: g.id }) & PermissionsBits.CREATE_GUILD_EXPRESSIONS) === PermissionsBits.CREATE_GUILD_EXPRESSIONS;
|
||||||
if (!canCreate) return false;
|
if (!canCreate) return false;
|
||||||
|
|
||||||
if (data.t === "Sticker") return true;
|
if (data.t === "Sticker") return true;
|
@ -19,7 +19,7 @@
|
|||||||
import { definePluginSettings } from "@api/Settings";
|
import { definePluginSettings } from "@api/Settings";
|
||||||
import ErrorBoundary from "@components/ErrorBoundary";
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { ErrorCard } from "@components/ErrorCard";
|
import { ErrorCard } from "@components/ErrorCard";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs, isMac } from "@utils/constants";
|
||||||
import { Margins } from "@utils/margins";
|
import { Margins } from "@utils/margins";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { findByPropsLazy } from "@webpack";
|
import { findByPropsLazy } from "@webpack";
|
||||||
@ -33,12 +33,6 @@ const settings = definePluginSettings({
|
|||||||
type: OptionType.BOOLEAN,
|
type: OptionType.BOOLEAN,
|
||||||
default: false,
|
default: false,
|
||||||
restartNeeded: true
|
restartNeeded: true
|
||||||
},
|
|
||||||
forceStagingBanner: {
|
|
||||||
description: "Whether to force Staging banner under user area.",
|
|
||||||
type: OptionType.BOOLEAN,
|
|
||||||
default: false,
|
|
||||||
restartNeeded: true
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -58,7 +52,7 @@ export default definePlugin({
|
|||||||
{
|
{
|
||||||
find: "Object.defineProperties(this,{isDeveloper",
|
find: "Object.defineProperties(this,{isDeveloper",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(?<={isDeveloper:\{[^}]+?,get:function\(\)\{return )\w/,
|
match: /(?<={isDeveloper:\{[^}]+?,get:\(\)=>)\i/,
|
||||||
replace: "true"
|
replace: "true"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -70,25 +64,26 @@ export default definePlugin({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
find: ".isStaff=function(){",
|
find: ".isStaff=()",
|
||||||
predicate: () => settings.store.enableIsStaff,
|
predicate: () => settings.store.enableIsStaff,
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
match: /return\s*?(\i)\.hasFlag\((\i\.\i)\.STAFF\)}/,
|
match: /=>*?(\i)\.hasFlag\((\i\.\i)\.STAFF\)}/,
|
||||||
replace: (_, user, flags) => `return Vencord.Webpack.Common.UserStore.getCurrentUser()?.id===${user}.id||${user}.hasFlag(${flags}.STAFF)}`
|
replace: (_, user, flags) => `=>Vencord.Webpack.Common.UserStore.getCurrentUser()?.id===${user}.id||${user}.hasFlag(${flags}.STAFF)}`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
match: /hasFreePremium=function\(\){return this.isStaff\(\)\s*?\|\|/,
|
match: /hasFreePremium\(\){return this.isStaff\(\)\s*?\|\|/,
|
||||||
replace: "hasFreePremium=function(){return ",
|
replace: "hasFreePremium(){return ",
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
// Fix search history being disabled / broken with isStaff
|
||||||
{
|
{
|
||||||
find: ".Messages.DEV_NOTICE_STAGING",
|
find: '("showNewSearch")',
|
||||||
predicate: () => settings.store.forceStagingBanner,
|
predicate: () => settings.store.enableIsStaff,
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /"staging"===window\.GLOBAL_ENV\.RELEASE_CHANNEL/,
|
match: /(?<=showNewSearch"\);return)\s?/,
|
||||||
replace: "true"
|
replace: "!1&&"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -101,9 +96,8 @@ export default definePlugin({
|
|||||||
],
|
],
|
||||||
|
|
||||||
settingsAboutComponent: () => {
|
settingsAboutComponent: () => {
|
||||||
const isMacOS = navigator.platform.includes("Mac");
|
const modKey = isMac ? "cmd" : "ctrl";
|
||||||
const modKey = isMacOS ? "cmd" : "ctrl";
|
const altKey = isMac ? "opt" : "alt";
|
||||||
const altKey = isMacOS ? "opt" : "alt";
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<Forms.FormTitle tag="h3">More Information</Forms.FormTitle>
|
<Forms.FormTitle tag="h3">More Information</Forms.FormTitle>
|
@ -24,35 +24,33 @@ import { getCurrentGuild } from "@utils/discord";
|
|||||||
import { proxyLazy } from "@utils/lazy";
|
import { proxyLazy } from "@utils/lazy";
|
||||||
import { Logger } from "@utils/Logger";
|
import { Logger } from "@utils/Logger";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { findByCodeLazy, findByPropsLazy, findLazy, findStoreLazy } from "@webpack";
|
import { findByPropsLazy, findStoreLazy } from "@webpack";
|
||||||
import { ChannelStore, EmojiStore, FluxDispatcher, Parser, PermissionStore, UserStore } from "@webpack/common";
|
import { ChannelStore, EmojiStore, FluxDispatcher, lodash, Parser, PermissionStore, UploadHandler, UserSettingsActionCreators, UserStore } from "@webpack/common";
|
||||||
import type { Message } from "discord-types/general";
|
import type { Message } from "discord-types/general";
|
||||||
import { applyPalette, GIFEncoder, quantize } from "gifenc";
|
import { applyPalette, GIFEncoder, quantize } from "gifenc";
|
||||||
import type { ReactElement, ReactNode } from "react";
|
import type { ReactElement, ReactNode } from "react";
|
||||||
|
|
||||||
const DRAFT_TYPE = 0;
|
const DRAFT_TYPE = 0;
|
||||||
const promptToUpload = findByCodeLazy("UPLOAD_FILE_LIMIT_ERROR");
|
|
||||||
const UserSettingsProtoStore = findStoreLazy("UserSettingsProtoStore");
|
|
||||||
const PreloadedUserSettingsProtoHandler = findLazy(m => m.ProtoClass?.typeName === "discord_protos.discord_users.v1.PreloadedUserSettings");
|
|
||||||
const ReaderFactory = findByPropsLazy("readerFactory");
|
|
||||||
const StickerStore = findStoreLazy("StickersStore") as {
|
const StickerStore = findStoreLazy("StickersStore") as {
|
||||||
getPremiumPacks(): StickerPack[];
|
getPremiumPacks(): StickerPack[];
|
||||||
getAllGuildStickers(): Map<string, Sticker[]>;
|
getAllGuildStickers(): Map<string, Sticker[]>;
|
||||||
getStickerById(id: string): Sticker | undefined;
|
getStickerById(id: string): Sticker | undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
function searchProtoClass(localName: string, parentProtoClass: any) {
|
const UserSettingsProtoStore = findStoreLazy("UserSettingsProtoStore");
|
||||||
if (!parentProtoClass) return;
|
const ProtoUtils = findByPropsLazy("BINARY_READ_OPTIONS");
|
||||||
|
|
||||||
const field = parentProtoClass.fields.find(field => field.localName === localName);
|
function searchProtoClassField(localName: string, protoClass: any) {
|
||||||
|
const field = protoClass?.fields?.find((field: any) => field.localName === localName);
|
||||||
if (!field) return;
|
if (!field) return;
|
||||||
|
|
||||||
const getter: any = Object.values(field).find(value => typeof value === "function");
|
const fieldGetter = Object.values(field).find(value => typeof value === "function") as any;
|
||||||
return getter?.();
|
return fieldGetter?.();
|
||||||
}
|
}
|
||||||
|
|
||||||
const AppearanceSettingsProto = proxyLazy(() => searchProtoClass("appearance", PreloadedUserSettingsProtoHandler.ProtoClass));
|
const PreloadedUserSettingsActionCreators = proxyLazy(() => UserSettingsActionCreators.PreloadedUserSettingsActionCreators);
|
||||||
const ClientThemeSettingsProto = proxyLazy(() => searchProtoClass("clientThemeSettings", AppearanceSettingsProto));
|
const AppearanceSettingsActionCreators = proxyLazy(() => searchProtoClassField("appearance", PreloadedUserSettingsActionCreators.ProtoClass));
|
||||||
|
const ClientThemeSettingsActionsCreators = proxyLazy(() => searchProtoClassField("clientThemeSettings", AppearanceSettingsActionCreators));
|
||||||
|
|
||||||
const USE_EXTERNAL_EMOJIS = 1n << 18n;
|
const USE_EXTERNAL_EMOJIS = 1n << 18n;
|
||||||
const USE_EXTERNAL_STICKERS = 1n << 37n;
|
const USE_EXTERNAL_STICKERS = 1n << 37n;
|
||||||
@ -103,6 +101,11 @@ interface StickerPack {
|
|||||||
stickers: Sticker[];
|
stickers: Sticker[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const enum FakeNoticeType {
|
||||||
|
Sticker,
|
||||||
|
Emoji
|
||||||
|
}
|
||||||
|
|
||||||
const fakeNitroEmojiRegex = /\/emojis\/(\d+?)\.(png|webp|gif)/;
|
const fakeNitroEmojiRegex = /\/emojis\/(\d+?)\.(png|webp|gif)/;
|
||||||
const fakeNitroStickerRegex = /\/stickers\/(\d+?)\./;
|
const fakeNitroStickerRegex = /\/stickers\/(\d+?)\./;
|
||||||
const fakeNitroGifStickerRegex = /\/attachments\/\d+?\/\d+?\/(\d+?)\.gif/;
|
const fakeNitroGifStickerRegex = /\/attachments\/\d+?\/\d+?\/(\d+?)\.gif/;
|
||||||
@ -171,31 +174,37 @@ export default definePlugin({
|
|||||||
predicate: () => settings.store.enableEmojiBypass,
|
predicate: () => settings.store.enableEmojiBypass,
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
match: /(?<=(\i)=\i\.intention)/,
|
// Create a variable for the intention of listing the emoji
|
||||||
replace: (_, intention) => `,fakeNitroIntention=${intention}`
|
match: /(?<=,intention:(\i).+?;)/,
|
||||||
|
replace: (_, intention) => `let fakeNitroIntention=${intention};`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// Send the intention of listing the emoji to the nitro permission check functions
|
||||||
match: /\.(?:canUseEmojisEverywhere|canUseAnimatedEmojis)\(\i(?=\))/g,
|
match: /\.(?:canUseEmojisEverywhere|canUseAnimatedEmojis)\(\i(?=\))/g,
|
||||||
replace: '$&,typeof fakeNitroIntention!=="undefined"?fakeNitroIntention:void 0'
|
replace: '$&,typeof fakeNitroIntention!=="undefined"?fakeNitroIntention:void 0'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// Disallow the emoji if the intention doesn't allow it
|
||||||
match: /(&&!\i&&)!(\i)(?=\)return \i\.\i\.DISALLOW_EXTERNAL;)/,
|
match: /(&&!\i&&)!(\i)(?=\)return \i\.\i\.DISALLOW_EXTERNAL;)/,
|
||||||
replace: (_, rest, canUseExternal) => `${rest}(!${canUseExternal}&&(typeof fakeNitroIntention==="undefined"||![${EmojiIntentions.CHAT},${EmojiIntentions.GUILD_STICKER_RELATED_EMOJI}].includes(fakeNitroIntention)))`
|
replace: (_, rest, canUseExternal) => `${rest}(!${canUseExternal}&&(typeof fakeNitroIntention==="undefined"||![${EmojiIntentions.CHAT},${EmojiIntentions.GUILD_STICKER_RELATED_EMOJI}].includes(fakeNitroIntention)))`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// Make the emoji always available if the intention allows it
|
||||||
match: /if\(!\i\.available/,
|
match: /if\(!\i\.available/,
|
||||||
replace: m => `${m}&&(typeof fakeNitroIntention==="undefined"||![${EmojiIntentions.CHAT},${EmojiIntentions.GUILD_STICKER_RELATED_EMOJI}].includes(fakeNitroIntention))`
|
replace: m => `${m}&&(typeof fakeNitroIntention==="undefined"||![${EmojiIntentions.CHAT},${EmojiIntentions.GUILD_STICKER_RELATED_EMOJI}].includes(fakeNitroIntention))`
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
// Allow emojis and animated emojis to be sent everywhere
|
||||||
{
|
{
|
||||||
find: "canUseAnimatedEmojis:function",
|
find: "canUseAnimatedEmojis:function",
|
||||||
predicate: () => settings.store.enableEmojiBypass,
|
predicate: () => settings.store.enableEmojiBypass,
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /((?:canUseEmojisEverywhere|canUseAnimatedEmojis):function\(\i)\){(.+?\))/g,
|
match: /((?:canUseEmojisEverywhere|canUseAnimatedEmojis):function\(\i)\){(.+?\))(?=})/g,
|
||||||
replace: (_, rest, premiumCheck) => `${rest},fakeNitroIntention){${premiumCheck}||fakeNitroIntention==null||[${EmojiIntentions.CHAT},${EmojiIntentions.GUILD_STICKER_RELATED_EMOJI}].includes(fakeNitroIntention)`
|
replace: (_, rest, premiumCheck) => `${rest},fakeNitroIntention){${premiumCheck}||fakeNitroIntention!=null||[${EmojiIntentions.CHAT},${EmojiIntentions.GUILD_STICKER_RELATED_EMOJI}].includes(fakeNitroIntention)`
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
// Allow stickers to be sent everywhere
|
||||||
{
|
{
|
||||||
find: "canUseStickersEverywhere:function",
|
find: "canUseStickersEverywhere:function",
|
||||||
predicate: () => settings.store.enableStickerBypass,
|
predicate: () => settings.store.enableStickerBypass,
|
||||||
@ -204,6 +213,7 @@ export default definePlugin({
|
|||||||
replace: "$&return true;"
|
replace: "$&return true;"
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
// Make stickers always available
|
||||||
{
|
{
|
||||||
find: "\"SENDABLE\"",
|
find: "\"SENDABLE\"",
|
||||||
predicate: () => settings.store.enableStickerBypass,
|
predicate: () => settings.store.enableStickerBypass,
|
||||||
@ -212,13 +222,13 @@ export default definePlugin({
|
|||||||
replace: "true?"
|
replace: "true?"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
// Allow streaming with high quality
|
||||||
{
|
{
|
||||||
find: "canUseHighVideoUploadQuality:function",
|
find: "canUseHighVideoUploadQuality:function",
|
||||||
predicate: () => settings.store.enableStreamQualityBypass,
|
predicate: () => settings.store.enableStreamQualityBypass,
|
||||||
replacement: [
|
replacement: [
|
||||||
"canUseHighVideoUploadQuality",
|
"canUseHighVideoUploadQuality",
|
||||||
// TODO: Remove the last two when they get removed from stable
|
"canStreamQuality",
|
||||||
"(?:canStreamQuality|canStreamHighQuality|canStreamMidQuality)",
|
|
||||||
].map(func => {
|
].map(func => {
|
||||||
return {
|
return {
|
||||||
match: new RegExp(`${func}:function\\(\\i(?:,\\i)?\\){`, "g"),
|
match: new RegExp(`${func}:function\\(\\i(?:,\\i)?\\){`, "g"),
|
||||||
@ -226,14 +236,16 @@ export default definePlugin({
|
|||||||
};
|
};
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
// Remove boost requirements to stream with high quality
|
||||||
{
|
{
|
||||||
find: "STREAM_FPS_OPTION.format",
|
find: "STREAM_FPS_OPTION.format",
|
||||||
predicate: () => settings.store.enableStreamQualityBypass,
|
predicate: () => settings.store.enableStreamQualityBypass,
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(userPremiumType|guildPremiumTier):.{0,10}TIER_\d,?/g,
|
match: /guildPremiumTier:\i\.\i\.TIER_\d,?/g,
|
||||||
replace: ""
|
replace: ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
// Allow client themes to be changeable
|
||||||
{
|
{
|
||||||
find: "canUseClientThemes:function",
|
find: "canUseClientThemes:function",
|
||||||
replacement: {
|
replacement: {
|
||||||
@ -245,19 +257,22 @@ export default definePlugin({
|
|||||||
find: '.displayName="UserSettingsProtoStore"',
|
find: '.displayName="UserSettingsProtoStore"',
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
|
// Overwrite incoming connection settings proto with our local settings
|
||||||
match: /CONNECTION_OPEN:function\((\i)\){/,
|
match: /CONNECTION_OPEN:function\((\i)\){/,
|
||||||
replace: (m, props) => `${m}$self.handleProtoChange(${props}.userSettingsProto,${props}.user);`
|
replace: (m, props) => `${m}$self.handleProtoChange(${props}.userSettingsProto,${props}.user);`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
match: /=(\i)\.local;/,
|
// Overwrite non local proto changes with our local settings
|
||||||
replace: (m, props) => `${m}${props}.local||$self.handleProtoChange(${props}.settings.proto);`
|
match: /let{settings:/,
|
||||||
|
replace: "arguments[0].local||$self.handleProtoChange(arguments[0].settings.proto);$&"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
// Call our function to handle changing the gradient theme when selecting a new one
|
||||||
{
|
{
|
||||||
find: "updateTheme:function",
|
find: ",updateTheme(",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(function \i\(\i\){var (\i)=\i\.backgroundGradientPresetId.+?)(\i\.\i\.updateAsync.+?theme=(.+?);.+?\),\i\))/,
|
match: /(function \i\(\i\){let{backgroundGradientPresetId:(\i).+?)(\i\.\i\.updateAsync.+?theme=(.+?),.+?},\i\))/,
|
||||||
replace: (_, rest, backgroundGradientPresetId, originalCall, theme) => `${rest}$self.handleGradientThemeSelect(${backgroundGradientPresetId},${theme},()=>${originalCall});`
|
replace: (_, rest, backgroundGradientPresetId, originalCall, theme) => `${rest}$self.handleGradientThemeSelect(${backgroundGradientPresetId},${theme},()=>${originalCall});`
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -265,11 +280,13 @@ export default definePlugin({
|
|||||||
find: '["strong","em","u","text","inlineCode","s","spoiler"]',
|
find: '["strong","em","u","text","inlineCode","s","spoiler"]',
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
|
// Call our function to decide whether the emoji link should be kept or not
|
||||||
predicate: () => settings.store.transformEmojis,
|
predicate: () => settings.store.transformEmojis,
|
||||||
match: /1!==(\i)\.length\|\|1!==\i\.length/,
|
match: /1!==(\i)\.length\|\|1!==\i\.length/,
|
||||||
replace: (m, content) => `${m}||$self.shouldKeepEmojiLink(${content}[0])`
|
replace: (m, content) => `${m}||$self.shouldKeepEmojiLink(${content}[0])`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// Patch the rendered message content to add fake nitro emojis or remove sticker links
|
||||||
predicate: () => settings.store.transformEmojis || settings.store.transformStickers,
|
predicate: () => settings.store.transformEmojis || settings.store.transformStickers,
|
||||||
match: /(?=return{hasSpoilerEmbeds:\i,content:(\i)})/,
|
match: /(?=return{hasSpoilerEmbeds:\i,content:(\i)})/,
|
||||||
replace: (_, content) => `${content}=$self.patchFakeNitroEmojisOrRemoveStickersLinks(${content},arguments[2]?.formatInline);`
|
replace: (_, content) => `${content}=$self.patchFakeNitroEmojisOrRemoveStickersLinks(${content},arguments[2]?.formatInline);`
|
||||||
@ -277,45 +294,76 @@ export default definePlugin({
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
find: "renderEmbeds=function",
|
find: "renderEmbeds(",
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
|
// Call our function to decide whether the embed should be ignored or not
|
||||||
predicate: () => settings.store.transformEmojis || settings.store.transformStickers,
|
predicate: () => settings.store.transformEmojis || settings.store.transformStickers,
|
||||||
match: /(renderEmbeds=function\((\i)\){)(.+?embeds\.map\(\(function\((\i)\){)/,
|
match: /(renderEmbeds\((\i)\){)(.+?embeds\.map\((\i)=>{)/,
|
||||||
replace: (_, rest1, message, rest2, embed) => `${rest1}const fakeNitroMessage=${message};${rest2}if($self.shouldIgnoreEmbed(${embed},fakeNitroMessage))return null;`
|
replace: (_, rest1, message, rest2, embed) => `${rest1}const fakeNitroMessage=${message};${rest2}if($self.shouldIgnoreEmbed(${embed},fakeNitroMessage))return null;`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// Patch the stickers array to add fake nitro stickers
|
||||||
predicate: () => settings.store.transformStickers,
|
predicate: () => settings.store.transformStickers,
|
||||||
match: /renderStickersAccessories=function\((\i)\){var (\i)=\(0,\i\.\i\)\(\i\),/,
|
match: /(?<=renderStickersAccessories\((\i)\){let (\i)=\(0,\i\.\i\)\(\i\).+?;)/,
|
||||||
replace: (m, message, stickers) => `${m}${stickers}=$self.patchFakeNitroStickers(${stickers},${message}),`
|
replace: (_, message, stickers) => `${stickers}=$self.patchFakeNitroStickers(${stickers},${message});`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
// Filter attachments to remove fake nitro stickers or emojis
|
||||||
predicate: () => settings.store.transformStickers,
|
predicate: () => settings.store.transformStickers,
|
||||||
match: /renderAttachments=function\(\i\){var (\i)=\i.attachments.+?;/,
|
match: /renderAttachments\(\i\){let{attachments:(\i).+?;/,
|
||||||
replace: (m, attachments) => `${m}${attachments}=$self.filterAttachments(${attachments});`
|
replace: (m, attachments) => `${m}${attachments}=$self.filterAttachments(${attachments});`
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
find: ".STICKER_IN_MESSAGE_HOVER,",
|
find: ".Messages.STICKER_POPOUT_UNJOINED_PRIVATE_GUILD_DESCRIPTION.format",
|
||||||
predicate: () => settings.store.transformStickers,
|
predicate: () => settings.store.transformStickers,
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
match: /var (\i)=\i\.renderableSticker,.{0,50}closePopout.+?channel:\i,closePopout:\i,/,
|
// Export the renderable sticker to be used in the fake nitro sticker notice
|
||||||
replace: (m, renderableSticker) => `${m}renderableSticker:${renderableSticker},`
|
match: /let{renderableSticker:(\i).{0,250}isGuildSticker.+?channel:\i,/,
|
||||||
|
replace: (m, renderableSticker) => `${m}fakeNitroRenderableSticker:${renderableSticker},`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
match: /(emojiSection.{0,50}description:)(\i)(?<=(\i)\.sticker,.+?)(?=,)/,
|
// Add the fake nitro sticker notice
|
||||||
replace: (_, rest, reactNode, props) => `${rest}$self.addFakeNotice("STICKER",${reactNode},!!${props}.renderableSticker?.fake)`
|
match: /(let \i,{sticker:\i,channel:\i,closePopout:\i.+?}=(\i).+?;)(.+?description:)(\i)(?=,sticker:\i)/,
|
||||||
|
replace: (_, rest, props, rest2, reactNode) => `${rest}let{fakeNitroRenderableSticker}=${props};${rest2}$self.addFakeNotice(${FakeNoticeType.Sticker},${reactNode},!!fakeNitroRenderableSticker?.fake)`
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
find: ".Messages.EMOJI_POPOUT_PREMIUM_JOINED_GUILD_DESCRIPTION",
|
find: ".EMOJI_UPSELL_POPOUT_MORE_EMOJIS_OPENED,",
|
||||||
predicate: () => settings.store.transformEmojis,
|
predicate: () => settings.store.transformEmojis,
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /((\i)=\i\.node,\i=\i\.expressionSourceGuild)(.+?return )(.{0,450}Messages\.EMOJI_POPOUT_PREMIUM_JOINED_GUILD_DESCRIPTION.+?}\))/,
|
// Export the emoji node to be used in the fake nitro emoji notice
|
||||||
replace: (_, rest1, node, rest2, reactNode) => `${rest1},fakeNitroNode=${node}${rest2}$self.addFakeNotice("EMOJI",${reactNode},fakeNitroNode.fake)`
|
match: /isDiscoverable:\i,shouldHideRoleSubscriptionCTA:\i,(?<={node:(\i),.+?)/,
|
||||||
|
replace: (m, node) => `${m}fakeNitroNode:${node},`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
find: ".Messages.EMOJI_POPOUT_UNJOINED_DISCOVERABLE_GUILD_DESCRIPTION",
|
||||||
|
predicate: () => settings.store.transformEmojis,
|
||||||
|
replacement: {
|
||||||
|
// Add the fake nitro emoji notice
|
||||||
|
match: /(?<=isDiscoverable:\i,emojiComesFromCurrentGuild:\i,.+?}=(\i).+?;)(.+?return )(.{0,1000}\.Messages\.EMOJI_POPOUT_UNJOINED_DISCOVERABLE_GUILD_DESCRIPTION.+?)(?=},)/,
|
||||||
|
replace: (_, props, rest, reactNode) => `let{fakeNitroNode}=${props};${rest}$self.addFakeNotice(${FakeNoticeType.Emoji},${reactNode},!!fakeNitroNode?.fake)`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// Allow using custom app icons
|
||||||
|
{
|
||||||
|
find: "canUsePremiumAppIcons:function",
|
||||||
|
replacement: {
|
||||||
|
match: /canUsePremiumAppIcons:function\(\i\){/,
|
||||||
|
replace: "$&return true;"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// Separate patch for allowing using custom app icons
|
||||||
|
{
|
||||||
|
find: "location:\"AppIconHome\"",
|
||||||
|
replacement: {
|
||||||
|
match: /\i\.\i\.isPremium\(\i\.\i\.getCurrentUser\(\)\)/,
|
||||||
|
replace: "true"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@ -333,26 +381,30 @@ export default definePlugin({
|
|||||||
},
|
},
|
||||||
|
|
||||||
handleProtoChange(proto: any, user: any) {
|
handleProtoChange(proto: any, user: any) {
|
||||||
if (proto == null || typeof proto === "string" || !UserSettingsProtoStore || (!proto.appearance && !AppearanceSettingsProto)) return;
|
if (proto == null || typeof proto === "string" || !UserSettingsProtoStore || !PreloadedUserSettingsActionCreators || !AppearanceSettingsActionCreators || !ClientThemeSettingsActionsCreators) return;
|
||||||
|
|
||||||
const premiumType: number = user?.premium_type ?? UserStore?.getCurrentUser()?.premiumType ?? 0;
|
const premiumType: number = user?.premium_type ?? UserStore?.getCurrentUser()?.premiumType ?? 0;
|
||||||
|
|
||||||
if (premiumType !== 2) {
|
if (premiumType !== 2) {
|
||||||
proto.appearance ??= AppearanceSettingsProto.create();
|
proto.appearance ??= AppearanceSettingsActionCreators.create();
|
||||||
|
|
||||||
if (UserSettingsProtoStore.settings.appearance?.theme != null) {
|
if (UserSettingsProtoStore.settings.appearance?.theme != null) {
|
||||||
proto.appearance.theme = UserSettingsProtoStore.settings.appearance.theme;
|
const appearanceSettingsDummy = AppearanceSettingsActionCreators.create({
|
||||||
|
theme: UserSettingsProtoStore.settings.appearance.theme
|
||||||
|
});
|
||||||
|
|
||||||
|
proto.appearance.theme = appearanceSettingsDummy.theme;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (UserSettingsProtoStore.settings.appearance?.clientThemeSettings?.backgroundGradientPresetId?.value != null && ClientThemeSettingsProto) {
|
if (UserSettingsProtoStore.settings.appearance?.clientThemeSettings?.backgroundGradientPresetId?.value != null) {
|
||||||
const clientThemeSettingsDummyProto = ClientThemeSettingsProto.create({
|
const clientThemeSettingsDummy = ClientThemeSettingsActionsCreators.create({
|
||||||
backgroundGradientPresetId: {
|
backgroundGradientPresetId: {
|
||||||
value: UserSettingsProtoStore.settings.appearance.clientThemeSettings.backgroundGradientPresetId.value
|
value: UserSettingsProtoStore.settings.appearance.clientThemeSettings.backgroundGradientPresetId.value
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
proto.appearance.clientThemeSettings ??= clientThemeSettingsDummyProto;
|
proto.appearance.clientThemeSettings ??= clientThemeSettingsDummy;
|
||||||
proto.appearance.clientThemeSettings.backgroundGradientPresetId = clientThemeSettingsDummyProto.backgroundGradientPresetId;
|
proto.appearance.clientThemeSettings.backgroundGradientPresetId = clientThemeSettingsDummy.backgroundGradientPresetId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -361,26 +413,26 @@ export default definePlugin({
|
|||||||
const premiumType = UserStore?.getCurrentUser()?.premiumType ?? 0;
|
const premiumType = UserStore?.getCurrentUser()?.premiumType ?? 0;
|
||||||
if (premiumType === 2 || backgroundGradientPresetId == null) return original();
|
if (premiumType === 2 || backgroundGradientPresetId == null) return original();
|
||||||
|
|
||||||
if (!AppearanceSettingsProto || !ClientThemeSettingsProto || !ReaderFactory) return;
|
if (!PreloadedUserSettingsActionCreators || !AppearanceSettingsActionCreators || !ClientThemeSettingsActionsCreators || !ProtoUtils) return;
|
||||||
|
|
||||||
const currentAppearanceProto = PreloadedUserSettingsProtoHandler.getCurrentValue().appearance;
|
const currentAppearanceSettings = PreloadedUserSettingsActionCreators.getCurrentValue().appearance;
|
||||||
|
|
||||||
const newAppearanceProto = currentAppearanceProto != null
|
const newAppearanceProto = currentAppearanceSettings != null
|
||||||
? AppearanceSettingsProto.fromBinary(AppearanceSettingsProto.toBinary(currentAppearanceProto), ReaderFactory)
|
? AppearanceSettingsActionCreators.fromBinary(AppearanceSettingsActionCreators.toBinary(currentAppearanceSettings), ProtoUtils.BINARY_READ_OPTIONS)
|
||||||
: AppearanceSettingsProto.create();
|
: AppearanceSettingsActionCreators.create();
|
||||||
|
|
||||||
newAppearanceProto.theme = theme;
|
newAppearanceProto.theme = theme;
|
||||||
|
|
||||||
const clientThemeSettingsDummyProto = ClientThemeSettingsProto.create({
|
const clientThemeSettingsDummy = ClientThemeSettingsActionsCreators.create({
|
||||||
backgroundGradientPresetId: {
|
backgroundGradientPresetId: {
|
||||||
value: backgroundGradientPresetId
|
value: backgroundGradientPresetId
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
newAppearanceProto.clientThemeSettings ??= clientThemeSettingsDummyProto;
|
newAppearanceProto.clientThemeSettings ??= clientThemeSettingsDummy;
|
||||||
newAppearanceProto.clientThemeSettings.backgroundGradientPresetId = clientThemeSettingsDummyProto.backgroundGradientPresetId;
|
newAppearanceProto.clientThemeSettings.backgroundGradientPresetId = clientThemeSettingsDummy.backgroundGradientPresetId;
|
||||||
|
|
||||||
const proto = PreloadedUserSettingsProtoHandler.ProtoClass.create();
|
const proto = PreloadedUserSettingsActionCreators.ProtoClass.create();
|
||||||
proto.appearance = newAppearanceProto;
|
proto.appearance = newAppearanceProto;
|
||||||
|
|
||||||
FluxDispatcher.dispatch({
|
FluxDispatcher.dispatch({
|
||||||
@ -506,7 +558,7 @@ export default definePlugin({
|
|||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return modifyChildren(window._.cloneDeep(content));
|
return modifyChildren(lodash.cloneDeep(content));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
new Logger("FakeNitro").error(err);
|
new Logger("FakeNitro").error(err);
|
||||||
return content;
|
return content;
|
||||||
@ -611,18 +663,18 @@ export default definePlugin({
|
|||||||
return link.target && fakeNitroEmojiRegex.test(link.target);
|
return link.target && fakeNitroEmojiRegex.test(link.target);
|
||||||
},
|
},
|
||||||
|
|
||||||
addFakeNotice(type: "STICKER" | "EMOJI", node: Array<ReactNode>, fake: boolean) {
|
addFakeNotice(type: FakeNoticeType, node: Array<ReactNode>, fake: boolean) {
|
||||||
if (!fake) return node;
|
if (!fake) return node;
|
||||||
|
|
||||||
node = Array.isArray(node) ? node : [node];
|
node = Array.isArray(node) ? node : [node];
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case "STICKER": {
|
case FakeNoticeType.Sticker: {
|
||||||
node.push(" This is a FakeNitro sticker and renders like a real sticker only for you. Appears as a link to non-plugin users.");
|
node.push(" This is a FakeNitro sticker and renders like a real sticker only for you. Appears as a link to non-plugin users.");
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
case "EMOJI": {
|
case FakeNoticeType.Emoji: {
|
||||||
node.push(" This is a FakeNitro emoji and renders like a real emoji only for you. Appears as a link to non-plugin users.");
|
node.push(" This is a FakeNitro emoji and renders like a real emoji only for you. Appears as a link to non-plugin users.");
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
@ -703,7 +755,7 @@ export default definePlugin({
|
|||||||
gif.finish();
|
gif.finish();
|
||||||
|
|
||||||
const file = new File([gif.bytesView()], `${stickerId}.gif`, { type: "image/gif" });
|
const file = new File([gif.bytesView()], `${stickerId}.gif`, { type: "image/gif" });
|
||||||
promptToUpload([file], ChannelStore.getChannel(channelId), DRAFT_TYPE);
|
UploadHandler.promptToUpload([file], ChannelStore.getChannel(channelId), DRAFT_TYPE);
|
||||||
},
|
},
|
||||||
|
|
||||||
start() {
|
start() {
|
@ -87,15 +87,15 @@ export default definePlugin({
|
|||||||
authors: [Devs.Alyxia, Devs.Remty],
|
authors: [Devs.Alyxia, Devs.Remty],
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
find: "getUserProfile=",
|
find: "UserProfileStore",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(?<=getUserProfile=function\(\i\){return )(\i\[\i\])/,
|
match: /(?<=getUserProfile\(\i\){return )(\i\[\i\])/,
|
||||||
replace: "$self.colorDecodeHook($1)"
|
replace: "$self.colorDecodeHook($1)"
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
find: ".USER_SETTINGS_PROFILE_THEME_ACCENT",
|
find: ".USER_SETTINGS_PROFILE_THEME_ACCENT",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /RESET_PROFILE_THEME}\)(?<=},color:(\i).+?},color:(\i).+?)/,
|
match: /RESET_PROFILE_THEME}\)(?<=color:(\i),.{0,500}?color:(\i),.{0,500}?)/,
|
||||||
replace: "$&,$self.addCopy3y3Button({primary:$1,accent:$2})"
|
replace: "$&,$self.addCopy3y3Button({primary:$1,accent:$2})"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,5 +2,5 @@
|
|||||||
|
|
||||||
Puts your favorite emoji first in the emoji autocomplete.
|
Puts your favorite emoji first in the emoji autocomplete.
|
||||||
|
|
||||||
![FavEmojis](https://i.imgur.com/mEFCoZG.png)
|
![a screenshot of the favourite emojis section](https://github.com/Vendicated/Vencord/assets/45497981/419c8c16-1afc-46e0-9cc2-20b9c3489711)
|
||||||
![Example](https://i.imgur.com/wY3Tc43.png)
|
![a comparison of the emoji picker before and after enabling this plugin](https://github.com/Vendicated/Vencord/assets/45497981/4f57626d-cfc6-4155-a47c-2eac191231bb)
|
||||||
|
@ -39,20 +39,25 @@ export default definePlugin({
|
|||||||
description: "Puts your favorite emoji first in the emoji autocomplete.",
|
description: "Puts your favorite emoji first in the emoji autocomplete.",
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
find: ".activeCommandOption",
|
find: "renderResults({results:",
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
// = someFunc(a.selectedIndex); ...trackEmojiSearch({ state: theState, isInPopoutExperimental: someBool })
|
// https://regex101.com/r/N7kpLM/1
|
||||||
match: /=\i\(\i\.selectedIndex\);(?=.+?state:(\i),isInPopoutExperiment:\i)/,
|
match: /let \i=.{1,100}renderResults\({results:(\i)\.query\.results,/,
|
||||||
// self.sortEmojis(theState)
|
replace: "$self.sortEmojis($1);$&"
|
||||||
replace: "$&$self.sortEmojis($1);"
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
find: "MAX_AUTOCOMPLETE_RESULTS+",
|
||||||
|
replacement: [
|
||||||
// set maxCount to Infinity so our sortEmojis callback gets the entire list, not just the first 10
|
// set maxCount to Infinity so our sortEmojis callback gets the entire list, not just the first 10
|
||||||
// and remove Discord's emojiResult slice, storing the endIndex on the array for us to use later
|
// and remove Discord's emojiResult slice, storing the endIndex on the array for us to use later
|
||||||
{
|
{
|
||||||
|
// https://regex101.com/r/x2mobQ/1
|
||||||
// searchEmojis(...,maxCount: stuff) ... endEmojis = emojis.slice(0, maxCount - gifResults.length)
|
// searchEmojis(...,maxCount: stuff) ... endEmojis = emojis.slice(0, maxCount - gifResults.length)
|
||||||
match: /,maxCount:(\i)(.+?)=(\i)\.slice\(0,(\1-\i\.length)\)/,
|
match: /,maxCount:(\i)(.{1,500}\i)=(\i)\.slice\(0,(\i-\i\.length)\)/,
|
||||||
// ,maxCount:Infinity ... endEmojis = (emojis.sliceTo = n, emojis)
|
// ,maxCount:Infinity ... endEmojis = (emojis.sliceTo = n, emojis)
|
||||||
replace: ",maxCount:Infinity$2=($3.sliceTo = $4, $3)"
|
replace: ",maxCount:Infinity$2=($3.sliceTo = $4, $3)"
|
||||||
}
|
}
|
||||||
|
@ -2,4 +2,4 @@
|
|||||||
|
|
||||||
Adds a search bar to favorite gifs.
|
Adds a search bar to favorite gifs.
|
||||||
|
|
||||||
![Screenshot](https://i.imgur.com/Bcgb7PD.png)
|
![Screenshot](https://github.com/Vendicated/Vencord/assets/45497981/19552adc-d921-4153-976e-e9361dc8fdaf)
|
||||||
|
@ -91,13 +91,13 @@ export default definePlugin({
|
|||||||
|
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
find: "renderCategoryExtras",
|
find: "renderHeaderContent()",
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
// https://regex101.com/r/4uHtTE/1
|
// https://regex101.com/r/07gpzP/1
|
||||||
// ($1 renderHeaderContent=function { ... switch (x) ... case FAVORITES:return) ($2) ($3 case default:return r.jsx(($<searchComp>), {...props}))
|
// ($1 renderHeaderContent=function { ... switch (x) ... case FAVORITES:return) ($2) ($3 case default:return r.jsx(($<searchComp>), {...props}))
|
||||||
match: /(renderHeaderContent=function.{1,150}FAVORITES:return)(.{1,150};)(case.{1,200}default:return\(0,\i\.jsx\)\((?<searchComp>\i\.\i))/,
|
match: /(renderHeaderContent\(\).{1,150}FAVORITES:return)(.{1,150});(case.{1,200}default:return\(0,\i\.jsx\)\((?<searchComp>\i\..{1,10}),)/,
|
||||||
replace: "$1 this.state.resultType === \"Favorites\" ? $self.renderSearchBar(this, $<searchComp>) : $2; $3"
|
replace: "$1 this.state.resultType === 'Favorites' ? $self.renderSearchBar(this, $<searchComp>) : $2;$3"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// to persist filtered favorites when component re-renders.
|
// to persist filtered favorites when component re-renders.
|
||||||
@ -182,7 +182,7 @@ function SearchBar({ instance, SearchBarComponent }: { instance: Instance; Searc
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
autoFocus={true}
|
autoFocus={true}
|
||||||
className={containerClasses.searchBar}
|
className={containerClasses.searchBar}
|
||||||
size={SearchBarComponent.Sizes.MEDIUM}
|
size={SearchBarComponent.Sizes.SMALL}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
onClear={() => {
|
onClear={() => {
|
||||||
setQuery("");
|
setQuery("");
|
||||||
|
@ -1,56 +0,0 @@
|
|||||||
/*
|
|
||||||
* Vencord, a modification for Discord's desktop app
|
|
||||||
* Copyright (c) 2023 Vendicated and contributors
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { Devs } from "@utils/constants";
|
|
||||||
import definePlugin from "@utils/types";
|
|
||||||
import { Forms } from "@webpack/common";
|
|
||||||
|
|
||||||
export default definePlugin({
|
|
||||||
name: "FixInbox",
|
|
||||||
description: "Fixes the Unreads Inbox from crashing Discord when you're in lots of guilds.",
|
|
||||||
authors: [Devs.Megu],
|
|
||||||
|
|
||||||
patches: [{
|
|
||||||
find: "INBOX_OPEN:function",
|
|
||||||
replacement: {
|
|
||||||
// This function normally dispatches a subscribe event to every guild.
|
|
||||||
// this is badbadbadbadbad so we just get rid of it.
|
|
||||||
match: /INBOX_OPEN:function.+?\{/,
|
|
||||||
replace: "$&return true;"
|
|
||||||
}
|
|
||||||
}],
|
|
||||||
|
|
||||||
settingsAboutComponent() {
|
|
||||||
return (
|
|
||||||
<Forms.FormSection>
|
|
||||||
<Forms.FormTitle tag="h3">What's the problem?</Forms.FormTitle>
|
|
||||||
<Forms.FormText style={{ marginBottom: 8 }}>
|
|
||||||
By default, Discord emits a GUILD_SUBSCRIPTIONS event for every guild you're in.
|
|
||||||
When you're in a lot of guilds, this can cause the gateway to ratelimit you.
|
|
||||||
This causes the client to crash and get stuck in an infinite ratelimit loop as it tries to reconnect.
|
|
||||||
</Forms.FormText>
|
|
||||||
|
|
||||||
<Forms.FormTitle tag="h3">How does it work?</Forms.FormTitle>
|
|
||||||
<Forms.FormText>
|
|
||||||
This plugin works by stopping the client from sending GUILD_SUBSCRIPTIONS events to the gateway when you open the unreads inbox.
|
|
||||||
This means that not all unreads will be shown, instead only already-subscribed guilds' unreads will be shown, but your client won't crash anymore.
|
|
||||||
</Forms.FormText>
|
|
||||||
</Forms.FormSection>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
@ -19,6 +19,7 @@
|
|||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import definePlugin from "@utils/types";
|
import definePlugin from "@utils/types";
|
||||||
import { GuildStore } from "@webpack/common";
|
import { GuildStore } from "@webpack/common";
|
||||||
|
import { Channel, User } from "discord-types/general";
|
||||||
|
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "ForceOwnerCrown",
|
name: "ForceOwnerCrown",
|
||||||
@ -26,33 +27,22 @@ export default definePlugin({
|
|||||||
authors: [Devs.D3SOX, Devs.Nickyux],
|
authors: [Devs.D3SOX, Devs.Nickyux],
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
// This is the logic where it decides whether to render the owner crown or not
|
find: "AVATAR_DECORATION_PADDING:",
|
||||||
find: ".renderOwner=",
|
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /isOwner;return null!=(\w+)?&&/g,
|
match: /,isOwner:(\i),/,
|
||||||
replace: "isOwner;if($self.isGuildOwner(this.props)){$1=true;}return null!=$1&&"
|
replace: ",_isOwner:$1=$self.isGuildOwner(e),"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
],
|
],
|
||||||
isGuildOwner(props) {
|
isGuildOwner(props: { user: User, channel: Channel, isOwner: boolean, guildId?: string; }) {
|
||||||
// Check if channel is a Group DM, if so return false
|
if (!props?.user?.id) return props.isOwner;
|
||||||
if (props?.channel?.type === 3) {
|
if (props.channel?.type === 3 /* GROUP_DM */)
|
||||||
return false;
|
return props.isOwner;
|
||||||
}
|
|
||||||
|
|
||||||
// guild id is in props twice, fallback if the first is undefined
|
// guild id is in props twice, fallback if the first is undefined
|
||||||
const guildId = props?.guildId ?? props?.channel?.guild_id;
|
const guildId = props.guildId ?? props.channel?.guild_id;
|
||||||
const userId = props?.user?.id;
|
const userId = props.user.id;
|
||||||
|
|
||||||
if (guildId && userId) {
|
return GuildStore.getGuild(guildId)?.ownerId === userId;
|
||||||
const guild = GuildStore.getGuild(guildId);
|
|
||||||
if (guild) {
|
|
||||||
return guild.ownerId === userId;
|
|
||||||
}
|
|
||||||
console.error("[ForceOwnerCrown] failed to get guild", { guildId, guild, props });
|
|
||||||
} else {
|
|
||||||
console.error("[ForceOwnerCrown] no guildId or userId", { guildId, userId, props });
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
},
|
},
|
||||||
});
|
});
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Vencord, a modification for Discord's desktop app
|
* Vencord, a modification for Discord's desktop app
|
||||||
* Copyright (c) 2022 Vendicated and contributors
|
* Copyright (c) 2023 Vendicated and contributors
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -16,7 +16,7 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ApplicationCommandInputType, sendBotMessage } from "@api/Commands";
|
import { ApplicationCommandInputType, ApplicationCommandOptionType, findOption, sendBotMessage } from "@api/Commands";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import definePlugin from "@utils/types";
|
import definePlugin from "@utils/types";
|
||||||
import { findByPropsLazy } from "@webpack";
|
import { findByPropsLazy } from "@webpack";
|
||||||
@ -35,14 +35,29 @@ export default definePlugin({
|
|||||||
name: "create friend invite",
|
name: "create friend invite",
|
||||||
description: "Generates a friend invite link.",
|
description: "Generates a friend invite link.",
|
||||||
inputType: ApplicationCommandInputType.BOT,
|
inputType: ApplicationCommandInputType.BOT,
|
||||||
execute: async (_, ctx) => {
|
options: [{
|
||||||
if (!UserStore.getCurrentUser().phone)
|
name: "Uses",
|
||||||
|
description: "How many uses?",
|
||||||
|
choices: [
|
||||||
|
{ label: "1", name: "1", value: "1" },
|
||||||
|
{ label: "5", name: "5", value: "5" }
|
||||||
|
],
|
||||||
|
required: false,
|
||||||
|
type: ApplicationCommandOptionType.INTEGER
|
||||||
|
}],
|
||||||
|
|
||||||
|
execute: async (args, ctx) => {
|
||||||
|
const uses = findOption<number>(args, "Uses", 5);
|
||||||
|
|
||||||
|
if (uses === 1 && !UserStore.getCurrentUser().phone)
|
||||||
return sendBotMessage(ctx.channel.id, {
|
return sendBotMessage(ctx.channel.id, {
|
||||||
content: "You need to have a phone number connected to your account to create a friend invite!"
|
content: "You need to have a phone number connected to your account to create a friend invite with 1 use!"
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let invite: any;
|
||||||
|
if (uses === 1) {
|
||||||
const random = uuid.v4();
|
const random = uuid.v4();
|
||||||
const invite = await RestAPI.post({
|
const { body: { invite_suggestions } } = await RestAPI.post({
|
||||||
url: "/friend-finder/find-friends",
|
url: "/friend-finder/find-friends",
|
||||||
body: {
|
body: {
|
||||||
modified_contacts: {
|
modified_contacts: {
|
||||||
@ -50,15 +65,17 @@ export default definePlugin({
|
|||||||
},
|
},
|
||||||
phone_contact_methods_count: 1
|
phone_contact_methods_count: 1
|
||||||
}
|
}
|
||||||
}).then(res =>
|
});
|
||||||
FriendInvites.createFriendInvite({
|
invite = await FriendInvites.createFriendInvite({
|
||||||
code: res.body.invite_suggestions[0][3],
|
code: invite_suggestions[0][3],
|
||||||
recipient_phone_number_or_email: random,
|
recipient_phone_number_or_email: random,
|
||||||
contact_visibility: 1,
|
contact_visibility: 1,
|
||||||
filter_visibilities: [],
|
filter_visibilities: [],
|
||||||
filtered_invite_suggestions_index: 1
|
filtered_invite_suggestions_index: 1
|
||||||
})
|
});
|
||||||
);
|
} else {
|
||||||
|
invite = await FriendInvites.createFriendInvite();
|
||||||
|
}
|
||||||
|
|
||||||
sendBotMessage(ctx.channel.id, {
|
sendBotMessage(ctx.channel.id, {
|
||||||
content: `
|
content: `
|
||||||
@ -67,7 +84,7 @@ export default definePlugin({
|
|||||||
Max uses: \`${invite.max_uses}\`
|
Max uses: \`${invite.max_uses}\`
|
||||||
`.trim().replace(/\s+/g, " ")
|
`.trim().replace(/\s+/g, " ")
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "view friend invites",
|
name: "view friend invites",
|
||||||
@ -95,7 +112,7 @@ export default definePlugin({
|
|||||||
execute: async (_, ctx) => {
|
execute: async (_, ctx) => {
|
||||||
await FriendInvites.revokeFriendInvites();
|
await FriendInvites.revokeFriendInvites();
|
||||||
|
|
||||||
return void sendBotMessage(ctx.channel.id, {
|
sendBotMessage(ctx.channel.id, {
|
||||||
content: "All friend invites have been revoked."
|
content: "All friend invites have been revoked."
|
||||||
});
|
});
|
||||||
},
|
},
|
@ -16,16 +16,15 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { getSettingStoreLazy } from "@api/SettingsStore";
|
|
||||||
import { disableStyle, enableStyle } from "@api/Styles";
|
import { disableStyle, enableStyle } from "@api/Styles";
|
||||||
import ErrorBoundary from "@components/ErrorBoundary";
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import definePlugin from "@utils/types";
|
import definePlugin from "@utils/types";
|
||||||
import { findByCodeLazy } from "@webpack";
|
import { findByCodeLazy } from "@webpack";
|
||||||
|
import { StatusSettingsStores } from "@webpack/common";
|
||||||
|
|
||||||
import style from "./style.css?managed";
|
import style from "./style.css?managed";
|
||||||
|
|
||||||
const ShowCurrentGame = getSettingStoreLazy<boolean>("status", "showCurrentGame");
|
|
||||||
const Button = findByCodeLazy("Button.Sizes.NONE,disabled:");
|
const Button = findByCodeLazy("Button.Sizes.NONE,disabled:");
|
||||||
|
|
||||||
function makeIcon(showCurrentGame?: boolean) {
|
function makeIcon(showCurrentGame?: boolean) {
|
||||||
@ -50,7 +49,7 @@ function makeIcon(showCurrentGame?: boolean) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function GameActivityToggleButton() {
|
function GameActivityToggleButton() {
|
||||||
const showCurrentGame = ShowCurrentGame?.useSetting();
|
const showCurrentGame = StatusSettingsStores.ShowCurrentGame.useSetting();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
@ -58,7 +57,7 @@ function GameActivityToggleButton() {
|
|||||||
icon={makeIcon(showCurrentGame)}
|
icon={makeIcon(showCurrentGame)}
|
||||||
role="switch"
|
role="switch"
|
||||||
aria-checked={!showCurrentGame}
|
aria-checked={!showCurrentGame}
|
||||||
onClick={() => ShowCurrentGame?.updateSetting(old => !old)}
|
onClick={() => StatusSettingsStores.ShowCurrentGame.updateSetting(old => !old)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -67,7 +66,6 @@ export default definePlugin({
|
|||||||
name: "GameActivityToggle",
|
name: "GameActivityToggle",
|
||||||
description: "Adds a button next to the mic and deafen button to toggle game activity.",
|
description: "Adds a button next to the mic and deafen button to toggle game activity.",
|
||||||
authors: [Devs.Nuckyz, Devs.RuukuLada],
|
authors: [Devs.Nuckyz, Devs.RuukuLada],
|
||||||
dependencies: ["SettingsStoreAPI"],
|
|
||||||
|
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
[class*="withTagAsButton"] {
|
[class*="withTagAsButton"] {
|
||||||
min-width: 88px;
|
min-width: 88px !important;
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@ export default definePlugin({
|
|||||||
patches: [{
|
patches: [{
|
||||||
find: ".handleSelectGIF=",
|
find: ".handleSelectGIF=",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /\.handleSelectGIF=function.+?\{/,
|
match: /\.handleSelectGIF=\i=>\{/,
|
||||||
replace: ".handleSelectGIF=function(gif){return $self.handleSelect(gif);"
|
replace: ".handleSelectGIF=function(gif){return $self.handleSelect(gif);"
|
||||||
}
|
}
|
||||||
}],
|
}],
|
@ -18,8 +18,9 @@
|
|||||||
|
|
||||||
import { definePluginSettings } from "@api/Settings";
|
import { definePluginSettings } from "@api/Settings";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
|
import { proxyLazy } from "@utils/lazy";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { findByPropsLazy } from "@webpack";
|
import { findByProps, findByPropsLazy } from "@webpack";
|
||||||
import { ContextMenu, FluxDispatcher, Menu } from "@webpack/common";
|
import { ContextMenu, FluxDispatcher, Menu } from "@webpack/common";
|
||||||
import { Channel, Message } from "discord-types/general";
|
import { Channel, Message } from "discord-types/general";
|
||||||
|
|
||||||
@ -50,6 +51,7 @@ const settings = definePluginSettings({
|
|||||||
}>();
|
}>();
|
||||||
|
|
||||||
const MessageActions = findByPropsLazy("sendGreetMessage");
|
const MessageActions = findByPropsLazy("sendGreetMessage");
|
||||||
|
const WELCOME_STICKERS = proxyLazy(() => findByProps("WELCOME_STICKERS")?.WELCOME_STICKERS);
|
||||||
|
|
||||||
function greet(channel: Channel, message: Message, stickers: string[]) {
|
function greet(channel: Channel, message: Message, stickers: string[]) {
|
||||||
const options = MessageActions.getSendMessageOptionsForReply({
|
const options = MessageActions.getSendMessageOptionsForReply({
|
||||||
@ -75,7 +77,7 @@ function greet(channel: Channel, message: Message, stickers: string[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function GreetMenu({ stickers, channel, message }: { stickers: Sticker[], message: Message, channel: Channel; }) {
|
function GreetMenu({ channel, message }: { message: Message, channel: Channel; }) {
|
||||||
const s = settings.use(["greetMode", "multiGreetChoices"]);
|
const s = settings.use(["greetMode", "multiGreetChoices"]);
|
||||||
const { greetMode, multiGreetChoices = [] } = s;
|
const { greetMode, multiGreetChoices = [] } = s;
|
||||||
|
|
||||||
@ -105,7 +107,7 @@ function GreetMenu({ stickers, channel, message }: { stickers: Sticker[], messag
|
|||||||
<Menu.MenuGroup
|
<Menu.MenuGroup
|
||||||
label="Greet Stickers"
|
label="Greet Stickers"
|
||||||
>
|
>
|
||||||
{stickers.map(sticker => (
|
{WELCOME_STICKERS.map(sticker => (
|
||||||
<Menu.MenuItem
|
<Menu.MenuItem
|
||||||
key={sticker.id}
|
key={sticker.id}
|
||||||
id={"greet-" + sticker.id}
|
id={"greet-" + sticker.id}
|
||||||
@ -123,7 +125,7 @@ function GreetMenu({ stickers, channel, message }: { stickers: Sticker[], messag
|
|||||||
label="Unholy Multi-Greet"
|
label="Unholy Multi-Greet"
|
||||||
id="unholy-multi-greet"
|
id="unholy-multi-greet"
|
||||||
>
|
>
|
||||||
{stickers.map(sticker => {
|
{WELCOME_STICKERS.map(sticker => {
|
||||||
const checked = multiGreetChoices.some(s => s === sticker.id);
|
const checked = multiGreetChoices.some(s => s === sticker.id);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -168,21 +170,20 @@ export default definePlugin({
|
|||||||
{
|
{
|
||||||
find: "Messages.WELCOME_CTA_LABEL",
|
find: "Messages.WELCOME_CTA_LABEL",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /innerClassName:\i\(\).welcomeCTAButton,(?<=%\i\.length;return (\i)\[\i\].+?)/,
|
match: /innerClassName:\i\.welcomeCTAButton,(?<={channel:\i,message:\i}=(\i).{0,400}?)/,
|
||||||
replace: "$&onContextMenu:(e)=>$self.pickSticker(e,$1,arguments[0]),"
|
replace: "$&onContextMenu:(vcEvent)=>$self.pickSticker(vcEvent, $1),"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
pickSticker(
|
pickSticker(
|
||||||
event: React.UIEvent,
|
event: React.UIEvent,
|
||||||
stickers: Sticker[],
|
|
||||||
props: {
|
props: {
|
||||||
channel: Channel,
|
channel: Channel,
|
||||||
message: Message;
|
message: Message;
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
if (!(props.message as any).deleted)
|
if (!(props.message as any).deleted)
|
||||||
ContextMenu.open(event, () => <GreetMenu stickers={stickers} {...props} />);
|
ContextMenu.open(event, () => <GreetMenu {...props} />);
|
||||||
}
|
}
|
||||||
});
|
});
|
@ -25,10 +25,10 @@ export default definePlugin({
|
|||||||
authors: [Devs.botato, Devs.Animal],
|
authors: [Devs.botato, Devs.Animal],
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
find: "),{hasFlag:",
|
find: "hasFlag:{writable",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(if\((.{1,2})<=1<<30\)return)/,
|
match: /if\((\i)<=(?:1<<30|1073741824)\)return/,
|
||||||
replace: "if($2===(1<<20)){return false};$1",
|
replace: "if($1===(1<<20))return false;$&",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
@ -1,233 +0,0 @@
|
|||||||
/*
|
|
||||||
* Vencord, a modification for Discord's desktop app
|
|
||||||
* Copyright (c) 2022 Vendicated and contributors
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import * as DataStore from "@api/DataStore";
|
|
||||||
import ErrorBoundary from "@components/ErrorBoundary";
|
|
||||||
import { Devs } from "@utils/constants";
|
|
||||||
import { useForceUpdater } from "@utils/react";
|
|
||||||
import definePlugin from "@utils/types";
|
|
||||||
import { findByPropsLazy, findStoreLazy } from "@webpack";
|
|
||||||
import { Tooltip } from "webpack/common";
|
|
||||||
|
|
||||||
const enum ActivitiesTypes {
|
|
||||||
Game,
|
|
||||||
Embedded
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IgnoredActivity {
|
|
||||||
id: string;
|
|
||||||
type: ActivitiesTypes;
|
|
||||||
}
|
|
||||||
|
|
||||||
const RegisteredGamesClasses = findByPropsLazy("overlayToggleIconOff", "overlayToggleIconOn");
|
|
||||||
const TryItOutClasses = findByPropsLazy("tryItOutBadge", "tryItOutBadgeIcon");
|
|
||||||
const BaseShapeRoundClasses = findByPropsLazy("baseShapeRound", "baseShapeRoundLeft", "baseShapeRoundRight");
|
|
||||||
const RunningGameStore = findStoreLazy("RunningGameStore");
|
|
||||||
|
|
||||||
function ToggleIconOff() {
|
|
||||||
return (
|
|
||||||
<svg
|
|
||||||
className={RegisteredGamesClasses.overlayToggleIconOff}
|
|
||||||
height="24"
|
|
||||||
width="24"
|
|
||||||
viewBox="0 2.2 32 26"
|
|
||||||
aria-hidden={true}
|
|
||||||
role="img"
|
|
||||||
>
|
|
||||||
<g
|
|
||||||
fill="none"
|
|
||||||
fillRule="evenodd"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
className={RegisteredGamesClasses.fill}
|
|
||||||
fill="currentColor"
|
|
||||||
d="M 16 8 C 7.664063 8 1.25 15.34375 1.25 15.34375 L 0.65625 16 L 1.25 16.65625 C 1.25 16.65625 7.097656 23.324219 14.875 23.9375 C 15.246094 23.984375 15.617188 24 16 24 C 16.382813 24 16.753906 23.984375 17.125 23.9375 C 24.902344 23.324219 30.75 16.65625 30.75 16.65625 L 31.34375 16 L 30.75 15.34375 C 30.75 15.34375 24.335938 8 16 8 Z M 16 10 C 18.203125 10 20.234375 10.601563 22 11.40625 C 22.636719 12.460938 23 13.675781 23 15 C 23 18.613281 20.289063 21.582031 16.78125 21.96875 C 16.761719 21.972656 16.738281 21.964844 16.71875 21.96875 C 16.480469 21.980469 16.242188 22 16 22 C 15.734375 22 15.476563 21.984375 15.21875 21.96875 C 11.710938 21.582031 9 18.613281 9 15 C 9 13.695313 9.351563 12.480469 9.96875 11.4375 L 9.9375 11.4375 C 11.71875 10.617188 13.773438 10 16 10 Z M 16 12 C 14.34375 12 13 13.34375 13 15 C 13 16.65625 14.34375 18 16 18 C 17.65625 18 19 16.65625 19 15 C 19 13.34375 17.65625 12 16 12 Z M 7.25 12.9375 C 7.09375 13.609375 7 14.285156 7 15 C 7 16.753906 7.5 18.394531 8.375 19.78125 C 5.855469 18.324219 4.105469 16.585938 3.53125 16 C 4.011719 15.507813 5.351563 14.203125 7.25 12.9375 Z M 24.75 12.9375 C 26.648438 14.203125 27.988281 15.507813 28.46875 16 C 27.894531 16.585938 26.144531 18.324219 23.625 19.78125 C 24.5 18.394531 25 16.753906 25 15 C 25 14.285156 24.90625 13.601563 24.75 12.9375 Z"
|
|
||||||
/>
|
|
||||||
<rect
|
|
||||||
className={RegisteredGamesClasses.fill}
|
|
||||||
x="3"
|
|
||||||
y="26"
|
|
||||||
width="26"
|
|
||||||
height="2"
|
|
||||||
transform="rotate(-45 2 20)"
|
|
||||||
/>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function ToggleIconOn({ forceWhite }: { forceWhite?: boolean; }) {
|
|
||||||
return (
|
|
||||||
<svg
|
|
||||||
className={RegisteredGamesClasses.overlayToggleIconOn}
|
|
||||||
height="24"
|
|
||||||
width="24"
|
|
||||||
viewBox="0 2.2 32 26"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
className={forceWhite ? "" : RegisteredGamesClasses.fill}
|
|
||||||
fill={forceWhite ? "var(--white-500)" : ""}
|
|
||||||
d="M 16 8 C 7.664063 8 1.25 15.34375 1.25 15.34375 L 0.65625 16 L 1.25 16.65625 C 1.25 16.65625 7.097656 23.324219 14.875 23.9375 C 15.246094 23.984375 15.617188 24 16 24 C 16.382813 24 16.753906 23.984375 17.125 23.9375 C 24.902344 23.324219 30.75 16.65625 30.75 16.65625 L 31.34375 16 L 30.75 15.34375 C 30.75 15.34375 24.335938 8 16 8 Z M 16 10 C 18.203125 10 20.234375 10.601563 22 11.40625 C 22.636719 12.460938 23 13.675781 23 15 C 23 18.613281 20.289063 21.582031 16.78125 21.96875 C 16.761719 21.972656 16.738281 21.964844 16.71875 21.96875 C 16.480469 21.980469 16.242188 22 16 22 C 15.734375 22 15.476563 21.984375 15.21875 21.96875 C 11.710938 21.582031 9 18.613281 9 15 C 9 13.695313 9.351563 12.480469 9.96875 11.4375 L 9.9375 11.4375 C 11.71875 10.617188 13.773438 10 16 10 Z M 16 12 C 14.34375 12 13 13.34375 13 15 C 13 16.65625 14.34375 18 16 18 C 17.65625 18 19 16.65625 19 15 C 19 13.34375 17.65625 12 16 12 Z M 7.25 12.9375 C 7.09375 13.609375 7 14.285156 7 15 C 7 16.753906 7.5 18.394531 8.375 19.78125 C 5.855469 18.324219 4.105469 16.585938 3.53125 16 C 4.011719 15.507813 5.351563 14.203125 7.25 12.9375 Z M 24.75 12.9375 C 26.648438 14.203125 27.988281 15.507813 28.46875 16 C 27.894531 16.585938 26.144531 18.324219 23.625 19.78125 C 24.5 18.394531 25 16.753906 25 15 C 25 14.285156 24.90625 13.601563 24.75 12.9375 Z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function ToggleActivityComponent({ activity, forceWhite, forceLeftMargin }: { activity: IgnoredActivity; forceWhite?: boolean; forceLeftMargin?: boolean; }) {
|
|
||||||
const forceUpdate = useForceUpdater();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Tooltip text="Toggle activity">
|
|
||||||
{({ onMouseLeave, onMouseEnter }) => (
|
|
||||||
<div
|
|
||||||
onMouseLeave={onMouseLeave}
|
|
||||||
onMouseEnter={onMouseEnter}
|
|
||||||
className={RegisteredGamesClasses.overlayToggleIcon}
|
|
||||||
role="button"
|
|
||||||
aria-label="Toggle activity"
|
|
||||||
tabIndex={0}
|
|
||||||
style={forceLeftMargin ? { marginLeft: "2px" } : undefined}
|
|
||||||
onClick={e => handleActivityToggle(e, activity, forceUpdate)}
|
|
||||||
>
|
|
||||||
{
|
|
||||||
ignoredActivitiesCache.has(activity.id)
|
|
||||||
? <ToggleIconOff />
|
|
||||||
: <ToggleIconOn forceWhite={forceWhite} />
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</Tooltip>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function ToggleActivityComponentWithBackground({ activity }: { activity: IgnoredActivity; }) {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={`${TryItOutClasses.tryItOutBadge} ${BaseShapeRoundClasses.baseShapeRound}`}
|
|
||||||
style={{ padding: "0px 2px", height: 28 }}
|
|
||||||
>
|
|
||||||
<ToggleActivityComponent activity={activity} forceWhite={true} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleActivityToggle(e: React.MouseEvent<HTMLDivElement, MouseEvent>, activity: IgnoredActivity, forceUpdateComponent: () => void) {
|
|
||||||
e.stopPropagation();
|
|
||||||
if (ignoredActivitiesCache.has(activity.id)) ignoredActivitiesCache.delete(activity.id);
|
|
||||||
else ignoredActivitiesCache.set(activity.id, activity);
|
|
||||||
forceUpdateComponent();
|
|
||||||
saveCacheToDatastore();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function saveCacheToDatastore() {
|
|
||||||
await DataStore.set("IgnoreActivities_ignoredActivities", ignoredActivitiesCache);
|
|
||||||
}
|
|
||||||
|
|
||||||
let ignoredActivitiesCache = new Map<IgnoredActivity["id"], IgnoredActivity>();
|
|
||||||
|
|
||||||
export default definePlugin({
|
|
||||||
name: "IgnoreActivities",
|
|
||||||
authors: [Devs.Nuckyz],
|
|
||||||
description: "Ignore certain activities (like games and actual activities) from showing up on your status. You can configure which ones are ignored from the Registered Games and Activities tabs.",
|
|
||||||
patches: [
|
|
||||||
{
|
|
||||||
find: ".Messages.SETTINGS_GAMES_TOGGLE_OVERLAY",
|
|
||||||
replacement: {
|
|
||||||
match: /!(\i)(\)return null;var \i=(\i)\.overlay.+?children:)(\[.{0,70}overlayStatusText.+?\])(?=}\)}\(\))/,
|
|
||||||
replace: (_, platformCheck, restWithoutPlatformCheck, props, children) => "false"
|
|
||||||
+ `${restWithoutPlatformCheck}`
|
|
||||||
+ `(${platformCheck}?${children}:[])`
|
|
||||||
+ `.concat(Vencord.Plugins.plugins.IgnoreActivities.renderToggleGameActivityButton(${props}))`
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
find: ".overlayBadge",
|
|
||||||
replacement: [
|
|
||||||
{
|
|
||||||
match: /(?<=\(\)\.badgeContainer,children:).{0,50}?name:(\i)\.name.+?null/,
|
|
||||||
replace: (m, props) => `[${m},$self.renderToggleActivityButton(${props})]`
|
|
||||||
},
|
|
||||||
{
|
|
||||||
match: /(?<=\(\)\.badgeContainer,children:).{0,50}?name:(\i\.application)\.name.+?null/,
|
|
||||||
replace: (m, props) => `${m},$self.renderToggleActivityButton(${props})`
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
find: '.displayName="LocalActivityStore"',
|
|
||||||
replacement: {
|
|
||||||
match: /LISTENING.+?\)\);(?<=(\i)\.push.+?)/,
|
|
||||||
replace: (m, activities) => `${m}${activities}=${activities}.filter($self.isActivityNotIgnored);`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
|
|
||||||
async start() {
|
|
||||||
const ignoredActivitiesData = await DataStore.get<string[] | Map<IgnoredActivity["id"], IgnoredActivity>>("IgnoreActivities_ignoredActivities") ?? new Map<IgnoredActivity["id"], IgnoredActivity>();
|
|
||||||
/** Migrate old data */
|
|
||||||
if (Array.isArray(ignoredActivitiesData)) {
|
|
||||||
for (const id of ignoredActivitiesData) {
|
|
||||||
ignoredActivitiesCache.set(id, { id, type: ActivitiesTypes.Game });
|
|
||||||
}
|
|
||||||
|
|
||||||
await saveCacheToDatastore();
|
|
||||||
} else ignoredActivitiesCache = ignoredActivitiesData;
|
|
||||||
|
|
||||||
if (ignoredActivitiesCache.size !== 0) {
|
|
||||||
const gamesSeen: { id?: string; exePath: string; }[] = RunningGameStore.getGamesSeen();
|
|
||||||
|
|
||||||
for (const ignoredActivity of ignoredActivitiesCache.values()) {
|
|
||||||
if (ignoredActivity.type !== ActivitiesTypes.Game) continue;
|
|
||||||
|
|
||||||
if (!gamesSeen.some(game => game.id === ignoredActivity.id || game.exePath === ignoredActivity.id)) {
|
|
||||||
/** Custom added game which no longer exists */
|
|
||||||
ignoredActivitiesCache.delete(ignoredActivity.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await saveCacheToDatastore();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
renderToggleGameActivityButton(props: { id?: string; exePath: string; }) {
|
|
||||||
return (
|
|
||||||
<ErrorBoundary noop>
|
|
||||||
<ToggleActivityComponent activity={{ id: props.id ?? props.exePath, type: ActivitiesTypes.Game }} forceLeftMargin={true} />
|
|
||||||
</ErrorBoundary>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
renderToggleActivityButton(props: { id: string; }) {
|
|
||||||
return (
|
|
||||||
<ErrorBoundary noop>
|
|
||||||
<ToggleActivityComponentWithBackground activity={{ id: props.id, type: ActivitiesTypes.Embedded }} />
|
|
||||||
</ErrorBoundary>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
isActivityNotIgnored(props: { type: number; application_id?: string; name?: string; }) {
|
|
||||||
if (props.type === 0) {
|
|
||||||
if (props.application_id !== undefined) return !ignoredActivitiesCache.has(props.application_id);
|
|
||||||
else {
|
|
||||||
const exePath = RunningGameStore.getRunningGames().find(game => game.name === props.name)?.exePath;
|
|
||||||
if (exePath) return !ignoredActivitiesCache.has(exePath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
172
src/plugins/ignoreActivities/index.tsx
Normal file
172
src/plugins/ignoreActivities/index.tsx
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
/*
|
||||||
|
* Vencord, a Discord client mod
|
||||||
|
* Copyright (c) 2023 Vendicated and contributors
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as DataStore from "@api/DataStore";
|
||||||
|
import { definePluginSettings } from "@api/Settings";
|
||||||
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
|
import { Devs } from "@utils/constants";
|
||||||
|
import { useForceUpdater } from "@utils/react";
|
||||||
|
import definePlugin from "@utils/types";
|
||||||
|
import { findStoreLazy } from "@webpack";
|
||||||
|
import { StatusSettingsStores, Tooltip } from "webpack/common";
|
||||||
|
|
||||||
|
const enum ActivitiesTypes {
|
||||||
|
Game,
|
||||||
|
Embedded
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IgnoredActivity {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
type: ActivitiesTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
const RunningGameStore = findStoreLazy("RunningGameStore");
|
||||||
|
|
||||||
|
function ToggleIcon(activity: IgnoredActivity, tooltipText: string, path: string, fill: string) {
|
||||||
|
const forceUpdate = useForceUpdater();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tooltip text={tooltipText}>
|
||||||
|
{tooltipProps => (
|
||||||
|
<button
|
||||||
|
{...tooltipProps}
|
||||||
|
onClick={e => handleActivityToggle(e, activity, forceUpdate)}
|
||||||
|
style={{ all: "unset", cursor: "pointer", display: "flex", justifyContent: "center", alignItems: "center" }}
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 -960 960 960"
|
||||||
|
>
|
||||||
|
<path fill={fill} d={path} />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const ToggleIconOn = (activity: IgnoredActivity, fill: string) => ToggleIcon(activity, "Disable Activity", "M480-320q75 0 127.5-52.5T660-500q0-75-52.5-127.5T480-680q-75 0-127.5 52.5T300-500q0 75 52.5 127.5T480-320Zm0-72q-45 0-76.5-31.5T372-500q0-45 31.5-76.5T480-608q45 0 76.5 31.5T588-500q0 45-31.5 76.5T480-392Zm0 192q-146 0-266-81.5T40-500q54-137 174-218.5T480-800q146 0 266 81.5T920-500q-54 137-174 218.5T480-200Zm0-300Zm0 220q113 0 207.5-59.5T832-500q-50-101-144.5-160.5T480-720q-113 0-207.5 59.5T128-500q50 101 144.5 160.5T480-280Z", fill);
|
||||||
|
const ToggleIconOff = (activity: IgnoredActivity, fill: string) => ToggleIcon(activity, "Enable Activity", "m644-428-58-58q9-47-27-88t-93-32l-58-58q17-8 34.5-12t37.5-4q75 0 127.5 52.5T660-500q0 20-4 37.5T644-428Zm128 126-58-56q38-29 67.5-63.5T832-500q-50-101-143.5-160.5T480-720q-29 0-57 4t-55 12l-62-62q41-17 84-25.5t90-8.5q151 0 269 83.5T920-500q-23 59-60.5 109.5T772-302Zm20 246L624-222q-35 11-70.5 16.5T480-200q-151 0-269-83.5T40-500q21-53 53-98.5t73-81.5L56-792l56-56 736 736-56 56ZM222-624q-29 26-53 57t-41 67q50 101 143.5 160.5T480-280q20 0 39-2.5t39-5.5l-36-38q-11 3-21 4.5t-21 1.5q-75 0-127.5-52.5T300-500q0-11 1.5-21t4.5-21l-84-82Zm319 93Zm-151 75Z", fill);
|
||||||
|
|
||||||
|
function ToggleActivityComponent(activity: IgnoredActivity, isPlaying = false) {
|
||||||
|
if (getIgnoredActivities().some(act => act.id === activity.id)) return ToggleIconOff(activity, "var(--status-danger)");
|
||||||
|
return ToggleIconOn(activity, isPlaying ? "var(--green-300)" : "var(--primary-400)");
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleActivityToggle(e: React.MouseEvent<HTMLButtonElement, MouseEvent>, activity: IgnoredActivity, forceUpdateButton: () => void) {
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
const ignoredActivityIndex = getIgnoredActivities().findIndex(act => act.id === activity.id);
|
||||||
|
if (ignoredActivityIndex === -1) settings.store.ignoredActivities = getIgnoredActivities().concat(activity);
|
||||||
|
else settings.store.ignoredActivities = getIgnoredActivities().filter((_, index) => index !== ignoredActivityIndex);
|
||||||
|
|
||||||
|
// Trigger activities recalculation
|
||||||
|
StatusSettingsStores.ShowCurrentGame.updateSetting(old => old);
|
||||||
|
forceUpdateButton();
|
||||||
|
}
|
||||||
|
|
||||||
|
const settings = definePluginSettings({}).withPrivateSettings<{
|
||||||
|
ignoredActivities: IgnoredActivity[];
|
||||||
|
}>();
|
||||||
|
|
||||||
|
function getIgnoredActivities() {
|
||||||
|
return settings.store.ignoredActivities ??= [];
|
||||||
|
}
|
||||||
|
|
||||||
|
export default definePlugin({
|
||||||
|
name: "IgnoreActivities",
|
||||||
|
authors: [Devs.Nuckyz],
|
||||||
|
description: "Ignore activities from showing up on your status ONLY. You can configure which ones are ignored from the Registered Games and Activities tabs.",
|
||||||
|
|
||||||
|
settings,
|
||||||
|
|
||||||
|
patches: [
|
||||||
|
{
|
||||||
|
find: '.displayName="LocalActivityStore"',
|
||||||
|
replacement: [
|
||||||
|
{
|
||||||
|
match: /LISTENING.+?}\),(?<=(\i)\.push.+?)/,
|
||||||
|
replace: (m, activities) => `${m}${activities}=${activities}.filter($self.isActivityNotIgnored),`
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
find: ".Messages.SETTINGS_GAMES_TOGGLE_OVERLAY",
|
||||||
|
replacement: {
|
||||||
|
match: /\.Messages\.SETTINGS_GAMES_TOGGLE_OVERLAY.+?}\(\),(?<={overlay:\i,.+?=(\i),.+?)(?=!(\i))/,
|
||||||
|
replace: (m, props, nowPlaying) => `${m}$self.renderToggleGameActivityButton(${props},${nowPlaying}),`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
find: ".activityTitleText,variant",
|
||||||
|
replacement: {
|
||||||
|
match: /(?<=\i\.activityTitleText.+?children:(\i)\.name.*?}\),)/,
|
||||||
|
replace: (_, props) => `$self.renderToggleActivityButton(${props}),`
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
find: ".activityCardDetails,children",
|
||||||
|
replacement: {
|
||||||
|
match: /(?<=\i\.activityCardDetails.+?children:(\i\.application)\.name.*?}\),)/,
|
||||||
|
replace: (_, props) => `$self.renderToggleActivityButton(${props}),`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
async start() {
|
||||||
|
const oldIgnoredActivitiesData = await DataStore.get<Map<IgnoredActivity["id"], IgnoredActivity>>("IgnoreActivities_ignoredActivities");
|
||||||
|
|
||||||
|
if (oldIgnoredActivitiesData != null) {
|
||||||
|
settings.store.ignoredActivities = Array.from(oldIgnoredActivitiesData.values())
|
||||||
|
.map(activity => ({ ...activity, name: "Unknown Name" }));
|
||||||
|
|
||||||
|
DataStore.del("IgnoreActivities_ignoredActivities");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getIgnoredActivities().length !== 0) {
|
||||||
|
const gamesSeen = RunningGameStore.getGamesSeen() as { id?: string; exePath: string; }[];
|
||||||
|
|
||||||
|
for (const [index, ignoredActivity] of getIgnoredActivities().entries()) {
|
||||||
|
if (ignoredActivity.type !== ActivitiesTypes.Game) continue;
|
||||||
|
|
||||||
|
if (!gamesSeen.some(game => game.id === ignoredActivity.id || game.exePath === ignoredActivity.id)) {
|
||||||
|
getIgnoredActivities().splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
isActivityNotIgnored(props: { type: number; application_id?: string; name?: string; }) {
|
||||||
|
if (props.type === 0 || props.type === 3) {
|
||||||
|
if (props.application_id != null) return !getIgnoredActivities().some(activity => activity.id === props.application_id);
|
||||||
|
else {
|
||||||
|
const exePath = RunningGameStore.getRunningGames().find(game => game.name === props.name)?.exePath;
|
||||||
|
if (exePath) return !getIgnoredActivities().some(activity => activity.id === exePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
renderToggleGameActivityButton(props: { id?: string; name: string, exePath: string; }, nowPlaying: boolean) {
|
||||||
|
return (
|
||||||
|
<ErrorBoundary noop>
|
||||||
|
<div style={{ marginLeft: 12, zIndex: 0 }}>
|
||||||
|
{ToggleActivityComponent({ id: props.id ?? props.exePath, name: props.name, type: ActivitiesTypes.Game }, nowPlaying)}
|
||||||
|
</div>
|
||||||
|
</ErrorBoundary>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
renderToggleActivityButton(props: { id: string; name: string; }) {
|
||||||
|
return (
|
||||||
|
<ErrorBoundary noop>
|
||||||
|
{ToggleActivityComponent({ id: props.id, name: props.name, type: ActivitiesTypes.Embedded })}
|
||||||
|
</ErrorBoundary>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
@ -2,5 +2,5 @@
|
|||||||
|
|
||||||
Lets you zoom in to images and gifs. Use scroll wheel to zoom in and shift + scroll wheel to increase lens radius / size
|
Lets you zoom in to images and gifs. Use scroll wheel to zoom in and shift + scroll wheel to increase lens radius / size
|
||||||
|
|
||||||
![Example](https://i.imgur.com/VJdo4aq.png)
|
![the plugin in action](https://github.com/Vendicated/Vencord/assets/45497981/408cd77d-c5f4-40bc-8de2-f977a31b3e5f)
|
||||||
![ContextMenu](https://i.imgur.com/0oaRM2s.png)
|
![the context menu options offered by the plugin](https://github.com/Vendicated/Vencord/assets/45497981/3bede636-f1ce-493f-af46-788b920cb81c)
|
||||||
|
@ -37,13 +37,6 @@ export const settings = definePluginSettings({
|
|||||||
default: true,
|
default: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
preventCarouselFromClosingOnClick: {
|
|
||||||
type: OptionType.BOOLEAN,
|
|
||||||
// Thanks chat gpt
|
|
||||||
description: "Allow the image modal in the image slideshow thing / carousel to remain open when clicking on the image",
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
|
|
||||||
invertScroll: {
|
invertScroll: {
|
||||||
type: OptionType.BOOLEAN,
|
type: OptionType.BOOLEAN,
|
||||||
description: "Invert scroll",
|
description: "Invert scroll",
|
||||||
@ -101,7 +94,7 @@ const imageContextMenuPatch: NavContextMenuPatchCallback = children => () => {
|
|||||||
/>
|
/>
|
||||||
<Menu.MenuCheckboxItem
|
<Menu.MenuCheckboxItem
|
||||||
id="vc-nearest-neighbour"
|
id="vc-nearest-neighbour"
|
||||||
label="Nearset Neighbour"
|
label="Nearest Neighbour"
|
||||||
checked={settings.store.nearestNeighbour}
|
checked={settings.store.nearestNeighbour}
|
||||||
action={() => {
|
action={() => {
|
||||||
settings.store.nearestNeighbour = !settings.store.nearestNeighbour;
|
settings.store.nearestNeighbour = !settings.store.nearestNeighbour;
|
||||||
@ -163,10 +156,14 @@ export default definePlugin({
|
|||||||
|
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
find: '"renderLinkComponent","maxWidth"',
|
find: "Messages.OPEN_IN_BROWSER",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(return\(.{1,100}\(\)\.wrapper.{1,100})(src)/,
|
// there are 2 image thingies. one for carosuel and one for the single image.
|
||||||
replace: `$1id: '${ELEMENT_ID}',$2`
|
// so thats why i added global flag.
|
||||||
|
// also idk if this patch is good, should it be more specific?
|
||||||
|
// https://regex101.com/r/xfvNvV/1
|
||||||
|
match: /return.{1,200}\.wrapper.{1,200}src:\i,/g,
|
||||||
|
replace: `$&id: '${ELEMENT_ID}',`
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -174,29 +171,21 @@ export default definePlugin({
|
|||||||
find: "handleImageLoad=",
|
find: "handleImageLoad=",
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
match: /(render=function\(\){.{1,500}limitResponsiveWidth.{1,600})onMouseEnter:/,
|
match: /showThumbhashPlaceholder:\i,/,
|
||||||
replace: "$1...$self.makeProps(this),onMouseEnter:"
|
replace: "...$self.makeProps(this),$&"
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
match: /componentDidMount=function\(\){/,
|
match: /componentDidMount\(\){/,
|
||||||
replace: "$&$self.renderMagnifier(this);",
|
replace: "$&$self.renderMagnifier(this);",
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
match: /componentWillUnmount=function\(\){/,
|
match: /componentWillUnmount\(\){/,
|
||||||
replace: "$&$self.unMountMagnifier();"
|
replace: "$&$self.unMountMagnifier();"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
|
||||||
find: ".carouselModal,",
|
|
||||||
replacement: {
|
|
||||||
match: /onClick:(\i),/,
|
|
||||||
replace: "onClick:$self.settings.store.preventCarouselFromClosingOnClick ? () => {} : $1,"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
],
|
||||||
|
|
||||||
settings,
|
settings,
|
||||||
|
@ -25,12 +25,6 @@
|
|||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
[class*="modalCarouselWrapper"] {
|
|
||||||
height: fit-content;
|
|
||||||
top: 50%;
|
|
||||||
transform: translateY(-50%);
|
|
||||||
}
|
|
||||||
|
|
||||||
[class|="wrapper"]:has(> #vc-imgzoom-magnify-modal) {
|
[class|="wrapper"]:has(> #vc-imgzoom-magnify-modal) {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
|
@ -131,15 +131,15 @@ export default definePlugin({
|
|||||||
// Indicator
|
// Indicator
|
||||||
find: ".Messages.MESSAGE_EDITED,",
|
find: ".Messages.MESSAGE_EDITED,",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /var .,.,.=(.)\.className,.=.\.message,.=.\.children,.=.\.content,.=.\.onUpdate/gm,
|
match: /let\{className:\i,message:\i[^}]*\}=(\i)/,
|
||||||
replace: "try {$1 && $self.INV_REGEX.test($1.message.content) ? $1.content.push($self.indicator()) : null } catch {};$&"
|
replace: "try {$1 && $self.INV_REGEX.test($1.message.content) ? $1.content.push($self.indicator()) : null } catch {};$&"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
find: ".activeCommandOption",
|
find: "ChannelTextAreaButtons",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(.)\.push.{1,30}disabled:(\i),.{1,20}\},"gift"\)\)/,
|
match: /(\i)\.push.{1,30}disabled:(\i),.{1,20}\},"gift"\)\)/,
|
||||||
replace: "$&;try{$2||$1.push($self.chatBarIcon(arguments[0]))}catch{}",
|
replace: "$&,(()=>{try{$2||$1.push($self.chatBarIcon(arguments[0]))}catch{}})()",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -21,8 +21,8 @@ import { Link } from "@components/Link";
|
|||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import { Logger } from "@utils/Logger";
|
import { Logger } from "@utils/Logger";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { filters, findByPropsLazy, mapMangledModuleLazy } from "@webpack";
|
import { findByPropsLazy } from "@webpack";
|
||||||
import { FluxDispatcher, Forms } from "@webpack/common";
|
import { ApplicationAssetUtils, FluxDispatcher, Forms } from "@webpack/common";
|
||||||
|
|
||||||
interface ActivityAssets {
|
interface ActivityAssets {
|
||||||
large_image?: string;
|
large_image?: string;
|
||||||
@ -86,15 +86,9 @@ const placeholderId = "2a96cbd8b46e442fc41c2b86b821562f";
|
|||||||
const logger = new Logger("LastFMRichPresence");
|
const logger = new Logger("LastFMRichPresence");
|
||||||
|
|
||||||
const presenceStore = findByPropsLazy("getLocalPresence");
|
const presenceStore = findByPropsLazy("getLocalPresence");
|
||||||
const assetManager = mapMangledModuleLazy(
|
|
||||||
"getAssetImage: size must === [number, number] for Twitch",
|
|
||||||
{
|
|
||||||
getAsset: filters.byCode("apply("),
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
async function getApplicationAsset(key: string): Promise<string> {
|
async function getApplicationAsset(key: string): Promise<string> {
|
||||||
return (await assetManager.getAsset(applicationId, [key, undefined]))[0];
|
return (await ApplicationAssetUtils.fetchAssetIds(applicationId, [key]))[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
function setActivity(activity: Activity | null) {
|
function setActivity(activity: Activity | null) {
|
@ -16,8 +16,9 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { definePluginSettings } from "@api/Settings";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import definePlugin from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
|
|
||||||
// These are Xor encrypted to prevent you from spoiling yourself when you read the source code.
|
// These are Xor encrypted to prevent you from spoiling yourself when you read the source code.
|
||||||
// don't worry about it :P
|
// don't worry about it :P
|
||||||
@ -60,17 +61,35 @@ const quotes = [
|
|||||||
"Wdn`khc'|f*eghl{%"
|
"Wdn`khc'|f*eghl{%"
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const settings = definePluginSettings({
|
||||||
|
replaceEvents: {
|
||||||
|
description: "Replace Event Quotes too",
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
default: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "LoadingQuotes",
|
name: "LoadingQuotes",
|
||||||
description: "Replace Discords loading quotes",
|
description: "Replace Discords loading quotes",
|
||||||
authors: [Devs.Ven, Devs.KraXen72],
|
authors: [Devs.Ven, Devs.KraXen72],
|
||||||
|
|
||||||
|
settings,
|
||||||
|
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
find: ".LOADING_DID_YOU_KNOW",
|
find: ".LOADING_DID_YOU_KNOW}",
|
||||||
replacement: {
|
replacement: [
|
||||||
match: /\._loadingText=.+?random\(.+?;/s,
|
{
|
||||||
replace: "._loadingText=$self.quote;",
|
match: /\._loadingText=function\(\)\{/,
|
||||||
|
replace: "$&return $self.quote;",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
match: /\._eventLoadingText=function\(\)\{/,
|
||||||
|
replace: "$&return $self.quote;",
|
||||||
|
predicate: () => settings.store.replaceEvents
|
||||||
|
}
|
||||||
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user