Compare commits
744 commits
main
...
immediate-
Author | SHA1 | Date | |
---|---|---|---|
7fcd9c3f06 | |||
8bfacc139a | |||
a9a8c338a6 | |||
262319a97c | |||
0379beb0ba | |||
13ada86934 | |||
fae5f9bfab | |||
7d4fc5e8e1 | |||
26a71caced | |||
b1ad0a0d58 | |||
4a9f258e34 | |||
301b0e7dfc | |||
2f806dde95 | |||
30ef53403b | |||
ade9a523ac | |||
464dddff13 | |||
e8965866e6 | |||
e3d9b2001f | |||
e4bc7f2add | |||
edc87ac75e | |||
5c7773c7bb | |||
3546632bbf | |||
9a48454a9f | |||
0e2c0b9bfb | |||
84c78a4262 | |||
cfbe4affc0 | |||
e8f017b15e | |||
22a81ab142 | |||
93d450a600 | |||
50bb6d867b | |||
6cc2f53501 | |||
f9776ce1ae | |||
18c5ca216c | |||
3f0d915c75 | |||
a8f77a12df | |||
44785d3498 | |||
473109b1d9 | |||
f398a1c871 | |||
9871e16e28 | |||
b22d4622ed | |||
f05d462a73 | |||
c183018fb1 | |||
f9c11a1149 | |||
45a876a0d5 | |||
4d88a13f5e | |||
eeb4d26812 | |||
2429c283aa | |||
1f1e3e8456 | |||
64e1f294e6 | |||
e801c71fdf | |||
4677ac779a | |||
ed32b7964d | |||
76f8b0e38e | |||
e24bdc2b6f | |||
4bcefb744d | |||
4d03a695aa | |||
ca3768ca5d | |||
6bdd0ba4a9 | |||
633a84581d | |||
4a590d3d0a | |||
1905aa0ae8 | |||
026e716710 | |||
9e6f24cc92 | |||
75a8098590 | |||
1f20074d4e | |||
fb394c6e1a | |||
465dd15e2c | |||
f676613173 | |||
be6209ee44 | |||
15a3694533 | |||
9fd16825e2 | |||
129b770ffa | |||
42353373db | |||
5f7c44f478 | |||
2df13a2e17 | |||
dc116fc3b1 | |||
cd3534f6fc | |||
01bd41413d | |||
654a271eae | |||
efd48e0364 | |||
09447d562e | |||
e57c99399d | |||
a3612dd4ee | |||
e9328cadea | |||
8fa19656ae | |||
d58d8aea71 | |||
5462bb1438 | |||
9dadde6884 | |||
81a1822e70 | |||
974953ba01 | |||
27fd143318 | |||
3bc71c688a | |||
d869b2ddbb | |||
fb3ae98419 | |||
70df451e64 | |||
2fb616ebf1 | |||
bd25e0cd75 | |||
62d4add6c1 | |||
3f5491d379 | |||
e35d0f680f | |||
187ece1905 | |||
2b99f4ece3 | |||
8bf98be472 | |||
0506505449 | |||
3edc36d5e0 | |||
6ffa969955 | |||
6213fca9ed | |||
56477d4e96 | |||
104f193a76 | |||
293b90a6d2 | |||
8159937827 | |||
aa35602827 | |||
42c4845261 | |||
710f627016 | |||
4389b78410 | |||
fde823fabf | |||
cce8f14d72 | |||
c8356570a3 | |||
021baa54d3 | |||
ddd5d9e0c3 | |||
920fb20d8e | |||
438b3e0598 | |||
763b24aa92 | |||
65013f0149 | |||
24795ba9f8 | |||
aa29e479b9 | |||
dd25044afd | |||
8c1979481e | |||
2a1684d902 | |||
b377cad1e6 | |||
4e09e347e2 | |||
ea6fccb54e | |||
c784bc5dbd | |||
edba931afe | |||
208882e8ec | |||
e0cb7242d6 | |||
b3ab32180c | |||
2506ba1b2e | |||
0d0c63f596 | |||
8abb68c553 | |||
f6147c6c13 | |||
b5fdb43cbf | |||
30057985c4 | |||
a95037d80a | |||
99e21ad284 | |||
d90bcdb063 | |||
771d51bcc3 | |||
c7e49672f5 | |||
3331ed2155 | |||
bc34d72b57 | |||
b333deb731 | |||
614cbc1146 | |||
a985286c88 | |||
1297408171 | |||
cc536bdbf4 | |||
ea859f59b0 | |||
a04ccd0d11 | |||
c1b4eb5b04 | |||
3f059e17be | |||
48442aee1d | |||
9ca13d1250 | |||
2c3be4533e | |||
7bf2152353 | |||
a6f4622837 | |||
2f55c7b126 | |||
14d1a2d1ea | |||
332f3e532b | |||
28a7fbb309 | |||
4caf86b60e | |||
5a85d6e78e | |||
20cfa932a4 | |||
9e998db456 | |||
3d4a7638a4 | |||
ab8da19e23 | |||
b8335108e4 | |||
403276d307 | |||
748a456cfb | |||
0cd0c813f2 | |||
f647ff0ee9 | |||
c2d708a621 | |||
35343bd790 | |||
cc5d4a7b3a | |||
ae655111de | |||
b768c61ef8 | |||
d95367b31d | |||
1e4ad4db4c | |||
d8ba959d1e | |||
0742cecd41 | |||
ed4a797ad3 | |||
505246954b | |||
eecc21db91 | |||
962eaa9df7 | |||
0190ada1a1 | |||
b5a1b3a124 | |||
1f3b8cdfd8 | |||
1f2c26cdba | |||
3b6bef1cda | |||
858b0e3dd7 | |||
718ebd56ac | |||
1511260666 | |||
4ff7c22505 | |||
057ccfde11 | |||
edf4e75ae2 | |||
62d9d1310d | |||
e592a50d7c | |||
c785053b86 | |||
e9081d2a52 | |||
98c67e1165 | |||
6c7d4259e1 | |||
d8e3381fb3 | |||
bdb9c75c74 | |||
a67b7847b1 | |||
bab081d4a9 | |||
0e5099416c | |||
20efa661b0 | |||
94ba593be9 | |||
c9295d3a85 | |||
d55770797a | |||
b4b67a98da | |||
453c0b62f4 | |||
a3fa74735f | |||
a3cab94a68 | |||
c4e7233604 | |||
889eb311f9 | |||
03120d5a98 | |||
9446efadd5 | |||
9c21c23364 | |||
ba6d3c7e60 | |||
cb1fff8d86 | |||
c400db41c3 | |||
de4743828a | |||
700a442ee4 | |||
dd3904badd | |||
0d360fb7e7 | |||
2429231965 | |||
570137040c | |||
ad3bb91bc1 | |||
161be30b2b | |||
0a4bcd5ccd | |||
3a5859b815 | |||
1ea5f56549 | |||
a7b02f2f1f | |||
32306cc6c3 | |||
8cfb289dc7 | |||
220062acdb | |||
e5d5bc2d34 | |||
17cc81f15d | |||
eba42489e9 | |||
51dee70ced | |||
11561e50f9 | |||
2095cc8d03 | |||
6e61ef297c | |||
58e810dfbe | |||
b2497373f0 | |||
18841f4551 | |||
da6ceb8479 | |||
6b811dce55 | |||
8982a53b47 | |||
3cf702a2ba | |||
cd79150a84 | |||
343e2802c3 | |||
dc8b579488 | |||
066001c57a | |||
24a0f62801 | |||
ef512e6ea1 | |||
c6b2eeb86d | |||
e457e58b49 | |||
cac0398276 | |||
e93562b95b | |||
f6dbcb7709 | |||
a82a0636cf | |||
2fc814124f | |||
a43d5d595a | |||
ceaaf9ab8a | |||
a01ee40591 | |||
26c2674652 | |||
9595f75a9a | |||
2189ce407b | |||
7b4f97fa08 | |||
f208472841 | |||
4e4a9f806f | |||
afe6d9f7b3 | |||
2dac66d7c6 | |||
83b09a8073 | |||
13d47d91bf | |||
004f764c2b | |||
a815122e3c | |||
b88e7faa72 | |||
bb22355a57 | |||
591f433135 | |||
f726376ab8 | |||
74bd89b721 | |||
aa4c6e5496 | |||
e8d696c18c | |||
d473d52047 | |||
36c8058920 | |||
8d31f3cf62 | |||
5021d2e344 | |||
92653eaf96 | |||
2f6e39c0bd | |||
5e52ecf42e | |||
b2ae33344b | |||
b24e8cb9e6 | |||
73b1c7b811 | |||
210fada19a | |||
002a1cfc16 | |||
e9e25306eb | |||
5931a72de2 | |||
3d0ce5449d | |||
31c39bc761 | |||
1d2b8a43c0 | |||
6d587e6530 | |||
853585c0bc | |||
6826fe003c | |||
c3bbc92fa2 | |||
ed35c496dc | |||
dc8ac98a6e | |||
337850c33a | |||
08f1b6a3c9 | |||
c8ad68c80b | |||
7b056364da | |||
7f3cad2528 | |||
5d290ecd35 | |||
b702b31744 | |||
3dbe621059 | |||
55195676b8 | |||
76cb49c381 | |||
95c4cc09af | |||
a5b30e762a | |||
5bbf467be0 | |||
46c771f351 | |||
94ea4f8e5c | |||
fc8a0e4904 | |||
9eb77cb7fd | |||
e1bbd67a9a | |||
625b248a95 | |||
94c365b1c3 | |||
f70e9290ce | |||
57b011aea7 | |||
c8097f0f1a | |||
5999af76c6 | |||
05717d6bc3 | |||
53dd86fa6e | |||
363f2e84fb | |||
066ce07512 | |||
f5b1b65db8 | |||
6e6ee4db68 | |||
ac1c417e1c | |||
4e14232b5a | |||
eddfca06ab | |||
61e1eada01 | |||
85322f9e90 | |||
d329d74fec | |||
ac6a21374f | |||
b385be51d6 | |||
d8524b087c | |||
c491122937 | |||
13337950dc | |||
281ab9194b | |||
a1881bff3d | |||
c50208b019 | |||
a8974f01eb | |||
b79a0dadcb | |||
c4c92ed366 | |||
78fd37a4c6 | |||
9de6e67cf1 | |||
33d5753ca7 | |||
8d3feef9a1 | |||
8580332c82 | |||
4d8c56689c | |||
5cbb816c1c | |||
b6594051d9 | |||
50415a949d | |||
4b03454bce | |||
f1b649e996 | |||
db5397f41a | |||
c34f40e30d | |||
dd4d80872e | |||
eb381767cd | |||
0aff3c29bc | |||
e7fdb3881a | |||
5977ff8f6b | |||
dd13b96728 | |||
01fdeb7292 | |||
6a3831c437 | |||
c485864ee8 | |||
2ab7d3209e | |||
654b2e8398 | |||
3f573c9af3 | |||
cbd9a9312c | |||
2656b60347 | |||
fbb7ee50dd | |||
4187932aa7 | |||
a19fb71c70 | |||
e21a6be4b6 | |||
b53c78c2ba | |||
db63fffe18 | |||
41ab8da5ad | |||
f68effc7e2 | |||
f21bfb9d78 | |||
f4cc95da5a | |||
3a32490937 | |||
545d69aa6d | |||
aa5ca42676 | |||
5197170b3d | |||
47eec049fc | |||
022a0497c3 | |||
e2f936538e | |||
b323fbb486 | |||
0c4e265530 | |||
111a4f2aa8 | |||
46710bf8cb | |||
1f938211a8 | |||
8d362fc394 | |||
79849fd37d | |||
a838d587ae | |||
3a25da5f14 | |||
a1e72e78d7 | |||
48fb6ecd1a | |||
3c62e393a3 | |||
034ef554fb | |||
2da9bf8f0e | |||
eb5fb0df29 | |||
479a4069b2 | |||
e22575805b | |||
c0984db612 | |||
9a9c1b0487 | |||
072619e539 | |||
9084459933 | |||
36f9eda3a8 | |||
99160638df | |||
2574d5311d | |||
7816727e26 | |||
892de53603 | |||
8de1ca86a9 | |||
01ba87cd76 | |||
83222631ab | |||
b74581908e | |||
896b8970b6 | |||
c8602ef52b | |||
ac1423f90c | |||
d0e4ec0ad5 | |||
7509f203cc | |||
17b575070f | |||
4a872236e1 | |||
d5440349c9 | |||
0f9b5f2d09 | |||
4db6c4b88a | |||
6e1094a5d1 | |||
15df3485d1 | |||
ea86df7174 | |||
66c466c9f0 | |||
96d5e1b89b | |||
4b4caa798a | |||
73503945fb | |||
43b679d676 | |||
011b375d02 | |||
9f1f2b15a4 | |||
a1309ea897 | |||
a4073703fd | |||
84b9e3fec1 | |||
6bbd91fafc | |||
d78f1c8000 | |||
64262001e8 | |||
6b648f3d38 | |||
1eeadbcd97 | |||
f016d277b7 | |||
9c2545ab16 | |||
b36d3b4385 | |||
acbc932542 | |||
3590d5abff | |||
c9c09b95a3 | |||
9128eb5760 | |||
513d9f4209 | |||
06393211b1 | |||
ac3130d37e | |||
3e2952890f | |||
a0751a0f84 | |||
fa45beb8ca | |||
539b70cd52 | |||
e96458fafa | |||
a1542bcec5 | |||
e00da485e8 | |||
513eea8d14 | |||
490e9131b5 | |||
9ceb0ae7cb | |||
6d59a943da | |||
00d71fbeab | |||
4459d11a97 | |||
1eab0146dd | |||
f345484e5b | |||
65adaf335c | |||
437e4eae44 | |||
f7d12d209c | |||
8a5678bec5 | |||
1f99162b5a | |||
8c5f2c897e | |||
9ada9bc1a9 | |||
8fd22f6deb | |||
c1e78b4397 | |||
32a2c90761 | |||
dae3841f10 | |||
4d27643d39 | |||
c2047e5f3b | |||
18142ecccb | |||
440cb1f29c | |||
64b1ab1112 | |||
3da88100df | |||
3e3201ad0d | |||
a2bda05211 | |||
1ed956114f | |||
2e27a513ac | |||
a8fa685cfa | |||
b2036762b5 | |||
067a16c9dc | |||
265cf12d39 | |||
b580d6dcfb | |||
affd527bc1 | |||
7d928e53aa | |||
b10afebc98 | |||
1a8a901e0a | |||
d936604f35 | |||
4dcc265be0 | |||
3e6e399544 | |||
9af63b362d | |||
090a3bc0a8 | |||
3331723496 | |||
999d6c812f | |||
c9c1e0c6c5 | |||
1e55ae5327 | |||
5b96881177 | |||
daa1a35fc3 | |||
ef895e787a | |||
2f16cdf77b | |||
e990795783 | |||
8dde496757 | |||
35f950c6e3 | |||
5fbabb0b70 | |||
ff9cc2e9e5 | |||
3793acd9bb | |||
ebae259f5d | |||
3296ee1c4b | |||
2f87363b89 | |||
bacf021a28 | |||
1138e22f58 | |||
d6b5bc58ce | |||
bd2264c125 | |||
86f69e84c1 | |||
488f133a90 | |||
77d8e70059 | |||
f64b228cb9 | |||
90f37471e5 | |||
b192c89146 | |||
ae10d3667c | |||
f727d101fc | |||
9bbec66ea5 | |||
0146789514 | |||
b38ab066d9 | |||
37514e0720 | |||
aa9be97f83 | |||
d8c6559967 | |||
72b014fe67 | |||
8eb4eab2b0 | |||
07361ff6fe | |||
64212512ca | |||
5e762ddd04 | |||
74f36a093b | |||
c01a8c2f78 | |||
90c26432f5 | |||
6f25005057 | |||
23c8818ed2 | |||
16d05c8935 | |||
c5541d297d | |||
a120b35f4f | |||
9740d28530 | |||
8e9434cdd5 | |||
f5be78d101 | |||
bd95cc449f | |||
bdd4b0f143 | |||
a60c8374ed | |||
1013c34840 | |||
7b7b87316d | |||
6567ff7435 | |||
22a75c4589 | |||
67b5ecca51 | |||
cdbd9ad101 | |||
1e96638219 | |||
7371abbaec | |||
cfb493c593 | |||
ac61a0377f | |||
bb3f629672 | |||
3ab68f9296 | |||
01a4ac9c13 | |||
66e1db1fdb | |||
688449ed62 | |||
5ca4e58fad | |||
deb0f7b9c6 | |||
40a1f48267 | |||
bc367b1d2a | |||
f6a7cdc430 | |||
d5bcbf54f9 | |||
4b2734610f | |||
c4645f79c6 | |||
cf9bbfc78f | |||
6cc40eb095 | |||
95acb147d1 | |||
21fea87551 | |||
2a2124c5a0 | |||
1a1e5c2e1d | |||
c9d9d53fb1 | |||
dc81d1c14c | |||
b938bdf138 | |||
e74aabb988 | |||
ab24ce08e3 | |||
c2435bb9dd | |||
d678e68899 | |||
dbb6f4165b | |||
9a1a7b374f | |||
ec373069b3 | |||
329bc68bf5 | |||
2a77941dff | |||
d36c361b47 | |||
b17963169f | |||
118361723c | |||
97e2e2c1c8 | |||
dbee2d9ecc | |||
1283494b46 | |||
e72c732d58 | |||
d40dfc345f | |||
70f60193b0 | |||
9e60d4979d | |||
3562c8ae11 | |||
093de43d54 | |||
86ad3bd4fc | |||
8045d65e99 | |||
63a1fe2c70 | |||
55760bc42b | |||
983013446d | |||
569dace244 | |||
8e5a2f6e59 | |||
41a1732df2 | |||
487af1c789 | |||
5168f326a3 | |||
4feaa40de7 | |||
7f5aaada88 | |||
725103a77c | |||
a878ea0b39 | |||
0cc89996c6 | |||
c72099fc39 | |||
84c223d471 | |||
82f9e36e5a | |||
7c8c2ff5b8 | |||
dc73b89b29 | |||
2596bf0990 | |||
7169dfe61a | |||
5ba5eca1a1 | |||
4209bfd3aa | |||
c007fc7478 | |||
903fa2cf11 | |||
7d91dea822 | |||
4fc4f1cb75 | |||
45d2cf49e0 | |||
b798f31669 | |||
7e1524362c | |||
e04dd85957 | |||
258e1efdce | |||
f6e484034c | |||
5676f45781 | |||
e11d651e33 | |||
09a6a9bf85 | |||
1b64419ef1 | |||
5edff4a3c5 | |||
b043d8abb7 | |||
e15868d499 | |||
a892a507be | |||
33a5f30ae4 | |||
38b6c7fa6c | |||
bd88625d25 | |||
5fca61f581 | |||
cc6cef02c1 | |||
546c5105b2 | |||
f65749e2d4 | |||
fb8f00137c | |||
20b071aa2d | |||
b7c1b495af | |||
4d80c53dd0 | |||
035e2add0b | |||
f70cc5205c | |||
fa948e8639 | |||
4cc6789b5b | |||
4753efa33c | |||
283a4b23ba | |||
3b7f1be9a2 | |||
c2e12e2827 | |||
8b6620ca2f | |||
f1e2d5572a | |||
49fcfd74df | |||
511ec1b129 | |||
94843a77ac | |||
8873391496 | |||
b77b654df1 | |||
c65e1f88d2 | |||
9cd20e3cea | |||
f04bd8a7ad | |||
fa82939e62 | |||
a1fefcf27a | |||
65b6440024 | |||
6e0064c5fc | |||
b047f9ce28 | |||
d97596a16a | |||
5b6d0f42fd | |||
4a89fadedd | |||
886071af4d | |||
12c9c324a1 | |||
e311bd9f76 | |||
707c23b188 | |||
49e78793c5 | |||
8d31330d3e | |||
17397dbe08 | |||
b98544ed1f | |||
043675f3f8 | |||
3b1cb02663 | |||
bc68bc0312 | |||
b3819228ed | |||
c23c65733b | |||
cfefdbc7a7 | |||
70103e8111 | |||
240705652c | |||
d42c9547ab | |||
ac85b3508b | |||
9ece4dddd3 | |||
36f5d9acc6 | |||
d4956f3ec7 | |||
8279c8d0ba | |||
74e3ba42e0 | |||
784ae0a02d | |||
901d803ba9 | |||
bdb693302a | |||
f8a7d1d634 | |||
770078f236 | |||
f1dd39885b | |||
a29115ba78 | |||
cd5e0aeec1 | |||
75fa0ff708 |
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -18,5 +18,7 @@ lerna-debug.log*
|
|||
.pnpm-debug.log*
|
||||
*.tsbuildinfo
|
||||
|
||||
src/userplugins
|
||||
|
||||
ExtensionCache/
|
||||
settings/
|
||||
|
|
3
.gitmodules
vendored
3
.gitmodules
vendored
|
@ -1,3 +0,0 @@
|
|||
[submodule "src/userplugins/vc-message-logger-enhanced"]
|
||||
path = src/userplugins/vc-message-logger-enhanced
|
||||
url = https://github.com/Syncxv/vc-message-logger-enhanced.git
|
|
@ -90,7 +90,13 @@ export default tseslint.config(
|
|||
"no-invalid-regexp": "error",
|
||||
"no-constant-condition": ["error", { "checkLoops": false }],
|
||||
"no-duplicate-imports": "error",
|
||||
"dot-notation": "error",
|
||||
"@typescript-eslint/dot-notation": [
|
||||
"error",
|
||||
{
|
||||
"allowPrivateClassPropertyAccess": true,
|
||||
"allowProtectedClassPropertyAccess": true
|
||||
}
|
||||
],
|
||||
"no-useless-escape": [
|
||||
"error",
|
||||
{
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "vencord",
|
||||
"private": "true",
|
||||
"version": "1.10.1",
|
||||
"version": "1.9.8",
|
||||
"description": "The cutest Discord client mod",
|
||||
"homepage": "https://github.com/Vendicated/Vencord#readme",
|
||||
"bugs": {
|
||||
|
@ -70,7 +70,6 @@
|
|||
"stylelint": "^16.8.1",
|
||||
"stylelint-config-standard": "^36.0.1",
|
||||
"ts-patch": "^3.2.1",
|
||||
"ts-pattern": "^5.3.1",
|
||||
"tsx": "^4.16.5",
|
||||
"type-fest": "^4.23.0",
|
||||
"typescript": "^5.5.4",
|
||||
|
|
|
@ -116,9 +116,6 @@ importers:
|
|||
ts-patch:
|
||||
specifier: ^3.2.1
|
||||
version: 3.2.1
|
||||
ts-pattern:
|
||||
specifier: ^5.3.1
|
||||
version: 5.3.1
|
||||
tsx:
|
||||
specifier: ^4.16.5
|
||||
version: 4.16.5
|
||||
|
@ -2527,9 +2524,6 @@ packages:
|
|||
resolution: {integrity: sha512-hlR43v+GUIUy8/ZGFP1DquEqPh7PFKQdDMTAmYt671kCCA6AkDQMoeFaFmZ7ObPLYOmpMgyKUqL1C+coFMf30w==}
|
||||
hasBin: true
|
||||
|
||||
ts-pattern@5.3.1:
|
||||
resolution: {integrity: sha512-1RUMKa8jYQdNfmnK4jyzBK3/PS/tnjcZ1CW0v1vWDeYe5RBklc/nquw03MEoB66hVBm4BnlCfmOqDVxHyT1DpA==}
|
||||
|
||||
tsconfig-paths@3.15.0:
|
||||
resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==}
|
||||
|
||||
|
@ -5164,8 +5158,6 @@ snapshots:
|
|||
semver: 7.6.3
|
||||
strip-ansi: 6.0.1
|
||||
|
||||
ts-pattern@5.3.1: {}
|
||||
|
||||
tsconfig-paths@3.15.0:
|
||||
dependencies:
|
||||
'@types/json5': 0.0.29
|
||||
|
|
|
@ -312,7 +312,7 @@ export const commonOpts = {
|
|||
logLevel: "info",
|
||||
bundle: true,
|
||||
watch,
|
||||
minify: !watch,
|
||||
minify: !watch && !IS_REPORTER,
|
||||
sourcemap: watch ? "inline" : "",
|
||||
legalComments: "linked",
|
||||
banner,
|
||||
|
@ -334,6 +334,5 @@ export const commonRendererPlugins = [
|
|||
banImportPlugin(builtinModuleRegex, "Cannot import node inbuilt modules in browser code. You need to use a native.ts file"),
|
||||
banImportPlugin(/^react$/, "Cannot import from react. React and hooks should be imported from @webpack/common"),
|
||||
banImportPlugin(/^electron(\/.*)?$/, "Cannot import electron in browser code. You need to use a native.ts file"),
|
||||
banImportPlugin(/^ts-pattern$/, "Cannot import from ts-pattern. match and P should be imported from @webpack/common"),
|
||||
...commonOpts.plugins
|
||||
];
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
import { readFileSync } from "fs";
|
||||
import pup, { JSHandle } from "puppeteer-core";
|
||||
|
||||
for (const variable of ["DISCORD_TOKEN", "CHROMIUM_BIN"]) {
|
||||
for (const variable of ["CHROMIUM_BIN"]) {
|
||||
if (!process.env[variable]) {
|
||||
console.error(`Missing environment variable ${variable}`);
|
||||
process.exit(1);
|
||||
|
@ -214,7 +214,7 @@ page.on("console", async e => {
|
|||
|
||||
switch (tag) {
|
||||
case "WebpackInterceptor:":
|
||||
const patchFailMatch = message.match(/Patch by (.+?) (had no effect|errored|found no module) \(Module id is (.+?)\): (.+)/)!;
|
||||
const patchFailMatch = message.match(/Patch by (.+?) (had no effect|errored|found no module|took [\d.]+?ms) \(Module id is (.+?)\): (.+)/)!;
|
||||
if (!patchFailMatch) break;
|
||||
|
||||
console.error(await getText());
|
||||
|
@ -291,7 +291,7 @@ page.on("error", e => console.error("[Error]", e.message));
|
|||
page.on("pageerror", e => {
|
||||
if (e.message.includes("Sentry successfully disabled")) return;
|
||||
|
||||
if (!e.message.startsWith("Object") && !e.message.includes("Cannot find module")) {
|
||||
if (!e.message.startsWith("Object") && !e.message.includes("Cannot find module") && !/^.{1,2}$/.test(e.message)) {
|
||||
console.error("[Page Error]", e.message);
|
||||
report.otherErrors.push(e.message);
|
||||
} else {
|
||||
|
@ -299,20 +299,9 @@ page.on("pageerror", e => {
|
|||
}
|
||||
});
|
||||
|
||||
async function reporterRuntime(token: string) {
|
||||
Vencord.Webpack.waitFor(
|
||||
"loginToken",
|
||||
m => {
|
||||
console.log("[PUP_DEBUG]", "Logging in with token...");
|
||||
m.loginToken(token);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
await page.evaluateOnNewDocument(`
|
||||
if (location.host.endsWith("discord.com")) {
|
||||
${readFileSync("./dist/browser.js", "utf-8")};
|
||||
(${reporterRuntime.toString()})(${JSON.stringify(process.env.DISCORD_TOKEN)});
|
||||
}
|
||||
`);
|
||||
|
||||
|
|
|
@ -23,10 +23,10 @@ export * as Util from "./utils";
|
|||
export * as QuickCss from "./utils/quickCss";
|
||||
export * as Updater from "./utils/updater";
|
||||
export * as Webpack from "./webpack";
|
||||
export * as WebpackPatcher from "./webpack/patchWebpack";
|
||||
export { PlainSettings, Settings };
|
||||
|
||||
import "./utils/quickCss";
|
||||
import "./webpack/patchWebpack";
|
||||
|
||||
import { openUpdaterModal } from "@components/VencordSettings/UpdaterTab";
|
||||
import { StartAt } from "@utils/types";
|
||||
|
@ -39,7 +39,7 @@ import { localStorage } from "./utils/localStorage";
|
|||
import { relaunch } from "./utils/native";
|
||||
import { getCloudSettings, putCloudSettings } from "./utils/settingsSync";
|
||||
import { checkForUpdates, update, UpdateLogger } from "./utils/updater";
|
||||
import { onceReady } from "./webpack";
|
||||
import { onceDiscordLoaded } from "./webpack";
|
||||
import { SettingsRouter } from "./webpack/common";
|
||||
|
||||
if (IS_REPORTER) {
|
||||
|
@ -86,7 +86,7 @@ async function syncSettings() {
|
|||
}
|
||||
|
||||
async function init() {
|
||||
await onceReady;
|
||||
await onceDiscordLoaded;
|
||||
startAllPlugins(StartAt.WebpackReady);
|
||||
|
||||
syncSettings();
|
||||
|
@ -125,7 +125,7 @@ async function init() {
|
|||
const pendingPatches = patches.filter(p => !p.all && p.predicate?.() !== false);
|
||||
if (pendingPatches.length)
|
||||
PMLogger.warn(
|
||||
"Webpack has finished initialising, but some patches haven't been applied yet.",
|
||||
"Webpack has finished initializing, but some patches haven't been applied yet.",
|
||||
"This might be expected since some Modules are lazy loaded, but please verify",
|
||||
"that all plugins are working as intended.",
|
||||
"You are seeing this warning because this is a Development build of Vencord.",
|
||||
|
|
|
@ -8,13 +8,12 @@ import "./ChatButton.css";
|
|||
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Logger } from "@utils/Logger";
|
||||
import { waitFor } from "@webpack";
|
||||
import { findByProps } from "@webpack";
|
||||
import { Button, ButtonLooks, ButtonWrapperClasses, Tooltip } from "@webpack/common";
|
||||
import { Channel } from "discord-types/general";
|
||||
import { HTMLProps, MouseEventHandler, ReactNode } from "react";
|
||||
|
||||
let ChannelTextAreaClasses: Record<"button" | "buttonContainer", string>;
|
||||
waitFor(["buttonContainer", "channelTextArea"], m => ChannelTextAreaClasses = m);
|
||||
const ChannelTextAreaClasses = findByProps<Record<"button" | "buttonContainer", string>>("buttonContainer", "channelTextArea");
|
||||
|
||||
export interface ChatBarProps {
|
||||
channel: Channel;
|
||||
|
|
|
@ -17,14 +17,14 @@
|
|||
*/
|
||||
|
||||
import { mergeDefaults } from "@utils/mergeDefaults";
|
||||
import { findByCodeLazy } from "@webpack";
|
||||
import { findByCode } from "@webpack";
|
||||
import { MessageActions, SnowflakeUtils } from "@webpack/common";
|
||||
import { Message } from "discord-types/general";
|
||||
import type { PartialDeep } from "type-fest";
|
||||
|
||||
import { Argument } from "./types";
|
||||
|
||||
const createBotMessage = findByCodeLazy('username:"Clyde"');
|
||||
const createBotMessage = findByCode('username:"Clyde"');
|
||||
|
||||
export function generateId() {
|
||||
return `-${SnowflakeUtils.fromTimestamp(Date.now())}`;
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Logger } from "@utils/Logger";
|
||||
import { makeCodeblock } from "@utils/text";
|
||||
|
||||
import { sendBotMessage } from "./commandHelpers";
|
||||
|
@ -47,10 +46,10 @@ export let RequiredMessageOption: Option = ReqPlaceholder;
|
|||
export const _init = function (cmds: Command[]) {
|
||||
try {
|
||||
BUILT_IN = cmds;
|
||||
OptionalMessageOption = cmds.find(c => (c.untranslatedName || c.displayName) === "shrug")!.options![0];
|
||||
RequiredMessageOption = cmds.find(c => (c.untranslatedName || c.displayName) === "me")!.options![0];
|
||||
OptionalMessageOption = cmds.find(c => c.name === "shrug")!.options![0];
|
||||
RequiredMessageOption = cmds.find(c => c.name === "me")!.options![0];
|
||||
} catch (e) {
|
||||
new Logger("CommandsAPI").error("Failed to load CommandsApi", e, " - cmds is", cmds);
|
||||
console.error("Failed to load CommandsApi");
|
||||
}
|
||||
return cmds;
|
||||
} as never;
|
||||
|
@ -139,8 +138,6 @@ export function registerCommand<C extends Command>(command: C, plugin: string) {
|
|||
throw new Error(`Command '${command.name}' already exists.`);
|
||||
|
||||
command.isVencordCommand = true;
|
||||
command.untranslatedName ??= command.name;
|
||||
command.untranslatedDescription ??= command.description;
|
||||
command.id ??= `-${BUILT_IN.length + 1}`;
|
||||
command.applicationId ??= "-1"; // BUILT_IN;
|
||||
command.type ??= ApplicationCommandType.CHAT_INPUT;
|
||||
|
|
|
@ -93,10 +93,8 @@ export interface Command {
|
|||
isVencordCommand?: boolean;
|
||||
|
||||
name: string;
|
||||
untranslatedName?: string;
|
||||
displayName?: string;
|
||||
description: string;
|
||||
untranslatedDescription?: string;
|
||||
displayDescription?: string;
|
||||
|
||||
options?: Option[];
|
||||
|
|
|
@ -16,23 +16,22 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { waitFor } from "@webpack";
|
||||
import { find } from "@webpack";
|
||||
|
||||
let NoticesModule: any;
|
||||
waitFor(m => m.show && m.dismiss && !m.suppressAll, m => NoticesModule = m);
|
||||
const Notices = find(m => m.show && m.dismiss && !m.suppressAll);
|
||||
|
||||
export const noticesQueue = [] as any[];
|
||||
export let currentNotice: any = null;
|
||||
|
||||
export function popNotice() {
|
||||
NoticesModule.dismiss();
|
||||
Notices.dismiss();
|
||||
}
|
||||
|
||||
export function nextNotice() {
|
||||
currentNotice = noticesQueue.shift();
|
||||
|
||||
if (currentNotice) {
|
||||
NoticesModule.show(...currentNotice, "VencordNotice");
|
||||
Notices.show(...currentNotice, "VencordNotice");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -32,9 +32,10 @@ export interface Settings {
|
|||
autoUpdate: boolean;
|
||||
autoUpdateNotification: boolean,
|
||||
useQuickCss: boolean;
|
||||
enableReactDevtools: boolean;
|
||||
themeLinks: string[];
|
||||
eagerPatches: boolean;
|
||||
enabledThemes: string[];
|
||||
enableReactDevtools: boolean;
|
||||
frameless: boolean;
|
||||
transparent: boolean;
|
||||
winCtrlQ: boolean;
|
||||
|
@ -81,6 +82,7 @@ const DefaultSettings: Settings = {
|
|||
autoUpdateNotification: true,
|
||||
useQuickCss: true,
|
||||
themeLinks: [],
|
||||
eagerPatches: IS_REPORTER,
|
||||
enabledThemes: [],
|
||||
enableReactDevtools: false,
|
||||
frameless: false,
|
||||
|
@ -116,7 +118,6 @@ const saveSettingsOnFrequentAction = debounce(async () => {
|
|||
}
|
||||
}, 60_000);
|
||||
|
||||
|
||||
export const SettingsStore = new SettingsStoreClass(settings, {
|
||||
readOnly: true,
|
||||
getDefaultValue({
|
||||
|
|
|
@ -89,8 +89,8 @@ export const isStyleEnabled = (name: string) => requireStyle(name).dom?.isConnec
|
|||
* // -- plugin.ts --
|
||||
* import pluginStyle from "./plugin.css?managed";
|
||||
* import { setStyleVars } from "@api/Styles";
|
||||
* import { findByPropsLazy } from "@webpack";
|
||||
* const classNames = findByPropsLazy("thin", "scrollerBase"); // { thin: "thin-31rlnD scrollerBase-_bVAAt", ... }
|
||||
* import { findByProps } from "@webpack";
|
||||
* const classNames = findByProps("thin", "scrollerBase"); // { thin: "thin-31rlnD scrollerBase-_bVAAt", ... }
|
||||
*
|
||||
* // Inside some plugin method like "start()"
|
||||
* setStyleClassNames(pluginStyle, classNames);
|
||||
|
|
|
@ -17,8 +17,7 @@
|
|||
*/
|
||||
|
||||
import { proxyLazy } from "@utils/lazy";
|
||||
import { Logger } from "@utils/Logger";
|
||||
import { findModuleId, proxyLazyWebpack, wreq } from "@webpack";
|
||||
import { findByFactoryCode } from "@webpack";
|
||||
|
||||
interface UserSettingDefinition<T> {
|
||||
/**
|
||||
|
@ -43,12 +42,7 @@ interface UserSettingDefinition<T> {
|
|||
userSettingsAPIName: string;
|
||||
}
|
||||
|
||||
export const UserSettings: Record<PropertyKey, UserSettingDefinition<any>> | undefined = proxyLazyWebpack(() => {
|
||||
const modId = findModuleId('"textAndImages","renderSpoilers"');
|
||||
if (modId == null) return new Logger("UserSettingsAPI ").error("Didn't find settings module.");
|
||||
|
||||
return wreq(modId as any);
|
||||
});
|
||||
export const UserSettings = findByFactoryCode<Record<PropertyKey, UserSettingDefinition<any>>>('"textAndImages","renderSpoilers"');
|
||||
|
||||
/**
|
||||
* Get the setting with the given setting group and name.
|
||||
|
@ -56,7 +50,7 @@ export const UserSettings: Record<PropertyKey, UserSettingDefinition<any>> | und
|
|||
* @param group The setting group
|
||||
* @param name The name of the setting
|
||||
*/
|
||||
export function getUserSetting<T = any>(group: string, name: string): UserSettingDefinition<T> | undefined {
|
||||
export function getUserSetting<T = any>(group: string, name: string): UserSettingDefinition<T> {
|
||||
if (!Vencord.Plugins.isPluginEnabled("UserSettingsAPI")) throw new Error("Cannot use UserSettingsAPI without setting as dependency.");
|
||||
|
||||
for (const key in UserSettings) {
|
||||
|
@ -66,10 +60,12 @@ export function getUserSetting<T = any>(group: string, name: string): UserSettin
|
|||
return userSetting;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error(`UserSettingsAPI: Setting ${group}.${name} not found.`);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link getUserSettingDefinition}, lazy.
|
||||
* Lazy version of {@link getUserSetting}
|
||||
*
|
||||
* Get the setting with the given setting group and name.
|
||||
*
|
||||
|
|
|
@ -4,10 +4,10 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
import { findByProps } from "@webpack";
|
||||
import { Parser } from "@webpack/common";
|
||||
|
||||
const CodeContainerClasses = findByPropsLazy("markup", "codeContainer");
|
||||
const CodeContainerClasses = findByProps("markup", "codeContainer");
|
||||
|
||||
/**
|
||||
* Renders code in a Discord codeblock
|
||||
|
|
|
@ -65,7 +65,8 @@ export function LinkIcon({ height = 24, width = 24, className }: IconProps) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Discord's copy icon, as seen in the user panel popout on the right of the username and in large code blocks
|
||||
* Discord's copy icon, as seen in the user popout right of the username when clicking
|
||||
* your own username in the bottom left user panel
|
||||
*/
|
||||
export function CopyIcon(props: IconProps) {
|
||||
return (
|
||||
|
@ -75,9 +76,8 @@ export function CopyIcon(props: IconProps) {
|
|||
viewBox="0 0 24 24"
|
||||
>
|
||||
<g fill="currentColor">
|
||||
<path d="M3 16a1 1 0 0 1-1-1v-5a8 8 0 0 1 8-8h5a1 1 0 0 1 1 1v.5a.5.5 0 0 1-.5.5H10a6 6 0 0 0-6 6v5.5a.5.5 0 0 1-.5.5H3Z" />
|
||||
<path d="M6 18a4 4 0 0 0 4 4h8a4 4 0 0 0 4-4v-4h-3a5 5 0 0 1-5-5V6h-4a4 4 0 0 0-4 4v8Z" />
|
||||
<path d="M21.73 12a3 3 0 0 0-.6-.88l-4.25-4.24a3 3 0 0 0-.88-.61V9a3 3 0 0 0 3 3h2.73Z" />
|
||||
<path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1z" />
|
||||
<path d="M15 5H8c-1.1 0-1.99.9-1.99 2L6 21c0 1.1.89 2 1.99 2H19c1.1 0 2-.9 2-2V11l-6-6zM8 21V7h6v5h5v9H8z" />
|
||||
</g>
|
||||
</Icon>
|
||||
);
|
||||
|
|
|
@ -24,13 +24,12 @@ import { classNameFactory } from "@api/Styles";
|
|||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Flex } from "@components/Flex";
|
||||
import { gitRemote } from "@shared/vencordUserAgent";
|
||||
import { proxyLazy } from "@utils/lazy";
|
||||
import { Margins } from "@utils/margins";
|
||||
import { classes, isObjectEmpty } from "@utils/misc";
|
||||
import { ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
||||
import { OptionType, Plugin } from "@utils/types";
|
||||
import { findByPropsLazy, findComponentByCodeLazy } from "@webpack";
|
||||
import { Button, Clickable, FluxDispatcher, Forms, React, Text, Tooltip, UserStore, UserUtils } from "@webpack/common";
|
||||
import { find, findByProps, findComponentByCode } from "@webpack";
|
||||
import { Button, Clickable, FluxDispatcher, Forms, React, Text, Tooltip, UserUtils } from "@webpack/common";
|
||||
import { User } from "discord-types/general";
|
||||
import { Constructor } from "type-fest";
|
||||
|
||||
|
@ -50,9 +49,9 @@ import { GithubButton, WebsiteButton } from "./LinkIconButton";
|
|||
|
||||
const cl = classNameFactory("vc-plugin-modal-");
|
||||
|
||||
const UserSummaryItem = findComponentByCodeLazy("defaultRenderUser", "showDefaultAvatarsForNullUsers");
|
||||
const AvatarStyles = findByPropsLazy("moreUsers", "emptyUser", "avatarContainer", "clickableAvatar");
|
||||
const UserRecord: Constructor<Partial<User>> = proxyLazy(() => UserStore.getCurrentUser().constructor) as any;
|
||||
const UserSummaryItem = findComponentByCode("defaultRenderUser", "showDefaultAvatarsForNullUsers");
|
||||
const AvatarStyles = findByProps("moreUsers", "emptyUser", "avatarContainer", "clickableAvatar");
|
||||
const UserRecord = find<Constructor<Partial<User>>>(m => m?.prototype?.getAvatarURL && m?.prototype?.hasHadPremium);
|
||||
|
||||
interface PluginModalProps extends ModalProps {
|
||||
plugin: Plugin;
|
||||
|
|
|
@ -33,19 +33,19 @@ import { Margins } from "@utils/margins";
|
|||
import { classes, isObjectEmpty } from "@utils/misc";
|
||||
import { useAwaiter } from "@utils/react";
|
||||
import { Plugin } from "@utils/types";
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
import { findByProps } from "@webpack";
|
||||
import { Alerts, Button, Card, Forms, lodash, Parser, React, Select, Text, TextInput, Toasts, Tooltip, useMemo } from "@webpack/common";
|
||||
|
||||
import Plugins, { ExcludedPlugins } from "~plugins";
|
||||
|
||||
// Avoid circular dependency
|
||||
const { startDependenciesRecursive, startPlugin, stopPlugin } = proxyLazy(() => require("../../plugins"));
|
||||
const PluginManager = proxyLazy(() => require("../../plugins")) as typeof import("../../plugins");
|
||||
|
||||
const cl = classNameFactory("vc-plugins-");
|
||||
const logger = new Logger("PluginSettings", "#a6d189");
|
||||
|
||||
const InputStyles = findByPropsLazy("inputWrapper", "inputDefault", "error");
|
||||
const ButtonClasses = findByPropsLazy("button", "disabled", "enabled");
|
||||
const InputStyles = findByProps("inputWrapper", "inputDefault", "error");
|
||||
const ButtonClasses = findByProps("button", "disabled", "enabled");
|
||||
|
||||
|
||||
function showErrorToast(message: string) {
|
||||
|
@ -100,7 +100,7 @@ export function PluginCard({ plugin, disabled, onRestartNeeded, onMouseEnter, on
|
|||
|
||||
// If we're enabling a plugin, make sure all deps are enabled recursively.
|
||||
if (!wasEnabled) {
|
||||
const { restartNeeded, failures } = startDependenciesRecursive(plugin);
|
||||
const { restartNeeded, failures } = PluginManager.startDependenciesRecursive(plugin);
|
||||
if (failures.length) {
|
||||
logger.error(`Failed to start dependencies for ${plugin.name}: ${failures.join(", ")}`);
|
||||
showNotice("Failed to start dependencies: " + failures.join(", "), "Close", () => null);
|
||||
|
@ -126,7 +126,7 @@ export function PluginCard({ plugin, disabled, onRestartNeeded, onMouseEnter, on
|
|||
return;
|
||||
}
|
||||
|
||||
const result = wasEnabled ? stopPlugin(plugin) : startPlugin(plugin);
|
||||
const result = wasEnabled ? PluginManager.stopPlugin(plugin) : PluginManager.startPlugin(plugin);
|
||||
|
||||
if (!result) {
|
||||
settings.enabled = false;
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
import "./Switch.css";
|
||||
|
||||
import { classes } from "@utils/misc";
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
import { findByProps } from "@webpack";
|
||||
|
||||
interface SwitchProps {
|
||||
checked: boolean;
|
||||
|
@ -29,7 +29,7 @@ interface SwitchProps {
|
|||
|
||||
const SWITCH_ON = "var(--green-360)";
|
||||
const SWITCH_OFF = "var(--primary-400)";
|
||||
const SwitchClasses = findByPropsLazy("slider", "input", "container");
|
||||
const SwitchClasses = findByProps("slider", "input", "container");
|
||||
|
||||
export function Switch({ checked, onChange, disabled }: SwitchProps) {
|
||||
return (
|
||||
|
|
|
@ -22,7 +22,7 @@ import { Margins } from "@utils/margins";
|
|||
import { canonicalizeMatch, canonicalizeReplace } from "@utils/patches";
|
||||
import { makeCodeblock } from "@utils/text";
|
||||
import { Patch, ReplaceFn } from "@utils/types";
|
||||
import { search } from "@webpack";
|
||||
import { searchFactories } from "@webpack";
|
||||
import { Button, Clipboard, Forms, Parser, React, Switch, TextArea, TextInput } from "@webpack/common";
|
||||
|
||||
import { SettingsTab, wrapTab } from "./shared";
|
||||
|
@ -33,7 +33,7 @@ if (IS_DEV) {
|
|||
}
|
||||
|
||||
const findCandidates = debounce(function ({ find, setModule, setError }) {
|
||||
const candidates = search(find);
|
||||
const candidates = searchFactories(find);
|
||||
const keys = Object.keys(candidates);
|
||||
const len = keys.length;
|
||||
if (len === 0)
|
||||
|
@ -56,7 +56,7 @@ function ReplacementComponent({ module, match, replacement, setReplacementError
|
|||
const [compileResult, setCompileResult] = React.useState<[boolean, string]>();
|
||||
|
||||
const [patchedCode, matchResult, diff] = React.useMemo(() => {
|
||||
const src: string = fact.toString().replaceAll("\n", "");
|
||||
const src = String(fact).replaceAll("\n", "");
|
||||
|
||||
try {
|
||||
new RegExp(match);
|
||||
|
@ -382,7 +382,6 @@ function PatchHelper() {
|
|||
<Forms.FormTitle className={Margins.top20}>Code</Forms.FormTitle>
|
||||
<CodeBlock lang="js" content={code} />
|
||||
<Button onClick={() => Clipboard.copy(code)}>Copy to Clipboard</Button>
|
||||
<Button className={Margins.top8} onClick={() => Clipboard.copy("```ts\n" + code + "\n```")}>Copy as Codeblock</Button>
|
||||
</>
|
||||
)}
|
||||
</SettingsTab>
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Settings, useSettings } from "@api/Settings";
|
||||
import { useSettings } from "@api/Settings";
|
||||
import { classNameFactory } from "@api/Styles";
|
||||
import { Flex } from "@components/Flex";
|
||||
import { DeleteIcon, FolderIcon, PaintbrushIcon, PencilIcon, PlusIcon, RestartIcon } from "@components/Icons";
|
||||
|
@ -27,9 +27,9 @@ import { openInviteModal } from "@utils/discord";
|
|||
import { Margins } from "@utils/margins";
|
||||
import { showItemInFolder } from "@utils/native";
|
||||
import { useAwaiter } from "@utils/react";
|
||||
import { findLazy } from "@webpack";
|
||||
import { findComponentByFields } from "@webpack";
|
||||
import { Card, Forms, React, showToast, TabBar, TextArea, useEffect, useRef, useState } from "@webpack/common";
|
||||
import type { ComponentType, Ref, SyntheticEvent } from "react";
|
||||
import type { Ref, SyntheticEvent } from "react";
|
||||
|
||||
import Plugins from "~plugins";
|
||||
|
||||
|
@ -37,14 +37,14 @@ import { AddonCard } from "./AddonCard";
|
|||
import { QuickAction, QuickActionCard } from "./quickActions";
|
||||
import { SettingsTab, wrapTab } from "./shared";
|
||||
|
||||
type FileInput = ComponentType<{
|
||||
type FileInputProps = {
|
||||
ref: Ref<HTMLInputElement>;
|
||||
onChange: (e: SyntheticEvent<HTMLInputElement>) => void;
|
||||
multiple?: boolean;
|
||||
filters?: { name?: string; extensions: string[]; }[];
|
||||
}>;
|
||||
};
|
||||
|
||||
const FileInput: FileInput = findLazy(m => m.prototype?.activateUploadDialogue && m.prototype.setRef);
|
||||
const FileInput = findComponentByFields<FileInputProps>("activateUploadDialogue", "setRef");
|
||||
|
||||
const cl = classNameFactory("vc-settings-theme-");
|
||||
|
||||
|
@ -77,16 +77,8 @@ function Validators({ themeLinks }: { themeLinks: string[]; }) {
|
|||
<Forms.FormTitle className={Margins.top20} tag="h5">Validator</Forms.FormTitle>
|
||||
<Forms.FormText>This section will tell you whether your themes can successfully be loaded</Forms.FormText>
|
||||
<div>
|
||||
{themeLinks.map(rawLink => {
|
||||
const { label, link } = (() => {
|
||||
const match = /^@(light|dark) (.*)/.exec(rawLink);
|
||||
if (!match) return { label: rawLink, link: rawLink };
|
||||
|
||||
const [, mode, link] = match;
|
||||
return { label: `[${mode} mode only] ${link}`, link };
|
||||
})();
|
||||
|
||||
return <Card style={{
|
||||
{themeLinks.map(link => (
|
||||
<Card style={{
|
||||
padding: ".5em",
|
||||
marginBottom: ".5em",
|
||||
marginTop: ".5em"
|
||||
|
@ -94,11 +86,11 @@ function Validators({ themeLinks }: { themeLinks: string[]; }) {
|
|||
<Forms.FormTitle tag="h5" style={{
|
||||
overflowWrap: "break-word"
|
||||
}}>
|
||||
{label}
|
||||
{link}
|
||||
</Forms.FormTitle>
|
||||
<Validator link={link} />
|
||||
</Card>;
|
||||
})}
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
@ -257,7 +249,7 @@ function ThemesTab() {
|
|||
Icon={PaintbrushIcon}
|
||||
/>
|
||||
|
||||
{Settings.plugins.ClientTheme.enabled && (
|
||||
{Vencord.Plugins.isPluginEnabled("ClientTheme") && (
|
||||
<QuickAction
|
||||
text="Edit ClientTheme"
|
||||
action={() => openPluginModal(Plugins.ClientTheme)}
|
||||
|
@ -304,7 +296,6 @@ function ThemesTab() {
|
|||
<Card className="vc-settings-card vc-text-selectable">
|
||||
<Forms.FormTitle tag="h5">Paste links to css files here</Forms.FormTitle>
|
||||
<Forms.FormText>One link per line</Forms.FormText>
|
||||
<Forms.FormText>You can prefix lines with @light or @dark to toggle them based on your Discord theme</Forms.FormText>
|
||||
<Forms.FormText>Make sure to use direct links to files (raw or github.io)!</Forms.FormText>
|
||||
</Card>
|
||||
|
||||
|
|
|
@ -23,35 +23,61 @@ if (IS_DEV || IS_REPORTER) {
|
|||
var logger = new Logger("Tracer", "#FFD166");
|
||||
}
|
||||
|
||||
const noop = function () { };
|
||||
|
||||
export const beginTrace = !(IS_DEV || IS_REPORTER) ? noop :
|
||||
export const beginTrace = !(IS_DEV || IS_REPORTER) ? () => { } :
|
||||
function beginTrace(name: string, ...args: any[]) {
|
||||
if (name in traces)
|
||||
if (name in traces) {
|
||||
throw new Error(`Trace ${name} already exists!`);
|
||||
}
|
||||
|
||||
traces[name] = [performance.now(), args];
|
||||
};
|
||||
|
||||
export const finishTrace = !(IS_DEV || IS_REPORTER) ? noop : function finishTrace(name: string) {
|
||||
export const finishTrace = !(IS_DEV || IS_REPORTER) ? () => 0 :
|
||||
function finishTrace(name: string) {
|
||||
const end = performance.now();
|
||||
|
||||
const [start, args] = traces[name];
|
||||
delete traces[name];
|
||||
|
||||
logger.debug(`${name} took ${end - start}ms`, args);
|
||||
const totalTime = end - start;
|
||||
logger.debug(`${name} took ${totalTime}ms`, args);
|
||||
|
||||
return totalTime;
|
||||
};
|
||||
|
||||
type Func = (...args: any[]) => any;
|
||||
type TraceNameMapper<F extends Func> = (...args: Parameters<F>) => string;
|
||||
|
||||
const noopTracer =
|
||||
<F extends Func>(name: string, f: F, mapper?: TraceNameMapper<F>) => f;
|
||||
function noopTracerWithResults<F extends Func>(name: string, f: F, mapper?: TraceNameMapper<F>) {
|
||||
return function (this: unknown, ...args: Parameters<F>): [ReturnType<F>, number] {
|
||||
return [f.apply(this, args), 0];
|
||||
};
|
||||
}
|
||||
|
||||
function noopTracer<F extends Func>(name: string, f: F, mapper?: TraceNameMapper<F>) {
|
||||
return f;
|
||||
}
|
||||
|
||||
export const traceFunctionWithResults = !(IS_DEV || IS_REPORTER)
|
||||
? noopTracerWithResults
|
||||
: function traceFunctionWithResults<F extends Func>(name: string, f: F, mapper?: TraceNameMapper<F>): (this: unknown, ...args: Parameters<F>) => [ReturnType<F>, number] {
|
||||
return function (this: unknown, ...args: Parameters<F>) {
|
||||
const traceName = mapper?.(...args) ?? name;
|
||||
|
||||
beginTrace(traceName, ...arguments);
|
||||
try {
|
||||
return [f.apply(this, args), finishTrace(traceName)];
|
||||
} catch (e) {
|
||||
finishTrace(traceName);
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const traceFunction = !(IS_DEV || IS_REPORTER)
|
||||
? noopTracer
|
||||
: function traceFunction<F extends Func>(name: string, f: F, mapper?: TraceNameMapper<F>): F {
|
||||
return function (this: any, ...args: Parameters<F>) {
|
||||
return function (this: unknown, ...args: Parameters<F>) {
|
||||
const traceName = mapper?.(...args) ?? name;
|
||||
|
||||
beginTrace(traceName, ...arguments);
|
||||
|
|
|
@ -8,10 +8,11 @@ import { Logger } from "@utils/Logger";
|
|||
import { canonicalizeMatch } from "@utils/patches";
|
||||
import * as Webpack from "@webpack";
|
||||
import { wreq } from "@webpack";
|
||||
|
||||
const LazyChunkLoaderLogger = new Logger("LazyChunkLoader");
|
||||
import { AnyModuleFactory, ModuleFactory } from "webpack";
|
||||
|
||||
export async function loadLazyChunks() {
|
||||
const LazyChunkLoaderLogger = new Logger("LazyChunkLoader");
|
||||
|
||||
try {
|
||||
LazyChunkLoaderLogger.log("Loading all chunks...");
|
||||
|
||||
|
@ -25,16 +26,12 @@ export async function loadLazyChunks() {
|
|||
// True if resolved, false otherwise
|
||||
const chunksSearchPromises = [] as Array<() => boolean>;
|
||||
|
||||
const LazyChunkRegex = canonicalizeMatch(/(?:(?:Promise\.all\(\[)?(\i\.e\("?[^)]+?"?\)[^\]]*?)(?:\]\))?)\.then\(\i\.bind\(\i,"?([^)]+?)"?\)\)/g);
|
||||
const LazyChunkRegex = canonicalizeMatch(/(?:(?:Promise\.all\(\[)?(\i\.e\("?[^)]+?"?\)[^\]]*?)(?:\]\))?)\.then\(\i(?:\.\i)?\.bind\(\i,"?([^)]+?)"?(?:,[^)]+?)?\)\)/g);
|
||||
|
||||
async function searchAndLoadLazyChunks(factoryCode: string) {
|
||||
const lazyChunks = factoryCode.matchAll(LazyChunkRegex);
|
||||
const validChunkGroups = new Set<[chunkIds: number[], entryPoint: number]>();
|
||||
|
||||
// Workaround for a chunk that depends on the ChannelMessage component but may be be force loaded before
|
||||
// the chunk containing the component
|
||||
const shouldForceDefer = factoryCode.includes(".Messages.GUILD_FEED_UNFEATURE_BUTTON_TEXT");
|
||||
|
||||
await Promise.all(Array.from(lazyChunks).map(async ([, rawChunkIds, entryPoint]) => {
|
||||
const chunkIds = rawChunkIds ? Array.from(rawChunkIds.matchAll(Webpack.ChunkIdsRegex)).map(m => Number(m[1])) : [];
|
||||
|
||||
|
@ -69,19 +66,14 @@ export async function loadLazyChunks() {
|
|||
await Promise.all(
|
||||
Array.from(validChunkGroups)
|
||||
.map(([chunkIds]) =>
|
||||
Promise.all(chunkIds.map(id => wreq.e(id as any).catch(() => { })))
|
||||
Promise.all(chunkIds.map(id => wreq.e(id)))
|
||||
)
|
||||
);
|
||||
|
||||
// Requires the entry points for all valid chunk groups
|
||||
for (const [, entryPoint] of validChunkGroups) {
|
||||
try {
|
||||
if (shouldForceDefer) {
|
||||
deferredRequires.add(entryPoint);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (wreq.m[entryPoint]) wreq(entryPoint as any);
|
||||
if (wreq.m[entryPoint]) wreq(entryPoint);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
|
@ -109,32 +101,33 @@ export async function loadLazyChunks() {
|
|||
}, 0);
|
||||
}
|
||||
|
||||
Webpack.factoryListeners.add(factory => {
|
||||
function factoryListener(factory: AnyModuleFactory | ModuleFactory) {
|
||||
let isResolved = false;
|
||||
searchAndLoadLazyChunks(factory.toString()).then(() => isResolved = true);
|
||||
|
||||
chunksSearchPromises.push(() => isResolved);
|
||||
});
|
||||
|
||||
for (const factoryId in wreq.m) {
|
||||
let isResolved = false;
|
||||
searchAndLoadLazyChunks(wreq.m[factoryId].toString()).then(() => isResolved = true);
|
||||
searchAndLoadLazyChunks(String(factory))
|
||||
.then(() => isResolved = true)
|
||||
.catch(() => isResolved = true);
|
||||
|
||||
chunksSearchPromises.push(() => isResolved);
|
||||
}
|
||||
|
||||
Webpack.factoryListeners.add(factoryListener);
|
||||
for (const factoryId in wreq.m) {
|
||||
factoryListener(wreq.m[factoryId]);
|
||||
}
|
||||
|
||||
await chunksSearchingDone;
|
||||
Webpack.factoryListeners.delete(factoryListener);
|
||||
|
||||
// Require deferred entry points
|
||||
for (const deferredRequire of deferredRequires) {
|
||||
wreq!(deferredRequire as any);
|
||||
wreq(deferredRequire);
|
||||
}
|
||||
|
||||
// All chunks Discord has mapped to asset files, even if they are not used anymore
|
||||
const allChunks = [] as number[];
|
||||
|
||||
// Matches "id" or id:
|
||||
for (const currentMatch of wreq!.u.toString().matchAll(/(?:"([\deE]+?)"(?![,}]))|(?:([\deE]+?):)/g)) {
|
||||
for (const currentMatch of String(wreq.u).matchAll(/(?:"([\deE]+?)"(?![,}]))|(?:([\deE]+?):)/g)) {
|
||||
const id = currentMatch[1] ?? currentMatch[2];
|
||||
if (id == null) continue;
|
||||
|
||||
|
@ -143,7 +136,8 @@ export async function loadLazyChunks() {
|
|||
|
||||
if (allChunks.length === 0) throw new Error("Failed to get all chunks");
|
||||
|
||||
// Chunks that are not loaded (not used) by Discord code anymore
|
||||
// Chunks which our regex could not catch to load
|
||||
// It will always contain WebWorker assets, and also currently contains some language packs which are loaded differently
|
||||
const chunksLeft = allChunks.filter(id => {
|
||||
return !(validChunks.has(id) || invalidChunks.has(id));
|
||||
});
|
||||
|
@ -153,12 +147,9 @@ export async function loadLazyChunks() {
|
|||
.then(r => r.text())
|
||||
.then(t => t.includes("importScripts("));
|
||||
|
||||
// Loads and requires a chunk
|
||||
// Loads the chunk. Currently this only happens with the language packs which are loaded differently
|
||||
if (!isWorkerAsset) {
|
||||
await wreq.e(id as any);
|
||||
// Technically, the id of the chunk does not match the entry point
|
||||
// But, still try it because we have no way to get the actual entry point
|
||||
if (wreq.m[id]) wreq(id as any);
|
||||
await wreq.e(id);
|
||||
}
|
||||
}));
|
||||
|
||||
|
|
|
@ -4,22 +4,39 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { SYM_LAZY_COMPONENT_INNER } from "@utils/lazyReact";
|
||||
import { Logger } from "@utils/Logger";
|
||||
import { SYM_PROXY_INNER_GET, SYM_PROXY_INNER_VALUE } from "@utils/proxyInner";
|
||||
import * as Webpack from "@webpack";
|
||||
import { patches } from "plugins";
|
||||
import { addPatch, patches } from "plugins";
|
||||
|
||||
import { loadLazyChunks } from "./loadLazyChunks";
|
||||
|
||||
async function runReporter() {
|
||||
const ReporterLogger = new Logger("Reporter");
|
||||
|
||||
async function runReporter() {
|
||||
try {
|
||||
ReporterLogger.log("Starting test...");
|
||||
|
||||
let loadLazyChunksResolve: (value: void | PromiseLike<void>) => void;
|
||||
let loadLazyChunksResolve: (value: void) => void;
|
||||
const loadLazyChunksDone = new Promise<void>(r => loadLazyChunksResolve = r);
|
||||
|
||||
Webpack.beforeInitListeners.add(() => loadLazyChunks().then((loadLazyChunksResolve)));
|
||||
// The main patch for starting the reporter chunk loading
|
||||
addPatch({
|
||||
find: '"Could not find app-mount"',
|
||||
replacement: {
|
||||
match: /(?<="use strict";)/,
|
||||
replace: "Vencord.Webpack._initReporter();"
|
||||
}
|
||||
}, "Vencord Reporter");
|
||||
|
||||
// @ts-ignore
|
||||
Vencord.Webpack._initReporter = function () {
|
||||
// initReporter is called in the patched entry point of Discord
|
||||
// setImmediate to only start searching for lazy chunks after Discord initialized the app
|
||||
setTimeout(() => loadLazyChunks().then(loadLazyChunksResolve), 0);
|
||||
};
|
||||
|
||||
await loadLazyChunksDone;
|
||||
|
||||
for (const patch of patches) {
|
||||
|
@ -28,52 +45,158 @@ async function runReporter() {
|
|||
}
|
||||
}
|
||||
|
||||
for (const [searchType, args] of Webpack.lazyWebpackSearchHistory) {
|
||||
let method = searchType;
|
||||
|
||||
if (searchType === "findComponent") method = "find";
|
||||
if (searchType === "findExportedComponent") method = "findByProps";
|
||||
if (searchType === "waitFor" || searchType === "waitForComponent") {
|
||||
if (typeof args[0] === "string") method = "findByProps";
|
||||
else method = "find";
|
||||
for (const [plugin, moduleId, match, totalTime] of Vencord.WebpackPatcher.patchTimings) {
|
||||
if (totalTime > 3) {
|
||||
new Logger("WebpackInterceptor").warn(`Patch by ${plugin} took ${totalTime}ms (Module id is ${String(moduleId)}): ${match}`);
|
||||
}
|
||||
}
|
||||
if (searchType === "waitForStore") method = "findStore";
|
||||
|
||||
let result: any;
|
||||
await Promise.all(Webpack.webpackSearchHistory.map(async ([searchType, args]) => {
|
||||
args = [...args];
|
||||
|
||||
let result = null as any;
|
||||
try {
|
||||
if (method === "proxyLazyWebpack" || method === "LazyComponentWebpack") {
|
||||
switch (searchType) {
|
||||
case "webpackDependantLazy":
|
||||
case "webpackDependantLazyComponent": {
|
||||
const [factory] = args;
|
||||
result = factory();
|
||||
} else if (method === "extractAndLoadChunks") {
|
||||
const [code, matcher] = args;
|
||||
break;
|
||||
}
|
||||
case "extractAndLoadChunks": {
|
||||
const extractAndLoadChunks = args.shift();
|
||||
|
||||
result = await Webpack.extractAndLoadChunks(code, matcher);
|
||||
if (result === false) result = null;
|
||||
} else if (method === "mapMangledModule") {
|
||||
const [code, mapper] = args;
|
||||
|
||||
result = Webpack.mapMangledModule(code, mapper);
|
||||
if (Object.keys(result).length !== Object.keys(mapper).length) throw new Error("Webpack Find Fail");
|
||||
} else {
|
||||
// @ts-ignore
|
||||
result = Webpack[method](...args);
|
||||
result = await extractAndLoadChunks();
|
||||
if (result === false) {
|
||||
result = null;
|
||||
}
|
||||
|
||||
if (result == null || (result.$$vencordInternal != null && result.$$vencordInternal() == null)) throw new Error("Webpack Find Fail");
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
const findResult = args.shift();
|
||||
|
||||
if (findResult != null) {
|
||||
if (findResult.$$vencordCallbackCalled != null && findResult.$$vencordCallbackCalled()) {
|
||||
result = findResult;
|
||||
break;
|
||||
}
|
||||
|
||||
if (findResult[SYM_PROXY_INNER_GET] != null) {
|
||||
result = findResult[SYM_PROXY_INNER_VALUE];
|
||||
break;
|
||||
}
|
||||
|
||||
if (findResult[SYM_LAZY_COMPONENT_INNER] != null) {
|
||||
result = findResult[SYM_LAZY_COMPONENT_INNER]();
|
||||
break;
|
||||
}
|
||||
|
||||
if (searchType === "mapMangledModule") {
|
||||
result = findResult;
|
||||
|
||||
for (const innerMap in result) {
|
||||
if (
|
||||
(result[innerMap][SYM_PROXY_INNER_GET] != null && result[innerMap][SYM_PROXY_INNER_VALUE] == null) ||
|
||||
(result[innerMap][SYM_LAZY_COMPONENT_INNER] != null && result[innerMap][SYM_LAZY_COMPONENT_INNER]() == null)
|
||||
) {
|
||||
throw new Error("Webpack Find Fail");
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// This can happen if a `find` was immediately found
|
||||
result = findResult;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (result == null) {
|
||||
throw new Error("Webpack Find Fail");
|
||||
}
|
||||
} catch (e) {
|
||||
let logMessage = searchType;
|
||||
if (method === "find" || method === "proxyLazyWebpack" || method === "LazyComponentWebpack") logMessage += `(${args[0].toString().slice(0, 147)}...)`;
|
||||
else if (method === "extractAndLoadChunks") logMessage += `([${args[0].map(arg => `"${arg}"`).join(", ")}], ${args[1].toString()})`;
|
||||
else if (method === "mapMangledModule") {
|
||||
const failedMappings = Object.keys(args[1]).filter(key => result?.[key] == null);
|
||||
|
||||
logMessage += `("${args[0]}", {\n${failedMappings.map(mapping => `\t${mapping}: ${args[1][mapping].toString().slice(0, 147)}...`).join(",\n")}\n})`;
|
||||
let filterName = "";
|
||||
let parsedArgs = args;
|
||||
|
||||
if (args[0].$$vencordProps != null) {
|
||||
if (["find", "findComponent", "waitFor"].includes(searchType)) {
|
||||
filterName = args[0].$$vencordProps[0];
|
||||
parsedArgs = args[0].$$vencordProps.slice(1);
|
||||
} else {
|
||||
parsedArgs = args[0].$$vencordProps;
|
||||
}
|
||||
}
|
||||
|
||||
function stringifyFilter(code: Webpack.CodeFilterWithSingle) {
|
||||
if (Array.isArray(code)) {
|
||||
return `[${code.map(arg => arg instanceof RegExp ? String(arg) : JSON.stringify(arg)).join(", ")}]`;
|
||||
}
|
||||
|
||||
return code instanceof RegExp ? String(code) : JSON.stringify(code);
|
||||
}
|
||||
|
||||
// if parsedArgs is the same as args, it means vencordProps of the filter was not available (like in normal filter functions),
|
||||
// so log the filter function instead
|
||||
if (
|
||||
parsedArgs === args &&
|
||||
["waitFor", "find", "findComponent", "webpackDependantLazy", "webpackDependantLazyComponent"].includes(searchType)
|
||||
) {
|
||||
let filter = String(parsedArgs[0]);
|
||||
if (filter.length > 150) {
|
||||
filter = filter.slice(0, 147) + "...";
|
||||
}
|
||||
|
||||
logMessage += `(${filter})`;
|
||||
} else if (searchType === "extractAndLoadChunks") {
|
||||
const [code, matcher] = parsedArgs;
|
||||
|
||||
let regexStr: string;
|
||||
if (matcher === Webpack.DefaultExtractAndLoadChunksRegex) {
|
||||
regexStr = "DefaultExtractAndLoadChunksRegex";
|
||||
} else {
|
||||
regexStr = String(matcher);
|
||||
}
|
||||
|
||||
logMessage += `(${stringifyFilter(code)}, ${regexStr})`;
|
||||
} else if (searchType === "mapMangledModule") {
|
||||
const [code, mappers] = parsedArgs;
|
||||
|
||||
const parsedFailedMappers = Object.entries<any>(mappers)
|
||||
.filter(([key]) =>
|
||||
result == null ||
|
||||
(result[key]?.[SYM_PROXY_INNER_GET] != null && result[key][SYM_PROXY_INNER_VALUE] == null) ||
|
||||
(result[key]?.[SYM_LAZY_COMPONENT_INNER] != null && result[key][SYM_LAZY_COMPONENT_INNER]() == null)
|
||||
)
|
||||
.map(([key, filter]) => {
|
||||
let parsedFilter: string;
|
||||
|
||||
if (filter.$$vencordProps != null) {
|
||||
const filterName = filter.$$vencordProps[0];
|
||||
parsedFilter = `${filterName}(${filter.$$vencordProps.slice(1).map((arg: any) => arg instanceof RegExp ? String(arg) : JSON.stringify(arg)).join(", ")})`;
|
||||
} else {
|
||||
parsedFilter = String(filter);
|
||||
if (parsedFilter.length > 150) {
|
||||
parsedFilter = parsedFilter.slice(0, 147) + "...";
|
||||
}
|
||||
}
|
||||
|
||||
return [key, parsedFilter];
|
||||
});
|
||||
|
||||
logMessage += `(${stringifyFilter(code)}, {\n${parsedFailedMappers.map(([key, parsedFilter]) => `\t${key}: ${parsedFilter}`).join(",\n")}\n})`;
|
||||
} else {
|
||||
logMessage += `(${filterName.length ? `${filterName}(` : ""}${parsedArgs.map(stringifyFilter).join(", ")})${filterName.length ? ")" : ""}`;
|
||||
}
|
||||
else logMessage += `(${args.map(arg => `"${arg}"`).join(", ")})`;
|
||||
|
||||
ReporterLogger.log("Webpack Find Fail:", logMessage);
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
ReporterLogger.log("Finished test");
|
||||
} catch (e) {
|
||||
|
@ -81,4 +204,5 @@ async function runReporter() {
|
|||
}
|
||||
}
|
||||
|
||||
runReporter();
|
||||
// Run after the Vencord object has been created
|
||||
setTimeout(runReporter, 0);
|
||||
|
|
7
src/globals.d.ts
vendored
7
src/globals.d.ts
vendored
|
@ -64,13 +64,8 @@ declare global {
|
|||
export var Vesktop: any;
|
||||
export var VesktopNative: any;
|
||||
|
||||
interface Window {
|
||||
webpackChunkdiscord_app: {
|
||||
push(chunk: any): any;
|
||||
pop(): any;
|
||||
};
|
||||
interface Window extends Record<PropertyKey, any> {
|
||||
_: LoDashStatic;
|
||||
[k: string]: any;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
3
src/plugins/_api/badges/fixBadgeOverflow.css
Normal file
3
src/plugins/_api/badges/fixBadgeOverflow.css
Normal file
|
@ -0,0 +1,3 @@
|
|||
[class*="profileBadges"] {
|
||||
flex: none;
|
||||
}
|
|
@ -16,6 +16,8 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import "./fixBadgeOverflow.css";
|
||||
|
||||
import { _getBadges, BadgePosition, BadgeUserArgs, ProfileBadge } from "@api/Badges";
|
||||
import DonateButton from "@components/DonateButton";
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
|
@ -77,7 +79,7 @@ export default definePlugin({
|
|||
replace: "...$1.props,$& $1.image??"
|
||||
},
|
||||
{
|
||||
match: /(?<=text:(\i)\.description,.{0,200})children:/,
|
||||
match: /(?<=text:(\i)\.description,.{0,50})children:/,
|
||||
replace: "children:$1.component ? $self.renderBadgeComponent({ ...$1 }) :"
|
||||
},
|
||||
// conditionally override their onClick with badge.onClick if it exists
|
||||
|
|
|
@ -34,7 +34,7 @@ export default definePlugin({
|
|||
{
|
||||
find: "Messages.SERVERS,children",
|
||||
replacement: {
|
||||
match: /(?<=Messages\.SERVERS,children:)\i\.map\(\i\)/,
|
||||
match: /(?<=Messages\.SERVERS,children:).+?default:return null\}\}\)/,
|
||||
replace: "Vencord.Api.ServerList.renderAll(Vencord.Api.ServerList.ServerListRenderPosition.In).concat($&)"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import { definePluginSettings } from "@api/Settings";
|
|||
import { Devs } from "@utils/constants";
|
||||
import { Logger } from "@utils/Logger";
|
||||
import definePlugin, { OptionType, StartAt } from "@utils/types";
|
||||
import { WebpackRequire } from "webpack";
|
||||
|
||||
const settings = definePluginSettings({
|
||||
disableAnalytics: {
|
||||
|
@ -48,7 +49,7 @@ export default definePlugin({
|
|||
},
|
||||
},
|
||||
{
|
||||
find: ".METRICS",
|
||||
find: ".METRICS,",
|
||||
replacement: [
|
||||
{
|
||||
match: /this\._intervalId=/,
|
||||
|
@ -81,9 +82,9 @@ export default definePlugin({
|
|||
Object.defineProperty(Function.prototype, "g", {
|
||||
configurable: true,
|
||||
|
||||
set(v: any) {
|
||||
set(this: WebpackRequire, globalObj: WebpackRequire["g"]) {
|
||||
Object.defineProperty(this, "g", {
|
||||
value: v,
|
||||
value: globalObj,
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
writable: true
|
||||
|
@ -92,11 +93,11 @@ export default definePlugin({
|
|||
// Ensure this is most likely the Sentry WebpackInstance.
|
||||
// Function.g is a very generic property and is not uncommon for another WebpackInstance (or even a React component: <g></g>) to include it
|
||||
const { stack } = new Error();
|
||||
if (!(stack?.includes("discord.com") || stack?.includes("discordapp.com")) || !String(this).includes("exports:{}") || this.c != null) {
|
||||
if (this.c != null || !stack?.includes("http") || !String(this).includes("exports:{}")) {
|
||||
return;
|
||||
}
|
||||
|
||||
const assetPath = stack?.match(/\/assets\/.+?\.js/)?.[0];
|
||||
const assetPath = stack.match(/http.+?(?=:\d+?:\d+?$)/m)?.[0];
|
||||
if (!assetPath) {
|
||||
return;
|
||||
}
|
||||
|
@ -106,7 +107,8 @@ export default definePlugin({
|
|||
srcRequest.send();
|
||||
|
||||
// Final condition to see if this is the Sentry WebpackInstance
|
||||
if (!srcRequest.responseText.includes("window.DiscordSentry=")) {
|
||||
// This is matching window.DiscordSentry=, but without `window` to avoid issues on some proxies
|
||||
if (!srcRequest.responseText.includes(".DiscordSentry=")) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Settings } from "@api/Settings";
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import BackupAndRestoreTab from "@components/VencordSettings/BackupAndRestoreTab";
|
||||
import CloudTab from "@components/VencordSettings/CloudTab";
|
||||
import PatchHelperTab from "@components/VencordSettings/PatchHelperTab";
|
||||
|
@ -33,11 +33,27 @@ import gitHash from "~git-hash";
|
|||
type SectionType = "HEADER" | "DIVIDER" | "CUSTOM";
|
||||
type SectionTypes = Record<SectionType, SectionType>;
|
||||
|
||||
const settings = definePluginSettings({
|
||||
settingsLocation: {
|
||||
type: OptionType.SELECT,
|
||||
description: "Where to put the Vencord settings section",
|
||||
options: [
|
||||
{ label: "At the very top", value: "top" },
|
||||
{ label: "Above the Nitro section", value: "aboveNitro", default: true },
|
||||
{ label: "Below the Nitro section", value: "belowNitro" },
|
||||
{ label: "Above Activity Settings", value: "aboveActivity" },
|
||||
{ label: "Below Activity Settings", value: "belowActivity" },
|
||||
{ label: "At the very bottom", value: "bottom" },
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
export default definePlugin({
|
||||
name: "Settings",
|
||||
description: "Adds Settings UI and debug info",
|
||||
authors: [Devs.Ven, Devs.Megu],
|
||||
required: true,
|
||||
settings,
|
||||
|
||||
patches: [
|
||||
{
|
||||
|
@ -64,7 +80,7 @@ export default definePlugin({
|
|||
replace: (_, sectionTypes, commaOrSemi, elements, element) => `${commaOrSemi} $self.addSettings(${elements}, ${element}, ${sectionTypes}) ${commaOrSemi}`
|
||||
},
|
||||
{
|
||||
match: /({(?=.+?function (\i).{0,120}(\i)=\i\.useMemo.{0,60}return \i\.useMemo\(\(\)=>\i\(\3).+?function\(\){return )\2(?=})/,
|
||||
match: /({(?=.+?function (\i).{0,120}(\i)=\i\.useMemo.{0,30}return \i\.useMemo\(\(\)=>\i\(\3).+?function\(\){return )\2(?=})/,
|
||||
replace: (_, rest, settingsHook) => `${rest}$self.wrapSettingsHook(${settingsHook})`
|
||||
}
|
||||
]
|
||||
|
@ -136,12 +152,12 @@ export default definePlugin({
|
|||
].filter(Boolean);
|
||||
},
|
||||
|
||||
isRightSpot({ header, settings }: { header?: string; settings?: string[]; }) {
|
||||
const firstChild = settings?.[0];
|
||||
isRightSpot({ header, settingsChilds }: { header?: string; settingsChilds?: string[]; }) {
|
||||
const firstChild = settingsChilds?.[0];
|
||||
// lowest two elements... sanity backup
|
||||
if (firstChild === "LOGOUT" || firstChild === "SOCIAL_LINKS") return true;
|
||||
|
||||
const { settingsLocation } = Settings.plugins.Settings;
|
||||
const { settingsLocation } = settings.store;
|
||||
|
||||
if (settingsLocation === "bottom") return firstChild === "LOGOUT";
|
||||
if (settingsLocation === "belowActivity") return firstChild === "CHANGELOG";
|
||||
|
@ -181,21 +197,6 @@ export default definePlugin({
|
|||
};
|
||||
},
|
||||
|
||||
options: {
|
||||
settingsLocation: {
|
||||
type: OptionType.SELECT,
|
||||
description: "Where to put the Vencord settings section",
|
||||
options: [
|
||||
{ label: "At the very top", value: "top" },
|
||||
{ label: "Above the Nitro section", value: "aboveNitro", default: true },
|
||||
{ label: "Below the Nitro section", value: "belowNitro" },
|
||||
{ label: "Above Activity Settings", value: "aboveActivity" },
|
||||
{ label: "Below Activity Settings", value: "belowActivity" },
|
||||
{ label: "At the very bottom", value: "bottom" },
|
||||
]
|
||||
},
|
||||
},
|
||||
|
||||
get electronVersion() {
|
||||
return VencordNative.native.getVersions().electron || window.armcord?.electron || null;
|
||||
},
|
||||
|
|
|
@ -59,7 +59,7 @@ const TrustedRolesIds = [
|
|||
|
||||
const AsyncFunction = async function () { }.constructor;
|
||||
|
||||
const ShowCurrentGame = getUserSettingLazy<boolean>("status", "showCurrentGame")!;
|
||||
const ShowCurrentGame = getUserSettingLazy<boolean>("status", "showCurrentGame");
|
||||
|
||||
async function forceUpdate() {
|
||||
const outdated = await checkForUpdates();
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
# AccountPanelServerProfile
|
||||
|
||||
Right click your account panel in the bottom left to view your profile in the current server
|
||||
|
||||
![](https://github.com/user-attachments/assets/3228497d-488f-479c-93d2-a32ccdb08f0f)
|
||||
|
||||
![](https://github.com/user-attachments/assets/6fc45363-d95f-4810-812f-2f9fb28b41b5)
|
|
@ -1,134 +0,0 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2024 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Devs } from "@utils/constants";
|
||||
import { getCurrentChannel } from "@utils/discord";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findByPropsLazy, findComponentByCodeLazy } from "@webpack";
|
||||
import { ContextMenuApi, Menu, useEffect, useRef } from "@webpack/common";
|
||||
import { User } from "discord-types/general";
|
||||
|
||||
interface UserProfileProps {
|
||||
popoutProps: Record<string, any>;
|
||||
currentUser: User;
|
||||
originalPopout: () => React.ReactNode;
|
||||
}
|
||||
|
||||
const UserProfile = findComponentByCodeLazy("UserProfilePopoutWrapper: user cannot be undefined");
|
||||
const styles = findByPropsLazy("accountProfilePopoutWrapper");
|
||||
|
||||
let openAlternatePopout = false;
|
||||
let accountPanelRef: React.MutableRefObject<Record<PropertyKey, any> | null> = { current: null };
|
||||
|
||||
const AccountPanelContextMenu = ErrorBoundary.wrap(() => {
|
||||
const { prioritizeServerProfile } = settings.use(["prioritizeServerProfile"]);
|
||||
|
||||
return (
|
||||
<Menu.Menu
|
||||
navId="vc-ap-server-profile"
|
||||
onClose={ContextMenuApi.closeContextMenu}
|
||||
>
|
||||
<Menu.MenuItem
|
||||
id="vc-ap-view-alternate-popout"
|
||||
label={prioritizeServerProfile ? "View Account Profile" : "View Server Profile"}
|
||||
disabled={getCurrentChannel()?.getGuildId() == null}
|
||||
action={e => {
|
||||
openAlternatePopout = true;
|
||||
accountPanelRef.current?.props.onMouseDown();
|
||||
accountPanelRef.current?.props.onClick(e);
|
||||
}}
|
||||
/>
|
||||
<Menu.MenuCheckboxItem
|
||||
id="vc-ap-prioritize-server-profile"
|
||||
label="Prioritize Server Profile"
|
||||
checked={prioritizeServerProfile}
|
||||
action={() => settings.store.prioritizeServerProfile = !prioritizeServerProfile}
|
||||
/>
|
||||
</Menu.Menu>
|
||||
);
|
||||
}, { noop: true });
|
||||
|
||||
const settings = definePluginSettings({
|
||||
prioritizeServerProfile: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Prioritize Server Profile when left clicking your account panel",
|
||||
default: false
|
||||
}
|
||||
});
|
||||
|
||||
export default definePlugin({
|
||||
name: "AccountPanelServerProfile",
|
||||
description: "Right click your account panel in the bottom left to view your profile in the current server",
|
||||
authors: [Devs.Nuckyz, Devs.relitrix],
|
||||
settings,
|
||||
|
||||
patches: [
|
||||
{
|
||||
find: ".Messages.ACCOUNT_SPEAKING_WHILE_MUTED",
|
||||
group: true,
|
||||
replacement: [
|
||||
{
|
||||
match: /(?<=\.SIZE_32\)}\);)/,
|
||||
replace: "$self.useAccountPanelRef();"
|
||||
},
|
||||
{
|
||||
match: /(\.AVATAR,children:.+?renderPopout:(\i)=>){(.+?)}(?=,position)(?<=currentUser:(\i).+?)/,
|
||||
replace: (_, rest, popoutProps, originalPopout, currentUser) => `${rest}$self.UserProfile({popoutProps:${popoutProps},currentUser:${currentUser},originalPopout:()=>{${originalPopout}}})`
|
||||
},
|
||||
{
|
||||
match: /\.AVATAR,children:.+?(?=renderPopout:)/,
|
||||
replace: "$&onRequestClose:$self.onPopoutClose,"
|
||||
},
|
||||
{
|
||||
match: /(?<=.avatarWrapper,)/,
|
||||
replace: "ref:$self.accountPanelRef,onContextMenu:$self.openAccountPanelContextMenu,"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
get accountPanelRef() {
|
||||
return accountPanelRef;
|
||||
},
|
||||
|
||||
useAccountPanelRef() {
|
||||
useEffect(() => () => {
|
||||
accountPanelRef.current = null;
|
||||
}, []);
|
||||
|
||||
return (accountPanelRef = useRef(null));
|
||||
},
|
||||
|
||||
openAccountPanelContextMenu(event: React.UIEvent) {
|
||||
ContextMenuApi.openContextMenu(event, AccountPanelContextMenu);
|
||||
},
|
||||
|
||||
onPopoutClose() {
|
||||
openAlternatePopout = false;
|
||||
},
|
||||
|
||||
UserProfile: ErrorBoundary.wrap(({ popoutProps, currentUser, originalPopout }: UserProfileProps) => {
|
||||
if (
|
||||
(settings.store.prioritizeServerProfile && openAlternatePopout) ||
|
||||
(!settings.store.prioritizeServerProfile && !openAlternatePopout)
|
||||
) {
|
||||
return originalPopout();
|
||||
}
|
||||
|
||||
const currentChannel = getCurrentChannel();
|
||||
if (currentChannel?.getGuildId() == null) {
|
||||
return originalPopout();
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.accountProfilePopoutWrapper}>
|
||||
<UserProfile {...popoutProps} userId={currentUser.id} guildId={currentChannel.getGuildId()} channelId={currentChannel.id} />
|
||||
</div>
|
||||
);
|
||||
}, { noop: true })
|
||||
});
|
|
@ -1,3 +0,0 @@
|
|||
# Always Expand Roles
|
||||
|
||||
Always expands the role list in profile popouts
|
|
@ -21,12 +21,12 @@ import { definePluginSettings } from "@api/Settings";
|
|||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findByCodeLazy, findByPropsLazy } from "@webpack";
|
||||
import { findByProps, findComponentByCode } from "@webpack";
|
||||
|
||||
type AnonUpload = Upload & { anonymise?: boolean; };
|
||||
|
||||
const ActionBarIcon = findByCodeLazy(".actionBarIcon)");
|
||||
const UploadDraft = findByPropsLazy("popFirstFile", "update");
|
||||
const ActionBarIcon = findComponentByCode(".actionBarIcon)");
|
||||
const UploadDraft = findByProps("popFirstFile", "update");
|
||||
|
||||
const enum Methods {
|
||||
Random,
|
||||
|
|
|
@ -20,10 +20,10 @@ import { popNotice, showNotice } from "@api/Notices";
|
|||
import { Link } from "@components/Link";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { ReporterTestable } from "@utils/types";
|
||||
import { findByCodeLazy } from "@webpack";
|
||||
import { findByCode } from "@webpack";
|
||||
import { ApplicationAssetUtils, FluxDispatcher, Forms, Toasts } from "@webpack/common";
|
||||
|
||||
const fetchApplicationsRPC = findByCodeLazy("APPLICATION_RPC(", "Client ID");
|
||||
const fetchApplicationsRPC = findByCode("APPLICATION_RPC(", "Client ID");
|
||||
|
||||
async function lookupAsset(applicationId: string, key: string): Promise<string> {
|
||||
return (await ApplicationAssetUtils.fetchAssetIds(applicationId, [key]))[0];
|
||||
|
|
|
@ -17,15 +17,15 @@
|
|||
*/
|
||||
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { findByPropsLazy, findComponentByCodeLazy, findStoreLazy } from "@webpack";
|
||||
import { findByProps, findComponentByCode, findStore } from "@webpack";
|
||||
import { useStateFromStores } from "@webpack/common";
|
||||
import type { CSSProperties } from "react";
|
||||
|
||||
import { ExpandedGuildFolderStore, settings } from ".";
|
||||
|
||||
const ChannelRTCStore = findStoreLazy("ChannelRTCStore");
|
||||
const Animations = findByPropsLazy("a", "animated", "useTransition");
|
||||
const GuildsBar = findComponentByCodeLazy('("guildsnav")');
|
||||
const ChannelRTCStore = findStore("ChannelRTCStore");
|
||||
const Animations = findByProps("a", "animated", "useTransition");
|
||||
const GuildsBar = findComponentByCode('("guildsnav")');
|
||||
|
||||
export default ErrorBoundary.wrap(guildsBarProps => {
|
||||
const expandedFolders = useStateFromStores([ExpandedGuildFolderStore], () => ExpandedGuildFolderStore.getExpandedFolders());
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
import { definePluginSettings } from "@api/Settings";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findByPropsLazy, findLazy, findStoreLazy } from "@webpack";
|
||||
import { find, findByProps, findStore } from "@webpack";
|
||||
import { FluxDispatcher, i18n, useMemo } from "@webpack/common";
|
||||
|
||||
import FolderSideBar from "./FolderSideBar";
|
||||
|
@ -30,10 +30,10 @@ enum FolderIconDisplay {
|
|||
MoreThanOneFolderExpanded
|
||||
}
|
||||
|
||||
const GuildsTree = findLazy(m => m.prototype?.moveNextTo);
|
||||
const SortedGuildStore = findStoreLazy("SortedGuildStore");
|
||||
export const ExpandedGuildFolderStore = findStoreLazy("ExpandedGuildFolderStore");
|
||||
const FolderUtils = findByPropsLazy("move", "toggleGuildFolderExpand");
|
||||
const GuildsTree = find(m => m.prototype?.moveNextTo);
|
||||
const SortedGuildStore = findStore("SortedGuildStore");
|
||||
export const ExpandedGuildFolderStore = findStore("ExpandedGuildFolderStore");
|
||||
const FolderUtils = findByProps("move", "toggleGuildFolderExpand");
|
||||
|
||||
let lastGuildId = null as string | null;
|
||||
let dispatchingFoldersClose = false;
|
||||
|
@ -132,8 +132,8 @@ export default definePlugin({
|
|||
},
|
||||
// Export the isBetterFolders variable to the folders component
|
||||
{
|
||||
match: /switch\(\i\.type\){case \i\.\i\.FOLDER:.+?folderNode:\i,/,
|
||||
replace: '$&isBetterFolders:typeof isBetterFolders!=="undefined"?isBetterFolders:false,'
|
||||
match: /(?<=\.Messages\.SERVERS.+?switch\((\i)\.type\){case \i\.\i\.FOLDER:.+?folderNode:\i,)/,
|
||||
replace: 'isBetterFolders:typeof isBetterFolders!=="undefined"?isBetterFolders:false,'
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { definePluginSettings, Settings } from "@api/Settings";
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { Devs } from "@utils/constants";
|
||||
import { canonicalizeMatch } from "@utils/patches";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
|
@ -31,7 +31,7 @@ const settings = definePluginSettings({
|
|||
noSpellCheck: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Disable spellcheck in notes",
|
||||
disabled: () => Settings.plugins.BetterNotesBox.hide,
|
||||
disabled: () => settings.store.hide,
|
||||
default: false
|
||||
}
|
||||
});
|
||||
|
|
|
@ -10,12 +10,12 @@ import { ImageIcon } from "@components/Icons";
|
|||
import { Devs } from "@utils/constants";
|
||||
import { getCurrentGuild, openImageModal } from "@utils/discord";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
import { findByProps } from "@webpack";
|
||||
import { Clipboard, GuildStore, Menu, PermissionStore } from "@webpack/common";
|
||||
|
||||
const GuildSettingsActions = findByPropsLazy("open", "selectRole", "updateGuild");
|
||||
const GuildSettingsActions = findByProps("open", "selectRole", "updateGuild");
|
||||
|
||||
const DeveloperMode = getUserSettingLazy("appearance", "developerMode")!;
|
||||
const DeveloperMode = getUserSettingLazy("appearance", "developerMode");
|
||||
|
||||
function PencilIcon() {
|
||||
return (
|
||||
|
|
|
@ -16,16 +16,32 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Settings } from "@api/Settings";
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { Clipboard, Toasts } from "@webpack/common";
|
||||
|
||||
const settings = definePluginSettings({
|
||||
bothStyles: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Show both role dot and coloured names",
|
||||
restartNeeded: true,
|
||||
default: false,
|
||||
},
|
||||
copyRoleColorInProfilePopout: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Allow click on role dot in profile popout to copy role color",
|
||||
restartNeeded: true,
|
||||
default: false
|
||||
}
|
||||
});
|
||||
|
||||
export default definePlugin({
|
||||
name: "BetterRoleDot",
|
||||
authors: [Devs.Ven, Devs.AutumnVN],
|
||||
description:
|
||||
"Copy role colour on RoleDot (accessibility setting) click. Also allows using both RoleDot and coloured names simultaneously",
|
||||
settings,
|
||||
|
||||
patches: [
|
||||
{
|
||||
|
@ -39,7 +55,7 @@ export default definePlugin({
|
|||
find: '"dot"===',
|
||||
all: true,
|
||||
noWarn: true,
|
||||
predicate: () => Settings.plugins.BetterRoleDot.bothStyles,
|
||||
predicate: () => settings.store.bothStyles,
|
||||
replacement: {
|
||||
match: /"(?:username|dot)"===\i(?!\.\i)/g,
|
||||
replace: "true",
|
||||
|
@ -49,7 +65,7 @@ export default definePlugin({
|
|||
{
|
||||
find: ".ADD_ROLE_A11Y_LABEL",
|
||||
all: true,
|
||||
predicate: () => Settings.plugins.BetterRoleDot.copyRoleColorInProfilePopout && !Settings.plugins.BetterRoleDot.bothStyles,
|
||||
predicate: () => settings.store.copyRoleColorInProfilePopout && !settings.store.bothStyles,
|
||||
noWarn: true,
|
||||
replacement: {
|
||||
match: /"dot"===\i/,
|
||||
|
@ -59,7 +75,7 @@ export default definePlugin({
|
|||
{
|
||||
find: ".roleVerifiedIcon",
|
||||
all: true,
|
||||
predicate: () => Settings.plugins.BetterRoleDot.copyRoleColorInProfilePopout && !Settings.plugins.BetterRoleDot.bothStyles,
|
||||
predicate: () => settings.store.copyRoleColorInProfilePopout && !settings.store.bothStyles,
|
||||
noWarn: true,
|
||||
replacement: {
|
||||
match: /"dot"===\i/,
|
||||
|
@ -68,21 +84,6 @@ export default definePlugin({
|
|||
}
|
||||
],
|
||||
|
||||
options: {
|
||||
bothStyles: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Show both role dot and coloured names",
|
||||
restartNeeded: true,
|
||||
default: false,
|
||||
},
|
||||
copyRoleColorInProfilePopout: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Allow click on role dot in profile popout to copy role color",
|
||||
restartNeeded: true,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
|
||||
copyToClipBoard(color: string) {
|
||||
Clipboard.copy(color);
|
||||
Toasts.show({
|
||||
|
|
|
@ -21,20 +21,20 @@ import { definePluginSettings } from "@api/Settings";
|
|||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findByPropsLazy, findExportedComponentLazy, findStoreLazy } from "@webpack";
|
||||
import { findByProps, findExportedComponent, findStore } from "@webpack";
|
||||
import { Constants, React, RestAPI, Tooltip } from "@webpack/common";
|
||||
|
||||
import { RenameButton } from "./components/RenameButton";
|
||||
import { Session, SessionInfo } from "./types";
|
||||
import { fetchNamesFromDataStore, getDefaultName, GetOsColor, GetPlatformIcon, savedSessionsCache, saveSessionsToDataStore } from "./utils";
|
||||
|
||||
const AuthSessionsStore = findStoreLazy("AuthSessionsStore");
|
||||
const UserSettingsModal = findByPropsLazy("saveAccountChanges", "open");
|
||||
const AuthSessionsStore = findStore("AuthSessionsStore");
|
||||
const UserSettingsModal = findByProps("saveAccountChanges", "open");
|
||||
|
||||
const TimestampClasses = findByPropsLazy("timestampTooltip", "blockquoteContainer");
|
||||
const SessionIconClasses = findByPropsLazy("sessionIcon");
|
||||
const TimestampClasses = findByProps("timestampTooltip", "blockquoteContainer");
|
||||
const SessionIconClasses = findByProps("sessionIcon");
|
||||
|
||||
const BlobMask = findExportedComponentLazy("BlobMask");
|
||||
const BlobMask = findExportedComponent("BlobMask");
|
||||
|
||||
const settings = definePluginSettings({
|
||||
backgroundCheck: {
|
||||
|
|
|
@ -8,16 +8,17 @@ import { definePluginSettings } from "@api/Settings";
|
|||
import { classNameFactory } from "@api/Styles";
|
||||
import { Devs } from "@utils/constants";
|
||||
import { Logger } from "@utils/Logger";
|
||||
import { SYM_PROXY_INNER_VALUE } from "@utils/proxyInner";
|
||||
import { NoopComponent } from "@utils/react";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { waitFor } from "@webpack";
|
||||
import { findByProps } from "@webpack";
|
||||
import { ComponentDispatch, FocusLock, i18n, Menu, useEffect, useRef } from "@webpack/common";
|
||||
import type { HTMLAttributes, ReactElement } from "react";
|
||||
|
||||
type SettingsEntry = { section: string, label: string; };
|
||||
|
||||
const cl = classNameFactory("");
|
||||
let Classes: Record<string, string>;
|
||||
waitFor(["animating", "baseLayer", "bg", "layer", "layers"], m => Classes = m);
|
||||
const Classes = findByProps("animating", "baseLayer", "bg", "layer", "layers");
|
||||
|
||||
const settings = definePluginSettings({
|
||||
disableFade: {
|
||||
|
@ -132,7 +133,7 @@ export default definePlugin({
|
|||
// try catch will only catch errors in the Layer function (hence why it's called as a plain function rather than a component), but
|
||||
// not in children
|
||||
Layer(props: LayerProps) {
|
||||
if (!FocusLock || !ComponentDispatch || !Classes) {
|
||||
if (FocusLock === NoopComponent || ComponentDispatch[SYM_PROXY_INNER_VALUE] == null || Classes[SYM_PROXY_INNER_VALUE] == null) {
|
||||
new Logger("BetterSettings").error("Failed to find some components");
|
||||
return props.children;
|
||||
}
|
||||
|
|
|
@ -16,9 +16,9 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { findStoreLazy } from "@webpack";
|
||||
import { findStore } from "@webpack";
|
||||
|
||||
import * as t from "./types/stores";
|
||||
|
||||
export const ApplicationStreamPreviewStore: t.ApplicationStreamPreviewStore = findStoreLazy("ApplicationStreamPreviewStore");
|
||||
export const ApplicationStreamingStore: t.ApplicationStreamingStore = findStoreLazy("ApplicationStreamingStore");
|
||||
export const ApplicationStreamPreviewStore: t.ApplicationStreamPreviewStore = findStore("ApplicationStreamPreviewStore");
|
||||
export const ApplicationStreamingStore: t.ApplicationStreamingStore = findStore("ApplicationStreamingStore");
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Settings } from "@api/Settings";
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
|
||||
|
@ -26,7 +26,7 @@ function setCss() {
|
|||
style.textContent = `
|
||||
.vc-nsfw-img [class^=imageWrapper] img,
|
||||
.vc-nsfw-img [class^=wrapperPaused] video {
|
||||
filter: blur(${Settings.plugins.BlurNSFW.blurAmount}px);
|
||||
filter: blur(${settings.store.blurAmount}px);
|
||||
transition: filter 0.2s;
|
||||
}
|
||||
.vc-nsfw-img [class^=imageWrapper]:hover img,
|
||||
|
@ -36,10 +36,20 @@ function setCss() {
|
|||
`;
|
||||
}
|
||||
|
||||
const settings = definePluginSettings({
|
||||
blurAmount: {
|
||||
type: OptionType.NUMBER,
|
||||
description: "Blur Amount",
|
||||
default: 10,
|
||||
onChange: setCss
|
||||
}
|
||||
});
|
||||
|
||||
export default definePlugin({
|
||||
name: "BlurNSFW",
|
||||
description: "Blur attachments in NSFW channels until hovered",
|
||||
authors: [Devs.Ven],
|
||||
settings,
|
||||
|
||||
patches: [
|
||||
{
|
||||
|
@ -51,15 +61,6 @@ export default definePlugin({
|
|||
}
|
||||
],
|
||||
|
||||
options: {
|
||||
blurAmount: {
|
||||
type: OptionType.NUMBER,
|
||||
description: "Blur Amount",
|
||||
default: 10,
|
||||
onChange: setCss
|
||||
}
|
||||
},
|
||||
|
||||
start() {
|
||||
style = document.createElement("style");
|
||||
style.id = "VcBlurNsfw";
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Settings } from "@api/Settings";
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Devs } from "@utils/constants";
|
||||
import { useTimer } from "@utils/react";
|
||||
|
@ -25,7 +25,7 @@ import { React } from "@webpack/common";
|
|||
|
||||
function formatDuration(ms: number) {
|
||||
// here be dragons (moment fucking sucks)
|
||||
const human = Settings.plugins.CallTimer.format === "human";
|
||||
const human = settings.store.format === "human";
|
||||
|
||||
const format = (n: number) => human ? n : n.toString().padStart(2, "0");
|
||||
const unit = (s: string) => human ? s : "";
|
||||
|
@ -46,15 +46,7 @@ function formatDuration(ms: number) {
|
|||
return res;
|
||||
}
|
||||
|
||||
export default definePlugin({
|
||||
name: "CallTimer",
|
||||
description: "Adds a timer to vcs",
|
||||
authors: [Devs.Ven],
|
||||
|
||||
startTime: 0,
|
||||
interval: void 0 as NodeJS.Timeout | undefined,
|
||||
|
||||
options: {
|
||||
const settings = definePluginSettings({
|
||||
format: {
|
||||
type: OptionType.SELECT,
|
||||
description: "The timer format. This can be any valid moment.js format",
|
||||
|
@ -70,7 +62,16 @@ export default definePlugin({
|
|||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
export default definePlugin({
|
||||
name: "CallTimer",
|
||||
description: "Adds a timer to vcs",
|
||||
authors: [Devs.Ven],
|
||||
settings,
|
||||
|
||||
startTime: 0,
|
||||
interval: void 0 as NodeJS.Timeout | undefined,
|
||||
|
||||
patches: [{
|
||||
find: "renderConnectionStatus(){",
|
||||
|
|
|
@ -11,10 +11,10 @@ import { Devs } from "@utils/constants";
|
|||
import { Margins } from "@utils/margins";
|
||||
import { classes } from "@utils/misc";
|
||||
import definePlugin, { OptionType, StartAt } from "@utils/types";
|
||||
import { findByCodeLazy, findComponentByCodeLazy, findStoreLazy } from "@webpack";
|
||||
import { Button, Forms, ThemeStore, useStateFromStores } from "@webpack/common";
|
||||
import { findByCode, findComponentByCode, findStore } from "@webpack";
|
||||
import { Button, Forms, useStateFromStores } from "@webpack/common";
|
||||
|
||||
const ColorPicker = findComponentByCodeLazy(".Messages.USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR", ".BACKGROUND_PRIMARY)");
|
||||
const ColorPicker = findComponentByCode(".Messages.USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR", ".BACKGROUND_PRIMARY)");
|
||||
|
||||
const colorPresets = [
|
||||
"#1E1514", "#172019", "#13171B", "#1C1C28", "#402D2D",
|
||||
|
@ -30,20 +30,21 @@ function onPickColor(color: number) {
|
|||
updateColorVars(hexColor);
|
||||
}
|
||||
|
||||
const saveClientTheme = findByCodeLazy('type:"UNSYNCED_USER_SETTINGS_UPDATE', '"system"===');
|
||||
const saveClientTheme = findByCode('type:"UNSYNCED_USER_SETTINGS_UPDATE', '"system"===');
|
||||
|
||||
function setTheme(theme: string) {
|
||||
saveClientTheme({ theme });
|
||||
}
|
||||
|
||||
const NitroThemeStore = findStoreLazy("ClientThemesBackgroundStore");
|
||||
const ThemeStore = findStore("ThemeStore");
|
||||
const ClientThemesBackgroundStore = findStore("ClientThemesBackgroundStore");
|
||||
|
||||
function ThemeSettings() {
|
||||
const theme = useStateFromStores([ThemeStore], () => ThemeStore.theme);
|
||||
const isLightTheme = theme === "light";
|
||||
const oppositeTheme = isLightTheme ? "dark" : "light";
|
||||
|
||||
const nitroTheme = useStateFromStores([NitroThemeStore], () => NitroThemeStore.gradientPreset);
|
||||
const nitroTheme = useStateFromStores([ClientThemesBackgroundStore], () => ClientThemesBackgroundStore.gradientPreset);
|
||||
const nitroThemeEnabled = nitroTheme !== undefined;
|
||||
|
||||
const selectedLuminance = relativeLuminance(settings.store.color);
|
||||
|
|
|
@ -60,6 +60,13 @@ export default definePlugin({
|
|||
replace: ""
|
||||
}
|
||||
},
|
||||
{
|
||||
find: "notosans-400-normalitalic",
|
||||
replacement: {
|
||||
match: /,"notosans-.+?"/g,
|
||||
replace: ""
|
||||
}
|
||||
},
|
||||
{
|
||||
find: 'console.warn("[DEPRECATED] Please use `subscribeWithSelector` middleware");',
|
||||
all: true,
|
||||
|
|
|
@ -19,11 +19,13 @@
|
|||
import { Devs } from "@utils/constants";
|
||||
import { getCurrentChannel, getCurrentGuild } from "@utils/discord";
|
||||
import { SYM_LAZY_CACHED, SYM_LAZY_GET } from "@utils/lazy";
|
||||
import { SYM_LAZY_COMPONENT_INNER } from "@utils/lazyReact";
|
||||
import { relaunch } from "@utils/native";
|
||||
import { canonicalizeMatch, canonicalizeReplace, canonicalizeReplacement } from "@utils/patches";
|
||||
import { SYM_PROXY_INNER_GET, SYM_PROXY_INNER_VALUE } from "@utils/proxyInner";
|
||||
import definePlugin, { PluginNative, StartAt } from "@utils/types";
|
||||
import * as Webpack from "@webpack";
|
||||
import { extract, filters, findAll, findModuleId, search } from "@webpack";
|
||||
import { cacheFindAll, cacheFindModuleId, extract, filters, searchFactories } from "@webpack";
|
||||
import * as Common from "@webpack/common";
|
||||
import { loadLazyChunks } from "debug/loadLazyChunks";
|
||||
import type { ComponentType } from "react";
|
||||
|
@ -32,27 +34,29 @@ const DESKTOP_ONLY = (f: string) => () => {
|
|||
throw new Error(`'${f}' is Discord Desktop only.`);
|
||||
};
|
||||
|
||||
const define: typeof Object.defineProperty =
|
||||
(obj, prop, desc) => {
|
||||
if (Object.hasOwn(desc, "value"))
|
||||
desc.writable = true;
|
||||
|
||||
return Object.defineProperty(obj, prop, {
|
||||
type Define = typeof Object.defineProperty;
|
||||
const define: Define = (target, p, attributes) => {
|
||||
if (Object.hasOwn(attributes, "value")) {
|
||||
attributes.writable = true;
|
||||
}
|
||||
|
||||
return Object.defineProperty(target, p, {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
...desc
|
||||
...attributes
|
||||
});
|
||||
};
|
||||
|
||||
function makeShortcuts() {
|
||||
function newFindWrapper(filterFactory: (...props: any[]) => Webpack.FilterFn) {
|
||||
function newFindWrapper(filterFactory: (...props: any[]) => Webpack.FilterFn, shouldReturnFactory = false) {
|
||||
const cache = new Map<string, unknown>();
|
||||
|
||||
return function (...filterProps: unknown[]) {
|
||||
const cacheKey = String(filterProps);
|
||||
if (cache.has(cacheKey)) return cache.get(cacheKey);
|
||||
|
||||
const matches = findAll(filterFactory(...filterProps));
|
||||
const matches = cacheFindAll(filterFactory(...filterProps), shouldReturnFactory);
|
||||
|
||||
const result = (() => {
|
||||
switch (matches.length) {
|
||||
|
@ -60,50 +64,71 @@ function makeShortcuts() {
|
|||
case 1: return matches[0];
|
||||
default:
|
||||
const uniqueMatches = [...new Set(matches)];
|
||||
if (uniqueMatches.length > 1)
|
||||
if (uniqueMatches.length > 1) {
|
||||
console.warn(`Warning: This filter matches ${matches.length} modules. Make it more specific!\n`, uniqueMatches);
|
||||
}
|
||||
|
||||
return matches[0];
|
||||
return uniqueMatches[0];
|
||||
}
|
||||
})();
|
||||
|
||||
if (result && cacheKey) cache.set(cacheKey, result);
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
let fakeRenderWin: WeakRef<Window> | undefined;
|
||||
|
||||
const find = newFindWrapper(f => f);
|
||||
const findByProps = newFindWrapper(filters.byProps);
|
||||
|
||||
return {
|
||||
...Object.fromEntries(Object.keys(Common).map(key => [key, { getter: () => Common[key] }])),
|
||||
wp: Webpack,
|
||||
wpc: { getter: () => Webpack.cache },
|
||||
wreq: { getter: () => Webpack.wreq },
|
||||
wpsearch: search,
|
||||
wpex: extract,
|
||||
wpexs: (code: string) => extract(findModuleId(code)!),
|
||||
|
||||
WebpackInstances: { getter: () => Vencord.WebpackPatcher.allWebpackInstances },
|
||||
loadLazyChunks: IS_DEV ? loadLazyChunks : () => { throw new Error("loadLazyChunks is dev only."); },
|
||||
|
||||
wpsearch: searchFactories,
|
||||
wpex: extract,
|
||||
wpexs: (...code: Webpack.CodeFilter) => extract(cacheFindModuleId(...code)!),
|
||||
|
||||
filters,
|
||||
find,
|
||||
findAll: findAll,
|
||||
findByProps,
|
||||
findAllByProps: (...props: string[]) => findAll(filters.byProps(...props)),
|
||||
findByCode: newFindWrapper(filters.byCode),
|
||||
findAllByCode: (code: string) => findAll(filters.byCode(code)),
|
||||
findAll: cacheFindAll,
|
||||
findComponent: find,
|
||||
findAllComponents: cacheFindAll,
|
||||
findExportedComponent: (...props: Webpack.PropsFilter) => findByProps(...props)[props[0]],
|
||||
findComponentByCode: newFindWrapper(filters.componentByCode),
|
||||
findAllComponentsByCode: (...code: string[]) => findAll(filters.componentByCode(...code)),
|
||||
findExportedComponent: (...props: string[]) => findByProps(...props)[props[0]],
|
||||
findAllComponentsByCode: (...code: Webpack.PropsFilter) => cacheFindAll(filters.componentByCode(...code)),
|
||||
findComponentByFields: newFindWrapper(filters.componentByFields),
|
||||
findAllComponentsByFields: (...fields: Webpack.PropsFilter) => cacheFindAll(filters.componentByFields(...fields)),
|
||||
findByProps,
|
||||
findAllByProps: (...props: Webpack.PropsFilter) => cacheFindAll(filters.byProps(...props)),
|
||||
findProp: (...props: Webpack.PropsFilter) => findByProps(...props)[props[0]],
|
||||
findByCode: newFindWrapper(filters.byCode),
|
||||
findAllByCode: (code: Webpack.CodeFilter) => cacheFindAll(filters.byCode(...code)),
|
||||
findStore: newFindWrapper(filters.byStoreName),
|
||||
PluginsApi: { getter: () => Vencord.Plugins },
|
||||
findByFactoryCode: newFindWrapper(filters.byFactoryCode),
|
||||
findAllByFactoryCode: (...code: Webpack.CodeFilter) => cacheFindAll(filters.byFactoryCode(...code)),
|
||||
findModuleFactory: newFindWrapper(filters.byFactoryCode, true),
|
||||
findAllModuleFactories: (...code: Webpack.CodeFilter) => cacheFindAll(filters.byFactoryCode(...code), true),
|
||||
|
||||
plugins: { getter: () => Vencord.Plugins.plugins },
|
||||
PluginsApi: { getter: () => Vencord.Plugins },
|
||||
Settings: { getter: () => Vencord.Settings },
|
||||
Api: { getter: () => Vencord.Api },
|
||||
Util: { getter: () => Vencord.Util },
|
||||
|
||||
reload: () => location.reload(),
|
||||
restart: IS_WEB ? DESKTOP_ONLY("restart") : relaunch,
|
||||
|
||||
canonicalizeMatch,
|
||||
canonicalizeReplace,
|
||||
canonicalizeReplacement,
|
||||
|
||||
preEnable: (plugin: string) => (Vencord.Settings.plugins[plugin] ??= { enabled: true }).enabled = true,
|
||||
fakeRender: (component: ComponentType, props: any) => {
|
||||
const prevWin = fakeRenderWin?.deref();
|
||||
const win = prevWin?.closed === false
|
||||
|
@ -133,8 +158,6 @@ function makeShortcuts() {
|
|||
Common.ReactDOM.render(Common.React.createElement(component, props), doc.body.appendChild(document.createElement("div")));
|
||||
},
|
||||
|
||||
preEnable: (plugin: string) => (Vencord.Settings.plugins[plugin] ??= { enabled: true }).enabled = true,
|
||||
|
||||
channel: { getter: () => getCurrentChannel(), preload: false },
|
||||
channelId: { getter: () => Common.SelectedChannelStore.getChannelId(), preload: false },
|
||||
guild: { getter: () => getCurrentGuild(), preload: false },
|
||||
|
@ -143,6 +166,7 @@ function makeShortcuts() {
|
|||
meId: { getter: () => Common.UserStore.getCurrentUser().id, preload: false },
|
||||
messages: { getter: () => Common.MessageStore.getMessages(Common.SelectedChannelStore.getChannelId()), preload: false },
|
||||
|
||||
...Object.fromEntries(Object.keys(Common).map(key => [key, { getter: () => Common[key] }])),
|
||||
Stores: {
|
||||
getter: () => Object.fromEntries(
|
||||
Common.Flux.Store.getAll()
|
||||
|
@ -157,11 +181,39 @@ function loadAndCacheShortcut(key: string, val: any, forceLoad: boolean) {
|
|||
const currentVal = val.getter();
|
||||
if (!currentVal || val.preload === false) return currentVal;
|
||||
|
||||
const value = currentVal[SYM_LAZY_GET]
|
||||
? forceLoad ? currentVal[SYM_LAZY_GET]() : currentVal[SYM_LAZY_CACHED]
|
||||
: currentVal;
|
||||
function unwrapProxy(value: any) {
|
||||
if (value[SYM_LAZY_GET]) {
|
||||
return forceLoad ? value[SYM_LAZY_GET]() : value[SYM_LAZY_CACHED];
|
||||
} else if (value[SYM_PROXY_INNER_GET]) {
|
||||
return forceLoad ? value[SYM_PROXY_INNER_GET]() : value[SYM_PROXY_INNER_VALUE];
|
||||
} else if (value[SYM_LAZY_COMPONENT_INNER]) {
|
||||
return value[SYM_LAZY_COMPONENT_INNER]() != null ? value[SYM_LAZY_COMPONENT_INNER]() : value;
|
||||
}
|
||||
|
||||
if (value) define(window.shortcutList, key, { value });
|
||||
return value;
|
||||
}
|
||||
|
||||
const value = unwrapProxy(currentVal);
|
||||
if (value != null && typeof value === "object") {
|
||||
const descriptors = Object.getOwnPropertyDescriptors(value);
|
||||
|
||||
for (const propKey in descriptors) {
|
||||
if (value[propKey] == null) continue;
|
||||
|
||||
const descriptor = descriptors[propKey];
|
||||
if (descriptor.writable === true || descriptor.set != null) {
|
||||
const currentValue = value[propKey];
|
||||
const newValue = unwrapProxy(currentValue);
|
||||
if (newValue != null && currentValue !== newValue) {
|
||||
value[propKey] = newValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (value != null) {
|
||||
define(window.shortcutList, key, { value });
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
@ -176,8 +228,10 @@ export default definePlugin({
|
|||
const shortcuts = makeShortcuts();
|
||||
window.shortcutList = {};
|
||||
|
||||
for (const [key, val] of Object.entries(shortcuts)) {
|
||||
if ("getter" in val) {
|
||||
for (const key in shortcuts) {
|
||||
const val = shortcuts[key];
|
||||
|
||||
if (Object.hasOwn(val, "getter")) {
|
||||
define(window.shortcutList, key, {
|
||||
get: () => loadAndCacheShortcut(key, val, true)
|
||||
});
|
||||
|
@ -191,8 +245,8 @@ export default definePlugin({
|
|||
}
|
||||
}
|
||||
|
||||
// unproxy loaded modules
|
||||
Webpack.onceReady.then(() => {
|
||||
// Unproxy loaded modules
|
||||
Webpack.onceDiscordLoaded.then(() => {
|
||||
setTimeout(() => this.eagerLoad(false), 1000);
|
||||
|
||||
if (!IS_WEB) {
|
||||
|
@ -203,13 +257,13 @@ export default definePlugin({
|
|||
},
|
||||
|
||||
async eagerLoad(forceLoad: boolean) {
|
||||
await Webpack.onceReady;
|
||||
await Webpack.onceDiscordLoaded;
|
||||
|
||||
const shortcuts = makeShortcuts();
|
||||
for (const key in shortcuts) {
|
||||
const val = shortcuts[key];
|
||||
|
||||
for (const [key, val] of Object.entries(shortcuts)) {
|
||||
if (!Object.hasOwn(val, "getter") || (val as any).preload === false) continue;
|
||||
|
||||
if (!Object.hasOwn(val, "getter") || val.preload === false) continue;
|
||||
try {
|
||||
loadAndCacheShortcut(key, val, forceLoad);
|
||||
} catch { } // swallow not found errors in DEV
|
||||
|
|
|
@ -8,10 +8,10 @@ import { definePluginSettings } from "@api/Settings";
|
|||
import { Devs } from "@utils/constants";
|
||||
import { copyWithToast } from "@utils/misc";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
import { findProp } from "@webpack";
|
||||
import { Menu } from "@webpack/common";
|
||||
|
||||
const { convertNameToSurrogate } = findByPropsLazy("convertNameToSurrogate");
|
||||
const convertNameToSurrogate = findProp("convertNameToSurrogate");
|
||||
|
||||
interface Emoji {
|
||||
type: string;
|
||||
|
@ -55,7 +55,7 @@ export default definePlugin({
|
|||
settings,
|
||||
|
||||
contextMenus: {
|
||||
"expression-picker"(children, { target }: { target: Target }) {
|
||||
"expression-picker"(children, { target }: { target: Target; }) {
|
||||
if (target.dataset.type !== "emoji") return;
|
||||
|
||||
children.push(
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
# CopyFileContents
|
||||
|
||||
Adds a button to text file attachments to copy their contents.
|
||||
|
||||
![](https://github.com/user-attachments/assets/b1a0f6f4-106f-4953-94d9-4c5ef5810bca)
|
|
@ -1,60 +0,0 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2024 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import "./style.css";
|
||||
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { CopyIcon, NoEntrySignIcon } from "@components/Icons";
|
||||
import { Devs } from "@utils/constants";
|
||||
import { copyWithToast } from "@utils/misc";
|
||||
import definePlugin from "@utils/types";
|
||||
import { Tooltip, useState } from "@webpack/common";
|
||||
|
||||
const CheckMarkIcon = () => {
|
||||
return <svg width="24" height="24" viewBox="0 0 24 24">
|
||||
<path fill="currentColor" d="M21.7 5.3a1 1 0 0 1 0 1.4l-12 12a1 1 0 0 1-1.4 0l-6-6a1 1 0 1 1 1.4-1.4L9 16.58l11.3-11.3a1 1 0 0 1 1.4 0Z"></path>
|
||||
</svg>;
|
||||
};
|
||||
|
||||
export default definePlugin({
|
||||
name: "CopyFileContents",
|
||||
description: "Adds a button to text file attachments to copy their contents",
|
||||
authors: [Devs.Obsidian, Devs.Nuckyz],
|
||||
patches: [
|
||||
{
|
||||
find: ".Messages.PREVIEW_BYTES_LEFT.format(",
|
||||
replacement: {
|
||||
match: /\.footerGap.+?url:\i,fileName:\i,fileSize:\i}\),(?<=fileContents:(\i),bytesLeft:(\i).+?)/g,
|
||||
replace: "$&$self.addCopyButton({fileContents:$1,bytesLeft:$2}),"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
addCopyButton: ErrorBoundary.wrap(({ fileContents, bytesLeft }: { fileContents: string, bytesLeft: number; }) => {
|
||||
const [recentlyCopied, setRecentlyCopied] = useState(false);
|
||||
|
||||
return (
|
||||
<Tooltip text={recentlyCopied ? "Copied!" : bytesLeft > 0 ? "File too large to copy" : "Copy File Contents"}>
|
||||
{tooltipProps => (
|
||||
<div
|
||||
{...tooltipProps}
|
||||
className="vc-cfc-button"
|
||||
role="button"
|
||||
onClick={() => {
|
||||
if (!recentlyCopied && bytesLeft <= 0) {
|
||||
copyWithToast(fileContents);
|
||||
setRecentlyCopied(true);
|
||||
setTimeout(() => setRecentlyCopied(false), 2000);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{recentlyCopied ? <CheckMarkIcon /> : bytesLeft > 0 ? <NoEntrySignIcon color="var(--channel-icon)" /> : <CopyIcon />}
|
||||
</div>
|
||||
)}
|
||||
</Tooltip>
|
||||
);
|
||||
}, { noop: true }),
|
||||
});
|
|
@ -1,8 +0,0 @@
|
|||
.vc-cfc-button {
|
||||
color: var(--interactive-normal);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.vc-cfc-button:hover {
|
||||
color: var(--interactive-hover);
|
||||
}
|
|
@ -23,22 +23,12 @@ import { Logger } from "@utils/Logger";
|
|||
import { closeAllModals } from "@utils/modal";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { maybePromptToUpdate } from "@utils/updater";
|
||||
import { filters, findBulk, proxyLazyWebpack } from "@webpack";
|
||||
import { findByProps } from "@webpack";
|
||||
import { DraftType, ExpressionPickerStore, FluxDispatcher, NavigationRouter, SelectedChannelStore } from "@webpack/common";
|
||||
|
||||
const CrashHandlerLogger = new Logger("CrashHandler");
|
||||
|
||||
const { ModalStack, DraftManager } = proxyLazyWebpack(() => {
|
||||
const [ModalStack, DraftManager] = findBulk(
|
||||
filters.byProps("pushLazy", "popAll"),
|
||||
filters.byProps("clearDraft", "saveDraft"),
|
||||
);
|
||||
|
||||
return {
|
||||
ModalStack,
|
||||
DraftManager
|
||||
};
|
||||
});
|
||||
const ModalStack = findByProps("pushLazy", "popAll");
|
||||
const DraftManager = findByProps("clearDraft", "saveDraft");
|
||||
|
||||
const settings = definePluginSettings({
|
||||
attemptToPreventCrashes: {
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { definePluginSettings, Settings } from "@api/Settings";
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { getUserSettingLazy } from "@api/UserSettings";
|
||||
import { ErrorCard } from "@components/ErrorCard";
|
||||
import { Link } from "@components/Link";
|
||||
|
@ -26,13 +26,14 @@ import { Margins } from "@utils/margins";
|
|||
import { classes } from "@utils/misc";
|
||||
import { useAwaiter } from "@utils/react";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findByCodeLazy, findComponentByCodeLazy } from "@webpack";
|
||||
import { findByCode, findByProps, findComponentByCode } from "@webpack";
|
||||
import { ApplicationAssetUtils, Button, FluxDispatcher, Forms, GuildStore, React, SelectedChannelStore, SelectedGuildStore, UserStore } from "@webpack/common";
|
||||
|
||||
const useProfileThemeStyle = findByCodeLazy("profileThemeStyle:", "--profile-gradient-primary-color");
|
||||
const ActivityComponent = findComponentByCodeLazy("onOpenGameProfile");
|
||||
const useProfileThemeStyle = findByCode("profileThemeStyle:", "--profile-gradient-primary-color");
|
||||
const ActivityComponent = findComponentByCode("onOpenGameProfile");
|
||||
const ActivityClassName = findByProps("activity", "buttonColor");
|
||||
|
||||
const ShowCurrentGame = getUserSettingLazy<boolean>("status", "showCurrentGame")!;
|
||||
const ShowCurrentGame = getUserSettingLazy<boolean>("status", "showCurrentGame");
|
||||
|
||||
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)\//, "");
|
||||
|
@ -260,7 +261,7 @@ const settings = definePluginSettings({
|
|||
|
||||
function onChange() {
|
||||
setRpc(true);
|
||||
if (Settings.plugins.CustomRPC.enabled) setRpc();
|
||||
if (Vencord.Plugins.isPluginEnabled("CustomRPC")) setRpc();
|
||||
}
|
||||
|
||||
function isStreamLinkDisabled() {
|
||||
|
@ -435,8 +436,8 @@ export default definePlugin({
|
|||
|
||||
<Forms.FormDivider className={Margins.top8} />
|
||||
|
||||
<div style={{ width: "284px", ...profileThemeStyle, padding: 8, marginTop: 8, borderRadius: 8, background: "var(--bg-mod-faint)" }}>
|
||||
{activity[0] && <ActivityComponent activity={activity[0]} channelId={SelectedChannelStore.getChannelId()}
|
||||
<div style={{ width: "284px", ...profileThemeStyle }}>
|
||||
{activity[0] && <ActivityComponent activity={activity[0]} className={ActivityClassName.activity} channelId={SelectedChannelStore.getChannelId()}
|
||||
guild={GuildStore.getGuild(SelectedGuildStore.getLastSelectedGuildId())}
|
||||
application={{ id: settings.store.appID }}
|
||||
user={UserStore.getCurrentUser()} />}
|
||||
|
|
|
@ -46,7 +46,7 @@ const embedUrlRe = /https:\/\/www\.youtube\.com\/embed\/([a-zA-Z0-9_-]{11})/;
|
|||
async function embedDidMount(this: Component<Props>) {
|
||||
try {
|
||||
const { embed } = this.props;
|
||||
const { replaceElements, dearrowByDefault } = settings.store;
|
||||
const { replaceElements } = settings.store;
|
||||
|
||||
if (!embed || embed.dearrow || embed.provider?.name !== "YouTube" || !embed.video?.url) return;
|
||||
|
||||
|
@ -63,22 +63,18 @@ async function embedDidMount(this: Component<Props>) {
|
|||
|
||||
if (!hasTitle && !hasThumb) return;
|
||||
|
||||
|
||||
embed.dearrow = {
|
||||
enabled: dearrowByDefault
|
||||
enabled: true
|
||||
};
|
||||
|
||||
if (hasTitle && replaceElements !== ReplaceElements.ReplaceThumbnailsOnly) {
|
||||
const replacementTitle = titles[0].title.replace(/(^|\s)>(\S)/g, "$1$2");
|
||||
|
||||
embed.dearrow.oldTitle = dearrowByDefault ? embed.rawTitle : replacementTitle;
|
||||
if (dearrowByDefault) embed.rawTitle = replacementTitle;
|
||||
embed.dearrow.oldTitle = embed.rawTitle;
|
||||
embed.rawTitle = titles[0].title.replace(/(^|\s)>(\S)/g, "$1$2");
|
||||
}
|
||||
if (hasThumb && replaceElements !== ReplaceElements.ReplaceTitlesOnly) {
|
||||
const replacementProxyURL = `https://dearrow-thumb.ajay.app/api/v1/getThumbnail?videoID=${videoId}&time=${thumbnails[0].timestamp}`;
|
||||
|
||||
embed.dearrow.oldThumb = dearrowByDefault ? embed.thumbnail.proxyURL : replacementProxyURL;
|
||||
if (dearrowByDefault) embed.thumbnail.proxyURL = replacementProxyURL;
|
||||
if (hasThumb && replaceElements !== ReplaceElements.ReplaceTitlesOnly) {
|
||||
embed.dearrow.oldThumb = embed.thumbnail.proxyURL;
|
||||
embed.thumbnail.proxyURL = `https://dearrow-thumb.ajay.app/api/v1/getThumbnail?videoID=${videoId}&time=${thumbnails[0].timestamp}`;
|
||||
}
|
||||
|
||||
this.forceUpdate();
|
||||
|
@ -100,7 +96,6 @@ function DearrowButton({ component }: { component: Component<Props>; }) {
|
|||
className={"vc-dearrow-toggle-" + (embed.dearrow.enabled ? "on" : "off")}
|
||||
onClick={() => {
|
||||
const { enabled, oldThumb, oldTitle } = embed.dearrow;
|
||||
settings.store.dearrowByDefault = !enabled;
|
||||
embed.dearrow.enabled = !enabled;
|
||||
if (oldTitle) {
|
||||
embed.dearrow.oldTitle = embed.rawTitle;
|
||||
|
@ -158,12 +153,6 @@ const settings = definePluginSettings({
|
|||
{ label: "Titles", value: ReplaceElements.ReplaceTitlesOnly },
|
||||
{ label: "Thumbnails", value: ReplaceElements.ReplaceThumbnailsOnly },
|
||||
],
|
||||
},
|
||||
dearrowByDefault: {
|
||||
description: "Dearrow videos automatically",
|
||||
type: OptionType.BOOLEAN,
|
||||
default: true,
|
||||
restartNeeded: false
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
|
||||
import { Flex } from "@components/Flex";
|
||||
import { findByCodeLazy } from "@webpack";
|
||||
import { findComponentByCode } from "@webpack";
|
||||
import { Button, useEffect } from "@webpack/common";
|
||||
|
||||
import { useAuthorizationStore } from "../../lib/stores/AuthorizationStore";
|
||||
|
@ -13,7 +13,7 @@ import { useCurrentUserDecorationsStore } from "../../lib/stores/CurrentUserDeco
|
|||
import { cl } from "../";
|
||||
import { openChangeDecorationModal } from "../modals/ChangeDecorationModal";
|
||||
|
||||
const CustomizationSection = findByCodeLazy(".customizationSectionBackground");
|
||||
const CustomizationSection = findComponentByCode(".customizationSectionBackground");
|
||||
|
||||
export interface DecorSectionProps {
|
||||
hideTitle?: boolean;
|
||||
|
|
|
@ -5,13 +5,13 @@
|
|||
*/
|
||||
|
||||
import { classes } from "@utils/misc";
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
import { findByProps } from "@webpack";
|
||||
import { React } from "@webpack/common";
|
||||
|
||||
import { cl } from "../";
|
||||
import Grid, { GridProps } from "./Grid";
|
||||
|
||||
const ScrollerClasses = findByPropsLazy("managedReactiveScroller");
|
||||
const ScrollerClasses = findByProps("managedReactiveScroller");
|
||||
|
||||
type Section<SectionT, ItemT> = SectionT & {
|
||||
items: Array<ItemT>;
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { findComponentByCode, LazyComponentWebpack } from "@webpack";
|
||||
import { NoopComponent } from "@utils/react";
|
||||
import { findComponentByCode } from "@webpack";
|
||||
import { React } from "@webpack/common";
|
||||
import type { ComponentType, HTMLProps, PropsWithChildren } from "react";
|
||||
|
||||
|
@ -15,13 +16,10 @@ type DecorationGridItemComponent = ComponentType<PropsWithChildren<HTMLProps<HTM
|
|||
isSelected: boolean,
|
||||
}>;
|
||||
|
||||
export let DecorationGridItem: DecorationGridItemComponent;
|
||||
export let DecorationGridItem: DecorationGridItemComponent = NoopComponent;
|
||||
export const setDecorationGridItem = v => DecorationGridItem = v;
|
||||
|
||||
export const AvatarDecorationModalPreview = LazyComponentWebpack(() => {
|
||||
const component = findComponentByCode(".shopPreviewBanner");
|
||||
return React.memo(component);
|
||||
});
|
||||
export const AvatarDecorationModalPreview = findComponentByCode<any>(".shopPreviewBanner", component => React.memo(component));
|
||||
|
||||
type DecorationGridDecorationComponent = React.ComponentType<HTMLProps<HTMLDivElement> & {
|
||||
avatarDecoration: AvatarDecoration;
|
||||
|
@ -29,5 +27,5 @@ type DecorationGridDecorationComponent = React.ComponentType<HTMLProps<HTMLDivEl
|
|||
isSelected: boolean,
|
||||
}>;
|
||||
|
||||
export let DecorationGridDecoration: DecorationGridDecorationComponent;
|
||||
export let DecorationGridDecoration: DecorationGridDecorationComponent = NoopComponent;
|
||||
export const setDecorationGridDecoration = v => DecorationGridDecoration = v;
|
||||
|
|
|
@ -5,10 +5,10 @@
|
|||
*/
|
||||
|
||||
import { classNameFactory } from "@api/Styles";
|
||||
import { extractAndLoadChunksLazy, findByPropsLazy } from "@webpack";
|
||||
import { extractAndLoadChunksLazy, findByProps } from "@webpack";
|
||||
|
||||
export const cl = classNameFactory("vc-decor-");
|
||||
export const DecorationModalStyles = findByPropsLazy("modalFooterShopButton");
|
||||
export const DecorationModalStyles = findByProps("modalFooterShopButton");
|
||||
|
||||
export const requireAvatarDecorationModal = extractAndLoadChunksLazy([".COLLECTIBLES_SHOP_FULLSCREEN&&"]);
|
||||
export const requireCreateStickerModal = extractAndLoadChunksLazy(["stickerInspected]:"]);
|
||||
export const requireAvatarDecorationModal = extractAndLoadChunksLazy(".COLLECTIBLES_SHOP_FULLSCREEN&&");
|
||||
export const requireCreateStickerModal = extractAndLoadChunksLazy("stickerInspected]:");
|
||||
|
|
|
@ -10,7 +10,7 @@ import { openInviteModal } from "@utils/discord";
|
|||
import { Margins } from "@utils/margins";
|
||||
import { classes, copyWithToast } from "@utils/misc";
|
||||
import { closeAllModals, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
||||
import { findComponentByCodeLazy } from "@webpack";
|
||||
import { findComponentByCode } from "@webpack";
|
||||
import { Alerts, Button, FluxDispatcher, Forms, GuildStore, NavigationRouter, Parser, Text, Tooltip, useEffect, UserStore, UserUtils, useState } from "@webpack/common";
|
||||
import { User } from "discord-types/general";
|
||||
|
||||
|
@ -29,7 +29,7 @@ import SectionedGridList from "../components/SectionedGridList";
|
|||
import { openCreateDecorationModal } from "./CreateDecorationModal";
|
||||
import { openGuidelinesModal } from "./GuidelinesModal";
|
||||
|
||||
const UserSummaryItem = findComponentByCodeLazy("defaultRenderUser", "showDefaultAvatarsForNullUsers");
|
||||
const UserSummaryItem = findComponentByCode("defaultRenderUser", "showDefaultAvatarsForNullUsers");
|
||||
|
||||
function usePresets() {
|
||||
const [presets, setPresets] = useState<Preset[]>([]);
|
||||
|
|
|
@ -9,7 +9,7 @@ import { Link } from "@components/Link";
|
|||
import { openInviteModal } from "@utils/discord";
|
||||
import { Margins } from "@utils/margins";
|
||||
import { closeAllModals, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
||||
import { filters, findComponentByCodeLazy, mapMangledModuleLazy } from "@webpack";
|
||||
import { filters, findComponentByCode, mapMangledModule } from "@webpack";
|
||||
import { Button, FluxDispatcher, Forms, GuildStore, NavigationRouter, Text, TextInput, useEffect, useMemo, UserStore, useState } from "@webpack/common";
|
||||
|
||||
import { GUILD_ID, INVITE_KEY, RAW_SKU_ID } from "../../lib/constants";
|
||||
|
@ -17,11 +17,11 @@ import { useCurrentUserDecorationsStore } from "../../lib/stores/CurrentUserDeco
|
|||
import { cl, DecorationModalStyles, requireAvatarDecorationModal, requireCreateStickerModal } from "../";
|
||||
import { AvatarDecorationModalPreview } from "../components";
|
||||
|
||||
const FileUpload = findComponentByCodeLazy("fileUploadInput,");
|
||||
const FileUpload = findComponentByCode("fileUploadInput,");
|
||||
|
||||
const { HelpMessage, HelpMessageTypes } = mapMangledModuleLazy('POSITIVE=3]="POSITIVE', {
|
||||
const { HelpMessage, HelpMessageTypes } = mapMangledModule('POSITIVE=3]="POSITIVE', {
|
||||
HelpMessage: filters.componentByCode(".iconDiv,", "messageType"),
|
||||
HelpMessageTypes: filters.byProps("POSITIVE", "WARNING"),
|
||||
HelpMessage: filters.byCode(".iconDiv")
|
||||
});
|
||||
|
||||
function useObjectURL(object: Blob | MediaSource | null) {
|
||||
|
|
|
@ -22,7 +22,7 @@ import { Devs } from "@utils/constants";
|
|||
import { Logger } from "@utils/Logger";
|
||||
import { canonicalizeMatch, canonicalizeReplace } from "@utils/patches";
|
||||
import definePlugin, { OptionType, ReporterTestable } from "@utils/types";
|
||||
import { filters, findAll, search } from "@webpack";
|
||||
import { cacheFindAll, filters, searchFactories } from "@webpack";
|
||||
|
||||
const PORT = 8485;
|
||||
const NAV_ID = "dev-companion-reconnect";
|
||||
|
@ -154,13 +154,13 @@ function initWs(isManual = false) {
|
|||
case "testPatch": {
|
||||
const { find, replacement } = data as PatchData;
|
||||
|
||||
const candidates = search(find);
|
||||
const candidates = searchFactories(find);
|
||||
const keys = Object.keys(candidates);
|
||||
if (keys.length !== 1)
|
||||
return reply("Expected exactly one 'find' matches, found " + keys.length);
|
||||
|
||||
const mod = candidates[keys[0]];
|
||||
let src = String(mod.original ?? mod).replaceAll("\n", "");
|
||||
let src = String(mod).replaceAll("\n", "");
|
||||
|
||||
if (src.startsWith("function(")) {
|
||||
src = "0," + src;
|
||||
|
@ -201,22 +201,22 @@ function initWs(isManual = false) {
|
|||
let results: any[];
|
||||
switch (type.replace("find", "").replace("Lazy", "")) {
|
||||
case "":
|
||||
results = findAll(parsedArgs[0]);
|
||||
results = cacheFindAll(parsedArgs[0]);
|
||||
break;
|
||||
case "ByProps":
|
||||
results = findAll(filters.byProps(...parsedArgs));
|
||||
results = cacheFindAll(filters.byProps(...parsedArgs));
|
||||
break;
|
||||
case "Store":
|
||||
results = findAll(filters.byStoreName(parsedArgs[0]));
|
||||
results = cacheFindAll(filters.byStoreName(parsedArgs[0]));
|
||||
break;
|
||||
case "ByCode":
|
||||
results = findAll(filters.byCode(...parsedArgs));
|
||||
results = cacheFindAll(filters.byCode(...parsedArgs));
|
||||
break;
|
||||
case "ModuleId":
|
||||
results = Object.keys(search(parsedArgs[0]));
|
||||
results = Object.keys(searchFactories(parsedArgs[0]));
|
||||
break;
|
||||
case "ComponentByCode":
|
||||
results = findAll(filters.componentByCode(...parsedArgs));
|
||||
results = cacheFindAll(filters.componentByCode(...parsedArgs));
|
||||
break;
|
||||
default:
|
||||
return reply("Unknown Find Type " + type);
|
||||
|
|
|
@ -23,12 +23,12 @@ import { Logger } from "@utils/Logger";
|
|||
import { Margins } from "@utils/margins";
|
||||
import { ModalContent, ModalHeader, ModalRoot, openModalLazy } from "@utils/modal";
|
||||
import definePlugin from "@utils/types";
|
||||
import { findByCodeLazy, findStoreLazy } from "@webpack";
|
||||
import { findByCode, findStore } from "@webpack";
|
||||
import { Constants, EmojiStore, FluxDispatcher, Forms, GuildStore, Menu, PermissionsBits, PermissionStore, React, RestAPI, Toasts, Tooltip, UserStore } from "@webpack/common";
|
||||
import { Promisable } from "type-fest";
|
||||
|
||||
const StickersStore = findStoreLazy("StickersStore");
|
||||
const uploadEmoji = findByCodeLazy(".GUILD_EMOJIS(", "EMOJI_UPLOAD_START");
|
||||
const StickersStore = findStore("StickersStore");
|
||||
const uploadEmoji = findByCode(".GUILD_EMOJIS(", "EMOJI_UPLOAD_START");
|
||||
|
||||
interface Sticker {
|
||||
t: "Sticker";
|
||||
|
|
|
@ -23,12 +23,12 @@ import { ErrorCard } from "@components/ErrorCard";
|
|||
import { Devs } from "@utils/constants";
|
||||
import { Margins } from "@utils/margins";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
import { findByProps } from "@webpack";
|
||||
import { Forms, React } from "@webpack/common";
|
||||
|
||||
import hideBugReport from "./hideBugReport.css?managed";
|
||||
|
||||
const KbdStyles = findByPropsLazy("key", "combo");
|
||||
const KbdStyles = findByProps("key", "combo");
|
||||
|
||||
const settings = definePluginSettings({
|
||||
toolbarDevMenu: {
|
||||
|
|
|
@ -23,36 +23,36 @@ import { ApngBlendOp, ApngDisposeOp, importApngJs } from "@utils/dependencies";
|
|||
import { getCurrentGuild } from "@utils/discord";
|
||||
import { Logger } from "@utils/Logger";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findByCodeLazy, findByPropsLazy, findStoreLazy, proxyLazyWebpack } from "@webpack";
|
||||
import { findByCode, findByProps, findStore, webpackDependantLazy } from "@webpack";
|
||||
import { Alerts, ChannelStore, DraftType, EmojiStore, FluxDispatcher, Forms, GuildMemberStore, IconUtils, lodash, Parser, PermissionsBits, PermissionStore, UploadHandler, UserSettingsActionCreators, UserStore } from "@webpack/common";
|
||||
import type { Emoji } from "@webpack/types";
|
||||
import type { Message } from "discord-types/general";
|
||||
import { applyPalette, GIFEncoder, quantize } from "gifenc";
|
||||
import type { ReactElement, ReactNode } from "react";
|
||||
|
||||
const StickerStore = findStoreLazy("StickersStore") as {
|
||||
const StickersStore = findStore("StickersStore") as {
|
||||
getPremiumPacks(): StickerPack[];
|
||||
getAllGuildStickers(): Map<string, Sticker[]>;
|
||||
getStickerById(id: string): Sticker | undefined;
|
||||
};
|
||||
|
||||
const UserSettingsProtoStore = findStoreLazy("UserSettingsProtoStore");
|
||||
const UserSettingsProtoStore = findStore("UserSettingsProtoStore");
|
||||
|
||||
const BINARY_READ_OPTIONS = findByPropsLazy("readerFactory");
|
||||
const BINARY_READ_OPTIONS = findByProps("readerFactory");
|
||||
|
||||
function searchProtoClassField(localName: string, protoClass: any) {
|
||||
const field = protoClass?.fields?.find((field: any) => field.localName === localName);
|
||||
if (!field) return;
|
||||
|
||||
const fieldGetter = Object.values(field).find(value => typeof value === "function") as any;
|
||||
const fieldGetter = Object.values<any>(field).find(value => typeof value === "function");
|
||||
return fieldGetter?.();
|
||||
}
|
||||
|
||||
const PreloadedUserSettingsActionCreators = proxyLazyWebpack(() => UserSettingsActionCreators.PreloadedUserSettingsActionCreators);
|
||||
const AppearanceSettingsActionCreators = proxyLazyWebpack(() => searchProtoClassField("appearance", PreloadedUserSettingsActionCreators.ProtoClass));
|
||||
const ClientThemeSettingsActionsCreators = proxyLazyWebpack(() => searchProtoClassField("clientThemeSettings", AppearanceSettingsActionCreators));
|
||||
const PreloadedUserSettingsActionCreators = webpackDependantLazy(() => UserSettingsActionCreators.PreloadedUserSettingsActionCreators);
|
||||
const AppearanceSettingsActionCreators = webpackDependantLazy(() => searchProtoClassField("appearance", PreloadedUserSettingsActionCreators.ProtoClass));
|
||||
const ClientThemeSettingsActionsCreators = webpackDependantLazy(() => searchProtoClassField("clientThemeSettings", AppearanceSettingsActionCreators));
|
||||
|
||||
const isUnusableRoleSubscriptionEmoji = findByCodeLazy(".getUserIsAdmin(");
|
||||
const isUnusableRoleSubscriptionEmoji = findByCode(".getUserIsAdmin(");
|
||||
|
||||
const enum EmojiIntentions {
|
||||
REACTION,
|
||||
|
@ -566,8 +566,8 @@ export default definePlugin({
|
|||
|
||||
const gifMatch = child.props.href.match(fakeNitroGifStickerRegex);
|
||||
if (gifMatch) {
|
||||
// There is no way to differentiate a regular gif attachment from a fake nitro animated sticker, so we check if the StickerStore contains the id of the fake sticker
|
||||
if (StickerStore.getStickerById(gifMatch[1])) return null;
|
||||
// There is no way to differentiate a regular gif attachment from a fake nitro animated sticker, so we check if the StickersStore contains the id of the fake sticker
|
||||
if (StickersStore.getStickerById(gifMatch[1])) return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -655,7 +655,7 @@ export default definePlugin({
|
|||
url = new URL(item);
|
||||
} catch { }
|
||||
|
||||
const stickerName = StickerStore.getStickerById(imgMatch[1])?.name ?? url?.searchParams.get("name") ?? "FakeNitroSticker";
|
||||
const stickerName = StickersStore.getStickerById(imgMatch[1])?.name ?? url?.searchParams.get("name") ?? "FakeNitroSticker";
|
||||
stickers.push({
|
||||
format_type: 1,
|
||||
id: imgMatch[1],
|
||||
|
@ -668,9 +668,9 @@ export default definePlugin({
|
|||
|
||||
const gifMatch = item.match(fakeNitroGifStickerRegex);
|
||||
if (gifMatch) {
|
||||
if (!StickerStore.getStickerById(gifMatch[1])) continue;
|
||||
if (!StickersStore.getStickerById(gifMatch[1])) continue;
|
||||
|
||||
const stickerName = StickerStore.getStickerById(gifMatch[1])?.name ?? "FakeNitroSticker";
|
||||
const stickerName = StickersStore.getStickerById(gifMatch[1])?.name ?? "FakeNitroSticker";
|
||||
stickers.push({
|
||||
format_type: 2,
|
||||
id: gifMatch[1],
|
||||
|
@ -703,8 +703,8 @@ export default definePlugin({
|
|||
|
||||
const gifMatch = embed.url!.match(fakeNitroGifStickerRegex);
|
||||
if (gifMatch) {
|
||||
// There is no way to differentiate a regular gif attachment from a fake nitro animated sticker, so we check if the StickerStore contains the id of the fake sticker
|
||||
if (StickerStore.getStickerById(gifMatch[1])) return true;
|
||||
// There is no way to differentiate a regular gif attachment from a fake nitro animated sticker, so we check if the StickersStore contains the id of the fake sticker
|
||||
if (StickersStore.getStickerById(gifMatch[1])) return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -721,8 +721,8 @@ export default definePlugin({
|
|||
|
||||
const match = attachment.url.match(fakeNitroGifStickerRegex);
|
||||
if (match) {
|
||||
// There is no way to differentiate a regular gif attachment from a fake nitro animated sticker, so we check if the StickerStore contains the id of the fake sticker
|
||||
if (StickerStore.getStickerById(match[1])) return false;
|
||||
// There is no way to differentiate a regular gif attachment from a fake nitro animated sticker, so we check if the StickersStore contains the id of the fake sticker
|
||||
if (StickersStore.getStickerById(match[1])) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -878,7 +878,7 @@ export default definePlugin({
|
|||
if (!s.enableStickerBypass)
|
||||
break stickerBypass;
|
||||
|
||||
const sticker = StickerStore.getStickerById(extra.stickers?.[0]!);
|
||||
const sticker = StickersStore.getStickerById(extra.stickers?.[0]!);
|
||||
if (!sticker)
|
||||
break stickerBypass;
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ import { Margins } from "@utils/margins";
|
|||
import { classes, copyWithToast } from "@utils/misc";
|
||||
import { useAwaiter } from "@utils/react";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { extractAndLoadChunksLazy, findComponentByCodeLazy } from "@webpack";
|
||||
import { extractAndLoadChunksLazy, findComponentByCode } from "@webpack";
|
||||
import { Button, Flex, Forms, React, Text, UserProfileStore, UserStore, useState } from "@webpack/common";
|
||||
import { User } from "discord-types/general";
|
||||
import virtualMerge from "virtual-merge";
|
||||
|
@ -108,10 +108,10 @@ interface ProfileModalProps {
|
|||
isTryItOutFlow: boolean;
|
||||
}
|
||||
|
||||
const ColorPicker = findComponentByCodeLazy<ColorPickerProps>(".Messages.USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR", ".BACKGROUND_PRIMARY)");
|
||||
const ProfileModal = findComponentByCodeLazy<ProfileModalProps>("isTryItOutFlow:", "pendingThemeColors:", "pendingAvatarDecoration:", "EDIT_PROFILE_BANNER");
|
||||
const ColorPicker = findComponentByCode<ColorPickerProps>(".Messages.USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR", ".BACKGROUND_PRIMARY)");
|
||||
const ProfileModal = findComponentByCode<ProfileModalProps>("isTryItOutFlow:", "pendingThemeColors:", "pendingAvatarDecoration:", "EDIT_PROFILE_BANNER");
|
||||
|
||||
const requireColorPicker = extractAndLoadChunksLazy(["USER_SETTINGS_PROFILE_COLOR_DEFAULT_BUTTON.format"], /createPromise:\(\)=>\i\.\i(\("?.+?"?\)).then\(\i\.bind\(\i,"?(.+?)"?\)\)/);
|
||||
const requireColorPicker = extractAndLoadChunksLazy("USER_SETTINGS_PROFILE_COLOR_DEFAULT_BUTTON.format");
|
||||
|
||||
export default definePlugin({
|
||||
name: "FakeProfileThemes",
|
||||
|
|
|
@ -20,7 +20,7 @@ import { definePluginSettings } from "@api/Settings";
|
|||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
import { findByProps } from "@webpack";
|
||||
import { useCallback, useEffect, useRef, useState } from "@webpack/common";
|
||||
|
||||
interface SearchBarComponentProps {
|
||||
|
@ -35,7 +35,7 @@ interface SearchBarComponentProps {
|
|||
}
|
||||
|
||||
type TSearchBarComponent =
|
||||
React.FC<SearchBarComponentProps> & { Sizes: Record<"SMALL" | "MEDIUM" | "LARGE", string>; };
|
||||
React.ComponentType<SearchBarComponentProps> & { Sizes: Record<"SMALL" | "MEDIUM" | "LARGE", string>; };
|
||||
|
||||
interface Gif {
|
||||
format: number;
|
||||
|
@ -60,7 +60,7 @@ interface Instance {
|
|||
}
|
||||
|
||||
|
||||
const containerClasses: { searchBar: string; } = findByPropsLazy("searchBar", "searchBarFullRow");
|
||||
const containerClasses: { searchBar: string; } = findByProps("searchBar", "searchBarFullRow");
|
||||
|
||||
export const settings = definePluginSettings({
|
||||
searchOption: {
|
||||
|
|
|
@ -19,9 +19,9 @@
|
|||
import { ApplicationCommandInputType, sendBotMessage } from "@api/Commands";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin from "@utils/types";
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
import { findByProps } from "@webpack";
|
||||
|
||||
const FriendInvites = findByPropsLazy("createFriendInvite");
|
||||
const FriendInvites = findByProps("createFriendInvite");
|
||||
|
||||
export default definePlugin({
|
||||
name: "FriendInvites",
|
||||
|
|
|
@ -7,15 +7,16 @@
|
|||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Devs } from "@utils/constants";
|
||||
import { getCurrentChannel } from "@utils/discord";
|
||||
import { Logger } from "@utils/Logger";
|
||||
import definePlugin from "@utils/types";
|
||||
import { findByCodeLazy, findByPropsLazy, findComponentByCodeLazy } from "@webpack";
|
||||
import { RelationshipStore, Text } from "@webpack/common";
|
||||
import { find, findByCode, findByProps } from "@webpack";
|
||||
import { Heading, RelationshipStore, Text } from "@webpack/common";
|
||||
|
||||
const containerWrapper = findByPropsLazy("memberSinceWrapper");
|
||||
const container = findByPropsLazy("memberSince");
|
||||
const getCreatedAtDate = findByCodeLazy('month:"short",day:"numeric"');
|
||||
const locale = findByPropsLazy("getLocale");
|
||||
const Section = findComponentByCodeLazy('"auto":"smooth"', ".section");
|
||||
const containerWrapper = findByProps("memberSinceWrapper");
|
||||
const container = findByProps("memberSince");
|
||||
const getCreatedAtDate = findByCode('month:"short",day:"numeric"');
|
||||
const locale = findByProps("getLocale");
|
||||
const section = find((m: any) => m.section !== void 0 && m.heading !== void 0 && Object.values(m).length === 2);
|
||||
|
||||
export default definePlugin({
|
||||
name: "FriendsSince",
|
||||
|
@ -27,7 +28,7 @@ export default definePlugin({
|
|||
find: ".PANEL}),nicknameIcons",
|
||||
replacement: {
|
||||
match: /USER_PROFILE_MEMBER_SINCE,.{0,100}userId:(\i\.id)}\)}\)/,
|
||||
replace: "$&,$self.FriendsSinceComponent({userId:$1,isSidebar:true})"
|
||||
replace: "$&,$self.friendsSinceNew({userId:$1,isSidebar:true})"
|
||||
}
|
||||
},
|
||||
// User Profile Modal
|
||||
|
@ -35,19 +36,34 @@ export default definePlugin({
|
|||
find: "action:\"PRESS_APP_CONNECTION\"",
|
||||
replacement: {
|
||||
match: /USER_PROFILE_MEMBER_SINCE,.{0,100}userId:(\i\.id),.{0,100}}\)}\),/,
|
||||
replace: "$&,$self.FriendsSinceComponent({userId:$1,isSidebar:false}),"
|
||||
replace: "$&,$self.friendsSinceNew({userId:$1,isSidebar:false}),"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
FriendsSinceComponent: ErrorBoundary.wrap(({ userId, isSidebar }: { userId: string; isSidebar: boolean; }) => {
|
||||
getFriendSince(userId: string) {
|
||||
try {
|
||||
if (!RelationshipStore.isFriend(userId)) return null;
|
||||
|
||||
return RelationshipStore.getSince(userId);
|
||||
} catch (err) {
|
||||
new Logger("FriendsSince").error(err);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
friendsSinceNew: ErrorBoundary.wrap(({ userId, isSidebar }: { userId: string; isSidebar: boolean; }) => {
|
||||
if (!RelationshipStore.isFriend(userId)) return null;
|
||||
|
||||
const friendsSince = RelationshipStore.getSince(userId);
|
||||
if (!friendsSince) return null;
|
||||
|
||||
return (
|
||||
<Section heading="Friends Since">
|
||||
<section className={section.section}>
|
||||
<Heading variant="text-xs/semibold" style={isSidebar ? {} : { color: "var(--header-secondary)" }}>
|
||||
Friends Since
|
||||
</Heading>
|
||||
|
||||
{
|
||||
isSidebar ? (
|
||||
<Text variant="text-sm/normal">
|
||||
|
@ -75,7 +91,8 @@ export default definePlugin({
|
|||
</div>
|
||||
)
|
||||
}
|
||||
</Section>
|
||||
|
||||
</section>
|
||||
);
|
||||
}, { noop: true }),
|
||||
});
|
||||
|
|
|
@ -22,13 +22,13 @@ import { getUserSettingLazy } from "@api/UserSettings";
|
|||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findComponentByCodeLazy } from "@webpack";
|
||||
import { findComponentByCode } from "@webpack";
|
||||
|
||||
import style from "./style.css?managed";
|
||||
|
||||
const Button = findComponentByCodeLazy("Button.Sizes.NONE,disabled:");
|
||||
const Button = findComponentByCode("Button.Sizes.NONE,disabled:");
|
||||
|
||||
const ShowCurrentGame = getUserSettingLazy<boolean>("status", "showCurrentGame")!;
|
||||
const ShowCurrentGame = getUserSettingLazy<boolean>("status", "showCurrentGame");
|
||||
|
||||
function makeIcon(showCurrentGame?: boolean) {
|
||||
const { oldIcon } = settings.use(["oldIcon"]);
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
import { definePluginSettings } from "@api/Settings";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findLazy } from "@webpack";
|
||||
import { find } from "@webpack";
|
||||
import { ContextMenuApi, FluxDispatcher, Menu, MessageActions } from "@webpack/common";
|
||||
import { Channel, Message } from "discord-types/general";
|
||||
|
||||
|
@ -49,7 +49,7 @@ const settings = definePluginSettings({
|
|||
unholyMultiGreetEnabled?: boolean;
|
||||
}>();
|
||||
|
||||
const WELCOME_STICKERS = findLazy(m => Array.isArray(m) && m[0]?.name === "Wave");
|
||||
const WELCOME_STICKERS = find(m => Array.isArray(m) && m[0]?.name === "Wave");
|
||||
|
||||
function greet(channel: Channel, message: Message, stickers: string[]) {
|
||||
const options = MessageActions.getSendMessageOptionsForReply({
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
# IgnoreActivities
|
||||
|
||||
Ignore activities from showing up on your status ONLY. You can configure which ones are specifically ignored from the Registered Games and Activities tabs, or use the general settings.
|
||||
|
||||
![](https://github.com/user-attachments/assets/f0c19060-0ecf-4f1c-8165-a5aa40143c82)
|
||||
|
||||
![](https://github.com/user-attachments/assets/73c3fa7a-5b90-41ee-a4d6-91fa76458b74)
|
||||
|
||||
![](https://github.com/user-attachments/assets/1ab3fe73-3911-48d1-8a08-e976af614b41)
|
||||
|
||||
The activity stays showing as a detected game even if ignored, differently from the stock Toggle Detection button from Discord:
|
||||
|
||||
![](https://github.com/user-attachments/assets/08ea60c3-3a31-42de-ae4c-7535fbf1b45a)
|
|
@ -12,7 +12,7 @@ import { Flex } from "@components/Flex";
|
|||
import { Devs } from "@utils/constants";
|
||||
import { Margins } from "@utils/margins";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findStoreLazy } from "@webpack";
|
||||
import { findStore } from "@webpack";
|
||||
import { Button, Forms, showToast, TextInput, Toasts, Tooltip, useEffect, useState } from "webpack/common";
|
||||
|
||||
const enum ActivitiesTypes {
|
||||
|
@ -26,14 +26,9 @@ interface IgnoredActivity {
|
|||
type: ActivitiesTypes;
|
||||
}
|
||||
|
||||
const enum FilterMode {
|
||||
Whitelist,
|
||||
Blacklist
|
||||
}
|
||||
const RunningGameStore = findStore("RunningGameStore");
|
||||
|
||||
const RunningGameStore = findStoreLazy("RunningGameStore");
|
||||
|
||||
const ShowCurrentGame = getUserSettingLazy("status", "showCurrentGame")!;
|
||||
const ShowCurrentGame = getUserSettingLazy("status", "showCurrentGame");
|
||||
|
||||
function ToggleIcon(activity: IgnoredActivity, tooltipText: string, path: string, fill: string) {
|
||||
return (
|
||||
|
@ -75,17 +70,14 @@ function handleActivityToggle(e: React.MouseEvent<HTMLButtonElement, MouseEvent>
|
|||
if (ignoredActivityIndex === -1) settings.store.ignoredActivities = getIgnoredActivities().concat(activity);
|
||||
else settings.store.ignoredActivities = getIgnoredActivities().filter((_, index) => index !== ignoredActivityIndex);
|
||||
|
||||
recalculateActivities();
|
||||
}
|
||||
|
||||
function recalculateActivities() {
|
||||
// Trigger activities recalculation
|
||||
ShowCurrentGame.updateSetting(old => old);
|
||||
}
|
||||
|
||||
function ImportCustomRPCComponent() {
|
||||
return (
|
||||
<Flex flexDirection="column">
|
||||
<Forms.FormText type={Forms.FormText.Types.DESCRIPTION}>Import the application id of the CustomRPC plugin to the filter list</Forms.FormText>
|
||||
<Forms.FormText type={Forms.FormText.Types.DESCRIPTION}>Import the application id of the CustomRPC plugin to the allowed list</Forms.FormText>
|
||||
<div>
|
||||
<Button
|
||||
onClick={() => {
|
||||
|
@ -94,7 +86,7 @@ function ImportCustomRPCComponent() {
|
|||
return showToast("CustomRPC application ID is not set.", Toasts.Type.FAILURE);
|
||||
}
|
||||
|
||||
const isAlreadyAdded = idsListPushID?.(id);
|
||||
const isAlreadyAdded = allowedIdsPushID?.(id);
|
||||
if (isAlreadyAdded) {
|
||||
showToast("CustomRPC application ID is already added.", Toasts.Type.FAILURE);
|
||||
}
|
||||
|
@ -107,39 +99,39 @@ function ImportCustomRPCComponent() {
|
|||
);
|
||||
}
|
||||
|
||||
let idsListPushID: ((id: string) => boolean) | null = null;
|
||||
let allowedIdsPushID: ((id: string) => boolean) | null = null;
|
||||
|
||||
function IdsListComponent(props: { setValue: (value: string) => void; }) {
|
||||
const [idsList, setIdsList] = useState<string>(settings.store.idsList ?? "");
|
||||
function AllowedIdsComponent(props: { setValue: (value: string) => void; }) {
|
||||
const [allowedIds, setAllowedIds] = useState<string>(settings.store.allowedIds ?? "");
|
||||
|
||||
idsListPushID = (id: string) => {
|
||||
const currentIds = new Set(idsList.split(",").map(id => id.trim()).filter(Boolean));
|
||||
allowedIdsPushID = (id: string) => {
|
||||
const currentIds = new Set(allowedIds.split(",").map(id => id.trim()).filter(Boolean));
|
||||
|
||||
const isAlreadyAdded = currentIds.has(id) || (currentIds.add(id), false);
|
||||
|
||||
const ids = Array.from(currentIds).join(", ");
|
||||
setIdsList(ids);
|
||||
setAllowedIds(ids);
|
||||
props.setValue(ids);
|
||||
|
||||
return isAlreadyAdded;
|
||||
};
|
||||
|
||||
useEffect(() => () => {
|
||||
idsListPushID = null;
|
||||
allowedIdsPushID = null;
|
||||
}, []);
|
||||
|
||||
function handleChange(newValue: string) {
|
||||
setIdsList(newValue);
|
||||
setAllowedIds(newValue);
|
||||
props.setValue(newValue);
|
||||
}
|
||||
|
||||
return (
|
||||
<Forms.FormSection>
|
||||
<Forms.FormTitle tag="h3">Filter List</Forms.FormTitle>
|
||||
<Forms.FormText className={Margins.bottom8} type={Forms.FormText.Types.DESCRIPTION}>Comma separated list of activity IDs to filter (Useful for filtering specific RPC activities and CustomRPC</Forms.FormText>
|
||||
<Forms.FormTitle tag="h3">Allowed List</Forms.FormTitle>
|
||||
<Forms.FormText className={Margins.bottom8} type={Forms.FormText.Types.DESCRIPTION}>Comma separated list of activity IDs to allow (Useful for allowing RPC activities and CustomRPC)</Forms.FormText>
|
||||
<TextInput
|
||||
type="text"
|
||||
value={idsList}
|
||||
value={allowedIds}
|
||||
onChange={handleChange}
|
||||
placeholder="235834946571337729, 343383572805058560"
|
||||
/>
|
||||
|
@ -153,62 +145,40 @@ const settings = definePluginSettings({
|
|||
description: "",
|
||||
component: () => <ImportCustomRPCComponent />
|
||||
},
|
||||
listMode: {
|
||||
type: OptionType.SELECT,
|
||||
description: "Change the mode of the filter list",
|
||||
options: [
|
||||
{
|
||||
label: "Whitelist",
|
||||
value: FilterMode.Whitelist,
|
||||
default: true
|
||||
},
|
||||
{
|
||||
label: "Blacklist",
|
||||
value: FilterMode.Blacklist,
|
||||
}
|
||||
],
|
||||
onChange: recalculateActivities
|
||||
},
|
||||
idsList: {
|
||||
allowedIds: {
|
||||
type: OptionType.COMPONENT,
|
||||
description: "",
|
||||
default: "",
|
||||
onChange(newValue: string) {
|
||||
const ids = new Set(newValue.split(",").map(id => id.trim()).filter(Boolean));
|
||||
settings.store.idsList = Array.from(ids).join(", ");
|
||||
recalculateActivities();
|
||||
settings.store.allowedIds = Array.from(ids).join(", ");
|
||||
},
|
||||
component: props => <IdsListComponent setValue={props.setValue} />
|
||||
component: props => <AllowedIdsComponent setValue={props.setValue} />
|
||||
},
|
||||
ignorePlaying: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Ignore all playing activities (These are usually game and RPC activities)",
|
||||
default: false,
|
||||
onChange: recalculateActivities
|
||||
default: false
|
||||
},
|
||||
ignoreStreaming: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Ignore all streaming activities",
|
||||
default: false,
|
||||
onChange: recalculateActivities
|
||||
default: false
|
||||
},
|
||||
ignoreListening: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Ignore all listening activities (These are usually spotify activities)",
|
||||
default: false,
|
||||
onChange: recalculateActivities
|
||||
default: false
|
||||
},
|
||||
ignoreWatching: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Ignore all watching activities",
|
||||
default: false,
|
||||
onChange: recalculateActivities
|
||||
default: false
|
||||
},
|
||||
ignoreCompeting: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Ignore all competing activities (These are normally special game activities)",
|
||||
default: false,
|
||||
onChange: recalculateActivities
|
||||
default: false
|
||||
}
|
||||
}).withPrivateSettings<{
|
||||
ignoredActivities: IgnoredActivity[];
|
||||
|
@ -219,8 +189,8 @@ function getIgnoredActivities() {
|
|||
}
|
||||
|
||||
function isActivityTypeIgnored(type: number, id?: string) {
|
||||
if (id && settings.store.idsList.includes(id)) {
|
||||
return settings.store.listMode === FilterMode.Blacklist;
|
||||
if (id && settings.store.allowedIds.includes(id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
|
@ -236,8 +206,8 @@ function isActivityTypeIgnored(type: number, id?: string) {
|
|||
|
||||
export default definePlugin({
|
||||
name: "IgnoreActivities",
|
||||
authors: [Devs.Nuckyz, Devs.Kylie],
|
||||
description: "Ignore activities from showing up on your status ONLY. You can configure which ones are specifically ignored from the Registered Games and Activities tabs, or use the general settings below",
|
||||
authors: [Devs.Nuckyz],
|
||||
description: "Ignore activities from showing up on your status ONLY. You can configure which ones are specifically ignored from the Registered Games and Activities tabs, or use the general settings below.",
|
||||
dependencies: ["UserSettingsAPI"],
|
||||
|
||||
settings,
|
||||
|
@ -266,7 +236,6 @@ export default definePlugin({
|
|||
replace: (m, props, nowPlaying) => `${m}$self.renderToggleGameActivityButton(${props},${nowPlaying}),`
|
||||
}
|
||||
},
|
||||
// Discord has 2 different components for activities. Currently, the last is the one being used
|
||||
{
|
||||
find: ".activityTitleText,variant",
|
||||
replacement: {
|
||||
|
@ -275,21 +244,15 @@ export default definePlugin({
|
|||
},
|
||||
},
|
||||
{
|
||||
find: ".promotedLabelWrapperNonBanner,children",
|
||||
find: ".activityCardDetails,children",
|
||||
replacement: {
|
||||
match: /\.appDetailsHeaderContainer.+?children:\i.*?}\),(?<=application:(\i).+?)/,
|
||||
match: /\.activityCardDetails.+?children:(\i\.application)\.name.*?}\),/,
|
||||
replace: (m, props) => `${m}$self.renderToggleActivityButton(${props}),`
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
async start() {
|
||||
// Migrate allowedIds
|
||||
if (Settings.plugins.IgnoreActivities.allowedIds) {
|
||||
settings.store.idsList = Settings.plugins.IgnoreActivities.allowedIds;
|
||||
delete Settings.plugins.IgnoreActivities.allowedIds; // Remove allowedIds
|
||||
}
|
||||
|
||||
const oldIgnoredActivitiesData = await DataStore.get<Map<IgnoredActivity["id"], IgnoredActivity>>("IgnoreActivities_ignoredActivities");
|
||||
|
||||
if (oldIgnoredActivitiesData != null) {
|
||||
|
@ -316,7 +279,7 @@ export default definePlugin({
|
|||
if (isActivityTypeIgnored(props.type, props.application_id)) return false;
|
||||
|
||||
if (props.application_id != null) {
|
||||
return !getIgnoredActivities().some(activity => activity.id === props.application_id) || (settings.store.listMode === FilterMode.Whitelist && settings.store.idsList.includes(props.application_id));
|
||||
return !getIgnoredActivities().some(activity => activity.id === props.application_id) || settings.store.allowedIds.includes(props.application_id);
|
||||
} else {
|
||||
const exePath = RunningGameStore.getRunningGames().find(game => game.name === props.name)?.exePath;
|
||||
if (exePath) {
|
||||
|
|
|
@ -19,16 +19,26 @@
|
|||
import { definePluginSettings } from "@api/Settings";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findStoreLazy } from "@webpack";
|
||||
import { findStore } from "@webpack";
|
||||
import { ChannelStore, Constants, FluxDispatcher, GuildStore, RelationshipStore, SnowflakeUtils, UserStore } from "@webpack/common";
|
||||
import { Settings } from "Vencord";
|
||||
|
||||
const UserAffinitiesStore = findStoreLazy("UserAffinitiesStore");
|
||||
const UserAffinitiesStore = findStore("UserAffinitiesStore");
|
||||
|
||||
const settings = definePluginSettings({
|
||||
sortByAffinity: {
|
||||
type: OptionType.BOOLEAN,
|
||||
default: true,
|
||||
description: "Whether to sort implicit relationships by their affinity to you.",
|
||||
restartNeeded: true
|
||||
}
|
||||
});
|
||||
|
||||
export default definePlugin({
|
||||
name: "ImplicitRelationships",
|
||||
description: "Shows your implicit relationships in the Friends tab.",
|
||||
authors: [Devs.Dolfies],
|
||||
settings,
|
||||
|
||||
patches: [
|
||||
// Counts header
|
||||
{
|
||||
|
@ -75,7 +85,7 @@ export default definePlugin({
|
|||
{
|
||||
find: "getRelationshipCounts(){",
|
||||
replacement: {
|
||||
predicate: () => Settings.plugins.ImplicitRelationships.sortByAffinity,
|
||||
predicate: () => settings.store.sortByAffinity,
|
||||
match: /\}\)\.sortBy\((.+?)\)\.value\(\)/,
|
||||
replace: "}).sortBy(row => $self.wrapSort(($1), row)).value()"
|
||||
}
|
||||
|
@ -104,16 +114,6 @@ export default definePlugin({
|
|||
},
|
||||
}
|
||||
],
|
||||
settings: definePluginSettings(
|
||||
{
|
||||
sortByAffinity: {
|
||||
type: OptionType.BOOLEAN,
|
||||
default: true,
|
||||
description: "Whether to sort implicit relationships by their affinity to you.",
|
||||
restartNeeded: true
|
||||
},
|
||||
}
|
||||
),
|
||||
|
||||
wrapSort(comparator: Function, row: any) {
|
||||
return row.type === 5
|
||||
|
|
|
@ -66,14 +66,14 @@ export function addPatch(newPatch: Omit<Patch, "plugin">, pluginName: string) {
|
|||
patch.replacement = [patch.replacement];
|
||||
}
|
||||
|
||||
patch.replacement = patch.replacement.filter(({ predicate }) => !predicate || predicate());
|
||||
|
||||
if (IS_REPORTER) {
|
||||
patch.replacement.forEach(r => {
|
||||
delete r.predicate;
|
||||
});
|
||||
}
|
||||
|
||||
patch.replacement = patch.replacement.filter(({ predicate }) => !predicate || predicate());
|
||||
|
||||
patches.push(patch);
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ import { Link } from "@components/Link";
|
|||
import { Devs } from "@utils/constants";
|
||||
import { Logger } from "@utils/Logger";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
import { findStore } from "@webpack";
|
||||
import { ApplicationAssetUtils, FluxDispatcher, Forms } from "@webpack/common";
|
||||
|
||||
interface ActivityAssets {
|
||||
|
@ -86,7 +86,7 @@ const placeholderId = "2a96cbd8b46e442fc41c2b86b821562f";
|
|||
|
||||
const logger = new Logger("LastFMRichPresence");
|
||||
|
||||
const presenceStore = findByPropsLazy("getLocalPresence");
|
||||
const SelfPresenceStore = findStore("SelfPresenceStore");
|
||||
|
||||
async function getApplicationAsset(key: string): Promise<string> {
|
||||
return (await ApplicationAssetUtils.fetchAssetIds(applicationId, [key]))[0];
|
||||
|
@ -275,7 +275,7 @@ export default definePlugin({
|
|||
|
||||
async getActivity(): Promise<Activity | null> {
|
||||
if (settings.store.hideWithSpotify) {
|
||||
for (const activity of presenceStore.getActivities()) {
|
||||
for (const activity of SelfPresenceStore.getActivities()) {
|
||||
if (activity.type === ActivityType.LISTENING && activity.application_id !== applicationId) {
|
||||
// there is already music status because of Spotify or richerCider (probably more)
|
||||
return null;
|
||||
|
|
|
@ -14,7 +14,7 @@ import { OnlineMemberCountStore } from "./OnlineMemberCountStore";
|
|||
export function MemberCount({ isTooltip, tooltipGuildId }: { isTooltip?: true; tooltipGuildId?: string; }) {
|
||||
const currentChannel = useStateFromStores([SelectedChannelStore], () => getCurrentChannel());
|
||||
|
||||
const guildId = isTooltip ? tooltipGuildId! : currentChannel?.guild_id;
|
||||
const guildId = isTooltip ? tooltipGuildId! : currentChannel.guild_id;
|
||||
|
||||
const totalCount = useStateFromStores(
|
||||
[GuildMemberCountStore],
|
||||
|
@ -33,7 +33,7 @@ export function MemberCount({ isTooltip, tooltipGuildId }: { isTooltip?: true; t
|
|||
|
||||
const threadGroups = useStateFromStores(
|
||||
[ThreadMemberListStore],
|
||||
() => ThreadMemberListStore.getMemberListSections(currentChannel?.id)
|
||||
() => ThreadMemberListStore.getMemberListSections(currentChannel.id)
|
||||
);
|
||||
|
||||
if (!isTooltip && (groups.length >= 1 || groups[0].id !== "unknown")) {
|
||||
|
|
|
@ -15,8 +15,8 @@ export const OnlineMemberCountStore = proxyLazy(() => {
|
|||
const onlineMemberMap = new Map<string, number>();
|
||||
|
||||
class OnlineMemberCountStore extends Flux.Store {
|
||||
getCount(guildId?: string) {
|
||||
return onlineMemberMap.get(guildId!);
|
||||
getCount(guildId: string) {
|
||||
return onlineMemberMap.get(guildId);
|
||||
}
|
||||
|
||||
async _ensureCount(guildId: string) {
|
||||
|
@ -25,8 +25,8 @@ export const OnlineMemberCountStore = proxyLazy(() => {
|
|||
await PrivateChannelsStore.preload(guildId, GuildChannelStore.getDefaultChannel(guildId).id);
|
||||
}
|
||||
|
||||
ensureCount(guildId?: string) {
|
||||
if (!guildId || onlineMemberMap.has(guildId)) return;
|
||||
ensureCount(guildId: string) {
|
||||
if (onlineMemberMap.has(guildId)) return;
|
||||
|
||||
preloadQueue.push(() =>
|
||||
this._ensureCount(guildId)
|
||||
|
|
|
@ -23,17 +23,17 @@ import { classNameFactory } from "@api/Styles";
|
|||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findStoreLazy } from "@webpack";
|
||||
import { findStore } from "@webpack";
|
||||
import { FluxStore } from "@webpack/types";
|
||||
|
||||
import { MemberCount } from "./MemberCount";
|
||||
|
||||
export const GuildMemberCountStore = findStoreLazy("GuildMemberCountStore") as FluxStore & { getMemberCount(guildId?: string): number | null; };
|
||||
export const ChannelMemberStore = findStoreLazy("ChannelMemberStore") as FluxStore & {
|
||||
getProps(guildId?: string, channelId?: string): { groups: { count: number; id: string; }[]; };
|
||||
export const GuildMemberCountStore = findStore("GuildMemberCountStore") as FluxStore & { getMemberCount(guildId: string): number | null; };
|
||||
export const ChannelMemberStore = findStore("ChannelMemberStore") as FluxStore & {
|
||||
getProps(guildId: string, channelId: string): { groups: { count: number; id: string; }[]; };
|
||||
};
|
||||
export const ThreadMemberListStore = findStoreLazy("ThreadMemberListStore") as FluxStore & {
|
||||
getMemberListSections(channelId?: string): { [sectionId: string]: { sectionId: string; userIds: string[]; }; };
|
||||
export const ThreadMemberListStore = findStore("ThreadMemberListStore") as FluxStore & {
|
||||
getMemberListSections(channelId: string): { [sectionId: string]: { sectionId: string; userIds: string[]; }; };
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
# MentionAvatars
|
||||
|
||||
Shows user avatars and role icons inside mentions
|
||||
Shows user avatars inside mentions
|
||||
|
||||
![](https://github.com/user-attachments/assets/fc76ea47-5e19-4063-a592-c57785a75cc7)
|
||||
![](https://github.com/user-attachments/assets/76c4c3d9-7cde-42db-ba84-903cbb40c163)
|
||||
|
|
|
@ -10,42 +10,21 @@ import { definePluginSettings } from "@api/Settings";
|
|||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { GuildStore, SelectedGuildStore, useState } from "@webpack/common";
|
||||
import { SelectedGuildStore, useState } from "@webpack/common";
|
||||
import { User } from "discord-types/general";
|
||||
|
||||
const settings = definePluginSettings({
|
||||
showAtSymbol: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Whether the the @ symbol should be displayed on user mentions",
|
||||
description: "Whether the the @ symbol should be displayed",
|
||||
default: true
|
||||
}
|
||||
});
|
||||
|
||||
function DefaultRoleIcon() {
|
||||
return (
|
||||
<svg
|
||||
className="vc-mentionAvatars-icon vc-mentionAvatars-role-icon"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
>
|
||||
<path
|
||||
d="M14 8.00598C14 10.211 12.206 12.006 10 12.006C7.795 12.006 6 10.211 6 8.00598C6 5.80098 7.794 4.00598 10 4.00598C12.206 4.00598 14 5.80098 14 8.00598ZM2 19.006C2 15.473 5.29 13.006 10 13.006C14.711 13.006 18 15.473 18 19.006V20.006H2V19.006Z"
|
||||
/>
|
||||
<path
|
||||
d="M20.0001 20.006H22.0001V19.006C22.0001 16.4433 20.2697 14.4415 17.5213 13.5352C19.0621 14.9127 20.0001 16.8059 20.0001 19.006V20.006Z"
|
||||
/>
|
||||
<path
|
||||
d="M14.8834 11.9077C16.6657 11.5044 18.0001 9.9077 18.0001 8.00598C18.0001 5.96916 16.4693 4.28218 14.4971 4.0367C15.4322 5.09511 16.0001 6.48524 16.0001 8.00598C16.0001 9.44888 15.4889 10.7742 14.6378 11.8102C14.7203 11.8418 14.8022 11.8743 14.8834 11.9077Z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default definePlugin({
|
||||
name: "MentionAvatars",
|
||||
description: "Shows user avatars and role icons inside mentions",
|
||||
authors: [Devs.Ven, Devs.SerStars],
|
||||
description: "Shows user avatars inside mentions",
|
||||
authors: [Devs.Ven],
|
||||
|
||||
patches: [{
|
||||
find: ".USER_MENTION)",
|
||||
|
@ -53,13 +32,6 @@ export default definePlugin({
|
|||
match: /children:"@"\.concat\((null!=\i\?\i:\i)\)(?<=\.useName\((\i)\).+?)/,
|
||||
replace: "children:$self.renderUsername({username:$1,user:$2})"
|
||||
}
|
||||
},
|
||||
{
|
||||
find: ".ROLE_MENTION)",
|
||||
replacement: {
|
||||
match: /children:\[\i&&.{0,50}\.RoleDot.{0,300},\i(?=\])/,
|
||||
replace: "$&,$self.renderRoleIcon(arguments[0])"
|
||||
}
|
||||
}],
|
||||
|
||||
settings,
|
||||
|
@ -75,31 +47,12 @@ export default definePlugin({
|
|||
onMouseEnter={() => setIsHovering(true)}
|
||||
onMouseLeave={() => setIsHovering(false)}
|
||||
>
|
||||
<img
|
||||
src={user.getAvatarURL(SelectedGuildStore.getGuildId(), 16, isHovering)}
|
||||
className="vc-mentionAvatars-icon"
|
||||
style={{ borderRadius: "50%" }}
|
||||
/>
|
||||
<img src={user.getAvatarURL(SelectedGuildStore.getGuildId(), 16, isHovering)} className="vc-mentionAvatars-avatar" />
|
||||
{getUsernameString(username)}
|
||||
</span>
|
||||
);
|
||||
}, { noop: true }),
|
||||
}, { noop: true })
|
||||
|
||||
renderRoleIcon: ErrorBoundary.wrap(({ roleId, guildId }: { roleId: string, guildId: string; }) => {
|
||||
// Discord uses Role Mentions for uncached users because .... idk
|
||||
if (!roleId) return null;
|
||||
|
||||
const role = GuildStore.getRole(guildId, roleId);
|
||||
|
||||
if (!role?.icon) return <DefaultRoleIcon />;
|
||||
|
||||
return (
|
||||
<img
|
||||
className="vc-mentionAvatars-icon vc-mentionAvatars-role-icon"
|
||||
src={`${location.protocol}//${window.GLOBAL_ENV.CDN_HOST}/role-icons/${roleId}/${role.icon}.webp?size=24&quality=lossless`}
|
||||
/>
|
||||
);
|
||||
}),
|
||||
});
|
||||
|
||||
function getUsernameString(username: string) {
|
||||
|
|
|
@ -1,16 +1,8 @@
|
|||
.vc-mentionAvatars-icon {
|
||||
.vc-mentionAvatars-avatar {
|
||||
vertical-align: middle;
|
||||
width: 1em !important; /* insane discord sets width: 100% in channel topic */
|
||||
height: 1em;
|
||||
margin: 0 4px 0.2rem 2px;
|
||||
border-radius: 50%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.vc-mentionAvatars-role-icon {
|
||||
margin: 0 2px 0.2rem 4px;
|
||||
}
|
||||
|
||||
/** don't display inside the ServerInfo modal owner mention */
|
||||
.vc-gp-owner .vc-mentionAvatars-icon {
|
||||
display: none;
|
||||
}
|
||||
|
|
|
@ -20,11 +20,11 @@ import { addClickListener, removeClickListener } from "@api/MessageEvents";
|
|||
import { definePluginSettings } from "@api/Settings";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
import { findByProps, findStore } from "@webpack";
|
||||
import { FluxDispatcher, PermissionsBits, PermissionStore, UserStore } from "@webpack/common";
|
||||
|
||||
const MessageActions = findByPropsLazy("deleteMessage", "startEditMessage");
|
||||
const EditStore = findByPropsLazy("isEditing", "isEditingAny");
|
||||
const MessageActions = findByProps("deleteMessage", "startEditMessage");
|
||||
const EditMessageStore = findStore("EditMessageStore");
|
||||
|
||||
let isDeletePressed = false;
|
||||
const keydown = (e: KeyboardEvent) => e.key === "Backspace" && (isDeletePressed = true);
|
||||
|
@ -74,7 +74,7 @@ export default definePlugin({
|
|||
if (msg.deleted === true) return;
|
||||
|
||||
if (isMe) {
|
||||
if (!settings.store.enableDoubleClickToEdit || EditStore.isEditing(channel.id, msg.id)) return;
|
||||
if (!settings.store.enableDoubleClickToEdit || EditMessageStore.isEditing(channel.id, msg.id)) return;
|
||||
|
||||
MessageActions.startEditMessage(channel.id, msg.id, msg.content);
|
||||
event.preventDefault();
|
||||
|
|
|
@ -9,7 +9,7 @@ import ErrorBoundary from "@components/ErrorBoundary";
|
|||
import { Devs } from "@utils/constants";
|
||||
import { isNonNullish } from "@utils/guards";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findExportedComponentLazy } from "@webpack";
|
||||
import { findExportedComponent } from "@webpack";
|
||||
import { SnowflakeUtils, Tooltip } from "@webpack/common";
|
||||
import { Message } from "discord-types/general";
|
||||
|
||||
|
@ -26,7 +26,7 @@ interface Diff {
|
|||
}
|
||||
|
||||
const DISCORD_KT_DELAY = 1471228928;
|
||||
const HiddenVisually = findExportedComponentLazy("HiddenVisually");
|
||||
const HiddenVisually = findExportedComponent("HiddenVisually");
|
||||
|
||||
export default definePlugin({
|
||||
name: "MessageLatency",
|
||||
|
|
|
@ -25,7 +25,7 @@ import { Devs } from "@utils/constants.js";
|
|||
import { classes } from "@utils/misc";
|
||||
import { Queue } from "@utils/Queue";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findByPropsLazy, findComponentByCodeLazy } from "@webpack";
|
||||
import { findByProps, findComponentByCode } from "@webpack";
|
||||
import {
|
||||
Button,
|
||||
ChannelStore,
|
||||
|
@ -47,14 +47,14 @@ const messageCache = new Map<string, {
|
|||
fetched: boolean;
|
||||
}>();
|
||||
|
||||
const Embed = findComponentByCodeLazy(".inlineMediaEmbed");
|
||||
const AutoModEmbed = findComponentByCodeLazy(".withFooter]:", "childrenMessageContent:");
|
||||
const ChannelMessage = findComponentByCodeLazy("childrenExecutedCommand:", ".hideAccessories");
|
||||
const Embed = findComponentByCode(".inlineMediaEmbed");
|
||||
const AutoModEmbed = findComponentByCode(".withFooter]:", "childrenMessageContent:");
|
||||
const ChannelMessage = findComponentByCode("childrenExecutedCommand:", ".hideAccessories");
|
||||
|
||||
const SearchResultClasses = findByPropsLazy("message", "searchResult");
|
||||
const EmbedClasses = findByPropsLazy("embedAuthorIcon", "embedAuthor", "embedAuthor");
|
||||
const SearchResultClasses = findByProps("message", "searchResult");
|
||||
const EmbedClasses = findByProps("embedAuthorIcon", "embedAuthor", "embedAuthor");
|
||||
|
||||
const MessageDisplayCompact = getUserSettingLazy("textAndImages", "messageDisplayCompact")!;
|
||||
const MessageDisplayCompact = getUserSettingLazy("textAndImages", "messageDisplayCompact");
|
||||
|
||||
const messageLinkRegex = /(?<!<)https?:\/\/(?:\w+\.)?discord(?:app)?\.com\/channels\/(?:\d{17,20}|@me)\/(\d{17,20})\/(\d{17,20})/g;
|
||||
const tenorRegex = /^https:\/\/(?:www\.)?tenor\.com\//;
|
||||
|
@ -215,10 +215,9 @@ function computeWidthAndHeight(width: number, height: number) {
|
|||
|
||||
function withEmbeddedBy(message: Message, embeddedBy: string[]) {
|
||||
return new Proxy(message, {
|
||||
get(_, prop) {
|
||||
get(target, prop, receiver) {
|
||||
if (prop === "vencordEmbeddedBy") return embeddedBy;
|
||||
// @ts-ignore ts so bad
|
||||
return Reflect.get(...arguments);
|
||||
return Reflect.get(target, prop, receiver);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -9,13 +9,13 @@ import ErrorBoundary from "@components/ErrorBoundary";
|
|||
import { Margins } from "@utils/margins";
|
||||
import { classes } from "@utils/misc";
|
||||
import { ModalCloseButton, ModalContent, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
import { findByProps } from "@webpack";
|
||||
import { TabBar, Text, Timestamp, TooltipContainer, useState } from "@webpack/common";
|
||||
|
||||
import { parseEditContent } from ".";
|
||||
|
||||
const CodeContainerClasses = findByPropsLazy("markup", "codeContainer");
|
||||
const MiscClasses = findByPropsLazy("messageContent", "markupRtl");
|
||||
const CodeContainerClasses = findByProps("markup", "codeContainer");
|
||||
const MiscClasses = findByProps("messageContent", "markupRtl");
|
||||
|
||||
const cl = classNameFactory("vc-ml-modal-");
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ import "./messageLogger.css";
|
|||
|
||||
import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu";
|
||||
import { updateMessage } from "@api/MessageUpdater";
|
||||
import { Settings } from "@api/Settings";
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { disableStyle, enableStyle } from "@api/Styles";
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Devs } from "@utils/constants";
|
||||
|
@ -28,7 +28,7 @@ import { proxyLazy } from "@utils/lazy";
|
|||
import { Logger } from "@utils/Logger";
|
||||
import { classes } from "@utils/misc";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findByCodeLazy, findByPropsLazy } from "@webpack";
|
||||
import { findByCode, findByProps } from "@webpack";
|
||||
import { ChannelStore, FluxDispatcher, i18n, Menu, MessageStore, Parser, SelectedChannelStore, Timestamp, UserStore, useStateFromStores } from "@webpack/common";
|
||||
import { Message } from "discord-types/general";
|
||||
|
||||
|
@ -42,11 +42,11 @@ interface MLMessage extends Message {
|
|||
firstEditTimestamp?: Date;
|
||||
}
|
||||
|
||||
const styles = findByPropsLazy("edited", "communicationDisabled", "isSystemMessage");
|
||||
const getMessage = findByCodeLazy('replace(/^\\n+|\\n+$/g,"")');
|
||||
const styles = findByProps("edited", "communicationDisabled", "isSystemMessage");
|
||||
const getMessage = findByCode('replace(/^\\n+|\\n+$/g,"")');
|
||||
|
||||
function addDeleteStyle() {
|
||||
if (Settings.plugins.MessageLogger.deleteStyle === "text") {
|
||||
if (settings.store.deleteStyle === "text") {
|
||||
enableStyle(textStyle);
|
||||
disableStyle(overlayStyle);
|
||||
} else {
|
||||
|
@ -142,58 +142,7 @@ export function parseEditContent(content: string, message: Message) {
|
|||
});
|
||||
}
|
||||
|
||||
export default definePlugin({
|
||||
name: "MessageLogger",
|
||||
description: "Temporarily logs deleted and edited messages.",
|
||||
authors: [Devs.rushii, Devs.Ven, Devs.AutumnVN, Devs.Nickyux, Devs.Kyuuhachi],
|
||||
dependencies: ["MessageUpdaterAPI"],
|
||||
|
||||
contextMenus: {
|
||||
"message": patchMessageContextMenu,
|
||||
"channel-context": patchChannelContextMenu,
|
||||
"thread-context": patchChannelContextMenu,
|
||||
"user-context": patchChannelContextMenu,
|
||||
"gdm-context": patchChannelContextMenu
|
||||
},
|
||||
|
||||
start() {
|
||||
addDeleteStyle();
|
||||
},
|
||||
|
||||
renderEdits: ErrorBoundary.wrap(({ message: { id: messageId, channel_id: channelId } }: { message: Message; }) => {
|
||||
const message = useStateFromStores(
|
||||
[MessageStore],
|
||||
() => MessageStore.getMessage(channelId, messageId) as MLMessage,
|
||||
null,
|
||||
(oldMsg, newMsg) => oldMsg?.editHistory === newMsg?.editHistory
|
||||
);
|
||||
|
||||
return Settings.plugins.MessageLogger.inlineEdits && (
|
||||
<>
|
||||
{message.editHistory?.map(edit => (
|
||||
<div className="messagelogger-edited">
|
||||
{parseEditContent(edit.content, message)}
|
||||
<Timestamp
|
||||
timestamp={edit.timestamp}
|
||||
isEdited={true}
|
||||
isInline={false}
|
||||
>
|
||||
<span className={styles.edited}>{" "}({i18n.Messages.MESSAGE_EDITED})</span>
|
||||
</Timestamp>
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}, { noop: true }),
|
||||
|
||||
makeEdit(newMessage: any, oldMessage: any): any {
|
||||
return {
|
||||
timestamp: new Date(newMessage.edited_timestamp),
|
||||
content: oldMessage.content
|
||||
};
|
||||
},
|
||||
|
||||
options: {
|
||||
const settings = definePluginSettings({
|
||||
deleteStyle: {
|
||||
type: OptionType.SELECT,
|
||||
description: "The style of deleted messages",
|
||||
|
@ -249,6 +198,58 @@ export default definePlugin({
|
|||
description: "Comma-separated list of guild IDs to ignore",
|
||||
default: ""
|
||||
},
|
||||
});
|
||||
|
||||
export default definePlugin({
|
||||
name: "MessageLogger",
|
||||
description: "Temporarily logs deleted and edited messages.",
|
||||
authors: [Devs.rushii, Devs.Ven, Devs.AutumnVN, Devs.Nickyux, Devs.Kyuuhachi],
|
||||
dependencies: ["MessageUpdaterAPI"],
|
||||
settings,
|
||||
|
||||
contextMenus: {
|
||||
"message": patchMessageContextMenu,
|
||||
"channel-context": patchChannelContextMenu,
|
||||
"thread-context": patchChannelContextMenu,
|
||||
"user-context": patchChannelContextMenu,
|
||||
"gdm-context": patchChannelContextMenu
|
||||
},
|
||||
|
||||
start() {
|
||||
addDeleteStyle();
|
||||
},
|
||||
|
||||
renderEdits: ErrorBoundary.wrap(({ message: { id: messageId, channel_id: channelId } }: { message: Message; }) => {
|
||||
const message = useStateFromStores(
|
||||
[MessageStore],
|
||||
() => MessageStore.getMessage(channelId, messageId) as MLMessage,
|
||||
null,
|
||||
(oldMsg, newMsg) => oldMsg?.editHistory === newMsg?.editHistory
|
||||
);
|
||||
|
||||
return settings.store.inlineEdits && (
|
||||
<>
|
||||
{message.editHistory?.map(edit => (
|
||||
<div className="messagelogger-edited">
|
||||
{parseEditContent(edit.content, message)}
|
||||
<Timestamp
|
||||
timestamp={edit.timestamp}
|
||||
isEdited={true}
|
||||
isInline={false}
|
||||
>
|
||||
<span className={styles.edited}>{" "}({i18n.Messages.MESSAGE_EDITED})</span>
|
||||
</Timestamp>
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}, { noop: true }),
|
||||
|
||||
makeEdit(newMessage: any, oldMessage: any): any {
|
||||
return {
|
||||
timestamp: new Date(newMessage.edited_timestamp),
|
||||
content: oldMessage.content
|
||||
};
|
||||
},
|
||||
|
||||
handleDelete(cache: any, data: { ids: string[], id: string; mlDeleted?: boolean; }, isBulk: boolean) {
|
||||
|
@ -285,7 +286,7 @@ export default definePlugin({
|
|||
},
|
||||
|
||||
shouldIgnore(message: any, isEdit = false) {
|
||||
const { ignoreBots, ignoreSelf, ignoreUsers, ignoreChannels, ignoreGuilds, logEdits, logDeletes } = Settings.plugins.MessageLogger;
|
||||
const { ignoreBots, ignoreSelf, ignoreUsers, ignoreChannels, ignoreGuilds, logEdits, logDeletes } = settings.store;
|
||||
const myId = UserStore.getCurrentUser().id;
|
||||
|
||||
return ignoreBots && message.author?.bot ||
|
||||
|
@ -493,7 +494,7 @@ export default definePlugin({
|
|||
match: /if\((\i)\.blocked\)return \i\.\i\.MESSAGE_GROUP_BLOCKED;/,
|
||||
replace: '$&else if($1.deleted) return"MESSAGE_GROUP_DELETED";',
|
||||
},
|
||||
predicate: () => Settings.plugins.MessageLogger.collapseDeleted
|
||||
predicate: () => settings.store.collapseDeleted
|
||||
},
|
||||
{
|
||||
// Message group rendering
|
||||
|
@ -508,7 +509,7 @@ export default definePlugin({
|
|||
replace: '$&$1.type==="MESSAGE_GROUP_DELETED"?$self.Messages.DELETED_MESSAGE_COUNT:',
|
||||
},
|
||||
],
|
||||
predicate: () => Settings.plugins.MessageLogger.collapseDeleted
|
||||
predicate: () => settings.store.collapseDeleted
|
||||
}
|
||||
]
|
||||
});
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
import { ApplicationCommandInputType, ApplicationCommandOptionType, findOption, registerCommand, sendBotMessage, unregisterCommand } from "@api/Commands";
|
||||
import * as DataStore from "@api/DataStore";
|
||||
import { Settings } from "@api/Settings";
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
|
||||
|
@ -60,7 +60,7 @@ function createTagCommand(tag: Tag) {
|
|||
return { content: `/${tag.name}` };
|
||||
}
|
||||
|
||||
if (Settings.plugins.MessageTags.clyde) sendBotMessage(ctx.channel.id, {
|
||||
if (settings.store.clyde) sendBotMessage(ctx.channel.id, {
|
||||
content: `${EMOTE} The tag **${tag.name}** has been sent!`
|
||||
});
|
||||
return { content: tag.message.replaceAll("\\n", "\n") };
|
||||
|
@ -69,19 +69,21 @@ function createTagCommand(tag: Tag) {
|
|||
}, "CustomTags");
|
||||
}
|
||||
|
||||
|
||||
export default definePlugin({
|
||||
name: "MessageTags",
|
||||
description: "Allows you to save messages and to use them with a simple command.",
|
||||
authors: [Devs.Luna],
|
||||
options: {
|
||||
const settings = definePluginSettings({
|
||||
clyde: {
|
||||
name: "Clyde message on send",
|
||||
description: "If enabled, clyde will send you an ephemeral message when a tag was used.",
|
||||
type: OptionType.BOOLEAN,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
export default definePlugin({
|
||||
name: "MessageTags",
|
||||
description: "Allows you to save messages and to use them with a simple command.",
|
||||
authors: [Devs.Luna],
|
||||
settings,
|
||||
|
||||
dependencies: ["CommandsAPI"],
|
||||
|
||||
async start() {
|
||||
|
|
|
@ -19,11 +19,12 @@
|
|||
import { definePluginSettings } from "@api/Settings";
|
||||
import { Flex } from "@components/Flex";
|
||||
import { Devs } from "@utils/constants";
|
||||
import { LazyComponentType } from "@utils/lazyReact";
|
||||
import { Margins } from "@utils/margins";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findByCodeLazy, findLazy } from "@webpack";
|
||||
import { findByCode, findComponentByCode } from "@webpack";
|
||||
import { Card, ChannelStore, Forms, GuildStore, PermissionsBits, Switch, TextInput, Tooltip } from "@webpack/common";
|
||||
import type { Permissions, RC } from "@webpack/types";
|
||||
import type { Permissions } from "@webpack/types";
|
||||
import type { Channel, Guild, Message, User } from "discord-types/general";
|
||||
|
||||
interface Tag {
|
||||
|
@ -59,9 +60,9 @@ const computePermissions: (options: {
|
|||
overwrites?: Channel["permissionOverwrites"] | null;
|
||||
checkElevated?: boolean /* = true */;
|
||||
excludeGuildPermissions?: boolean /* = false */;
|
||||
}) => bigint = findByCodeLazy(".getCurrentUser()", ".computeLurkerPermissionsAllowList()");
|
||||
}) => bigint = findByCode(".getCurrentUser()", ".computeLurkerPermissionsAllowList()");
|
||||
|
||||
const Tag = findLazy(m => m.Types?.[0] === "BOT") as RC<{ type?: number, className?: string, useRemSizes?: boolean; }> & { Types: Record<string, number>; };
|
||||
const Tag = findComponentByCode(".DISCORD_SYSTEM_MESSAGE_BOT_TAG_TOOLTIP_OFFICIAL,") as LazyComponentType<{ type?: number, className?: string, useRemSizes?: boolean; }> & { Types: Record<string, number>; };
|
||||
|
||||
const isWebhook = (message: Message, user: User) => !!message?.webhookId && user.isNonUserBot();
|
||||
|
||||
|
|
|
@ -20,16 +20,15 @@ import ErrorBoundary from "@components/ErrorBoundary";
|
|||
import { Devs } from "@utils/constants";
|
||||
import { isNonNullish } from "@utils/guards";
|
||||
import definePlugin from "@utils/types";
|
||||
import { findByPropsLazy, findComponentByCodeLazy } from "@webpack";
|
||||
import { findByProps } from "@webpack";
|
||||
import { Avatar, ChannelStore, Clickable, IconUtils, RelationshipStore, ScrollerThin, useMemo, UserStore } from "@webpack/common";
|
||||
import { Channel, User } from "discord-types/general";
|
||||
|
||||
const SelectedChannelActionCreators = findByPropsLazy("selectPrivateChannel");
|
||||
const UserUtils = findByPropsLazy("getGlobalName");
|
||||
const SelectedChannelActionCreators = findByProps("selectPrivateChannel");
|
||||
const UserUtils = findByProps("getGlobalName");
|
||||
|
||||
const ProfileListClasses = findByPropsLazy("emptyIconFriends", "emptyIconGuilds");
|
||||
const ExpandableList = findComponentByCodeLazy(".mutualFriendItem]");
|
||||
const GuildLabelClasses = findByPropsLazy("guildNick", "guildAvatarWithoutIcon");
|
||||
const ProfileListClasses = findByProps("emptyIconFriends", "emptyIconGuilds");
|
||||
const GuildLabelClasses = findByProps("guildNick", "guildAvatarWithoutIcon");
|
||||
|
||||
function getGroupDMName(channel: Channel) {
|
||||
return channel.name ||
|
||||
|
@ -51,29 +50,6 @@ function getMutualGDMCountText(user: User) {
|
|||
return `${count === 0 ? "No" : count} Mutual Group${count !== 1 ? "s" : ""}`;
|
||||
}
|
||||
|
||||
function renderClickableGDMs(mutualDms: Channel[], onClose: () => void) {
|
||||
return mutualDms.map(c => (
|
||||
<Clickable
|
||||
className={ProfileListClasses.listRow}
|
||||
onClick={() => {
|
||||
onClose();
|
||||
SelectedChannelActionCreators.selectPrivateChannel(c.id);
|
||||
}}
|
||||
>
|
||||
<Avatar
|
||||
src={IconUtils.getChannelIconURL({ id: c.id, icon: c.icon, size: 32 })}
|
||||
size="SIZE_40"
|
||||
className={ProfileListClasses.listAvatar}
|
||||
>
|
||||
</Avatar>
|
||||
<div className={ProfileListClasses.listRowContent}>
|
||||
<div className={ProfileListClasses.listName}>{getGroupDMName(c)}</div>
|
||||
<div className={GuildLabelClasses.guildNick}>{c.recipients.length + 1} Members</div>
|
||||
</div>
|
||||
</Clickable>
|
||||
));
|
||||
}
|
||||
|
||||
const IS_PATCHED = Symbol("MutualGroupDMs.Patched");
|
||||
|
||||
export default definePlugin({
|
||||
|
@ -94,13 +70,6 @@ export default definePlugin({
|
|||
replace: "$1==='MUTUAL_GDMS'?$self.renderMutualGDMs(arguments[0]):$&"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
find: 'section:"MUTUAL_FRIENDS"',
|
||||
replacement: {
|
||||
match: /\.openUserProfileModal.+?\)}\)}\)(?<=(\(0,\i\.jsxs?\)\(\i\.\i,{className:(\i)\.divider}\)).+?)/,
|
||||
replace: "$&,$self.renderDMPageList({user: arguments[0].user, Divider: $1, listStyle: $2.list})"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
|
@ -115,9 +84,28 @@ export default definePlugin({
|
|||
},
|
||||
|
||||
renderMutualGDMs: ErrorBoundary.wrap(({ user, onClose }: { user: User, onClose: () => void; }) => {
|
||||
const mutualGDms = useMemo(() => getMutualGroupDms(user.id), [user.id]);
|
||||
const mutualDms = useMemo(() => getMutualGroupDms(user.id), [user.id]);
|
||||
|
||||
const entries = renderClickableGDMs(mutualGDms, onClose);
|
||||
const entries = mutualDms.map(c => (
|
||||
<Clickable
|
||||
className={ProfileListClasses.listRow}
|
||||
onClick={() => {
|
||||
onClose();
|
||||
SelectedChannelActionCreators.selectPrivateChannel(c.id);
|
||||
}}
|
||||
>
|
||||
<Avatar
|
||||
src={IconUtils.getChannelIconURL({ id: c.id, icon: c.icon, size: 32 })}
|
||||
size="SIZE_40"
|
||||
className={ProfileListClasses.listAvatar}
|
||||
>
|
||||
</Avatar>
|
||||
<div className={ProfileListClasses.listRowContent}>
|
||||
<div className={ProfileListClasses.listName}>{getGroupDMName(c)}</div>
|
||||
<div className={GuildLabelClasses.guildNick}>{c.recipients.length + 1} Members</div>
|
||||
</div>
|
||||
</Clickable>
|
||||
));
|
||||
|
||||
return (
|
||||
<ScrollerThin
|
||||
|
@ -136,24 +124,5 @@ export default definePlugin({
|
|||
}
|
||||
</ScrollerThin>
|
||||
);
|
||||
}),
|
||||
|
||||
renderDMPageList: ErrorBoundary.wrap(({ user, Divider, listStyle }: { user: User, Divider: JSX.Element, listStyle: string; }) => {
|
||||
const mutualGDms = getMutualGroupDms(user.id);
|
||||
if (mutualGDms.length === 0) return null;
|
||||
|
||||
const header = getMutualGDMCountText(user);
|
||||
|
||||
return (
|
||||
<>
|
||||
{Divider}
|
||||
<ExpandableList
|
||||
className={listStyle}
|
||||
header={header}
|
||||
isLoadingHeader={false}
|
||||
children={renderClickableGDMs(mutualGDms, () => { })}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
})
|
||||
});
|
||||
|
|
|
@ -24,18 +24,18 @@ import { definePluginSettings, migratePluginSettings } from "@api/Settings";
|
|||
import { CogWheel } from "@components/Icons";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findByCodeLazy, findByPropsLazy, mapMangledModuleLazy } from "@webpack";
|
||||
import { findByCode, findProp, mapMangledModule } from "@webpack";
|
||||
import { Menu } from "@webpack/common";
|
||||
import { Guild } from "discord-types/general";
|
||||
|
||||
const { updateGuildNotificationSettings } = findByPropsLazy("updateGuildNotificationSettings");
|
||||
const { toggleShowAllChannels } = mapMangledModuleLazy(".onboardExistingMember(", {
|
||||
const updateGuildNotificationSettings = findProp("updateGuildNotificationSettings");
|
||||
const { toggleShowAllChannels } = mapMangledModule(".onboardExistingMember(", {
|
||||
toggleShowAllChannels: m => {
|
||||
const s = String(m);
|
||||
return s.length < 100 && !s.includes("onboardExistingMember") && !s.includes("getOptedInChannels");
|
||||
}
|
||||
});
|
||||
const isOptInEnabledForGuild = findByCodeLazy(".COMMUNITY)||", ".isOptInEnabled(");
|
||||
const isOptInEnabledForGuild = findByCode(".COMMUNITY)||", ".isOptInEnabled(");
|
||||
|
||||
const settings = definePluginSettings({
|
||||
guild: {
|
||||
|
|
|
@ -16,19 +16,28 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Settings } from "@api/Settings";
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { Devs } from "@utils/constants";
|
||||
import { Logger } from "@utils/Logger";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
import { RelationshipStore } from "@webpack/common";
|
||||
import { Message } from "discord-types/general";
|
||||
|
||||
const RelationshipStore = findByPropsLazy("getRelationships", "isBlocked");
|
||||
const settings = definePluginSettings({
|
||||
ignoreBlockedMessages: {
|
||||
description: "Completely ignores (recent) incoming messages from blocked users (locally).",
|
||||
type: OptionType.BOOLEAN,
|
||||
default: false,
|
||||
restartNeeded: true,
|
||||
},
|
||||
});
|
||||
|
||||
export default definePlugin({
|
||||
name: "NoBlockedMessages",
|
||||
description: "Hides all blocked messages from chat completely.",
|
||||
authors: [Devs.rushii, Devs.Samu],
|
||||
settings,
|
||||
|
||||
patches: [
|
||||
{
|
||||
find: "Messages.BLOCKED_MESSAGES_HIDE",
|
||||
|
@ -44,7 +53,7 @@ export default definePlugin({
|
|||
'"displayName","ReadStateStore")'
|
||||
].map(find => ({
|
||||
find,
|
||||
predicate: () => Settings.plugins.NoBlockedMessages.ignoreBlockedMessages === true,
|
||||
predicate: () => settings.store.ignoreBlockedMessages === true,
|
||||
replacement: [
|
||||
{
|
||||
match: /(?<=MESSAGE_CREATE:function\((\i)\){)/,
|
||||
|
|
|
@ -19,9 +19,9 @@
|
|||
import { definePluginSettings } from "@api/Settings";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
import { findStore } from "@webpack";
|
||||
|
||||
const MessageRequestStore = findByPropsLazy("getMessageRequestsCount");
|
||||
const MessageRequestStore = findStore("MessageRequestStore");
|
||||
|
||||
const settings = definePluginSettings({
|
||||
hideFriendRequestsCount: {
|
||||
|
@ -62,7 +62,16 @@ export default definePlugin({
|
|||
replace: "return 0;"
|
||||
}
|
||||
},
|
||||
// Message requests hook
|
||||
// New message requests hook
|
||||
{
|
||||
find: 'location:"use-message-requests-count"',
|
||||
predicate: () => settings.store.hideMessageRequestsCount,
|
||||
replacement: {
|
||||
match: /getNonChannelAckId\(\i\.\i\.MESSAGE_REQUESTS\).+?return /,
|
||||
replace: "$&0;"
|
||||
}
|
||||
},
|
||||
// Old message requests hook
|
||||
{
|
||||
find: "getMessageRequestsCount(){",
|
||||
predicate: () => settings.store.hideMessageRequestsCount,
|
||||
|
|
|
@ -36,7 +36,7 @@ export default definePlugin({
|
|||
}
|
||||
],
|
||||
shouldSkip(guildId: string, emoji: any) {
|
||||
if (emoji.type !== 1) {
|
||||
if (emoji.type !== "GUILD_EMOJI") {
|
||||
return false;
|
||||
}
|
||||
if (settings.store.shownEmojis === "onlyUnicode") {
|
||||
|
|
|
@ -33,7 +33,7 @@ interface URLReplacementRule {
|
|||
// Do not forget to add protocols to the ALLOWED_PROTOCOLS constant
|
||||
const UrlReplacementRules: Record<string, URLReplacementRule> = {
|
||||
spotify: {
|
||||
match: /^https:\/\/open\.spotify\.com\/(?:intl-[a-z]{2}\/)?(track|album|artist|playlist|user|episode)\/(.+)(?:\?.+?)?$/,
|
||||
match: /^https:\/\/open\.spotify\.com\/(track|album|artist|playlist|user|episode)\/(.+)(?:\?.+?)?$/,
|
||||
replace: (_, type, id) => `spotify://${type}/${id}`,
|
||||
description: "Open Spotify links in the Spotify app",
|
||||
shortlinkMatch: /^https:\/\/spotify\.link\/.+$/,
|
||||
|
|
|
@ -21,10 +21,8 @@ import { Flex } from "@components/Flex";
|
|||
import { InfoIcon, OwnerCrownIcon } from "@components/Icons";
|
||||
import { getUniqueUsername } from "@utils/discord";
|
||||
import { ModalCloseButton, ModalContent, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
||||
import { findByCodeLazy } from "@webpack";
|
||||
import { Clipboard, ContextMenuApi, FluxDispatcher, GuildMemberStore, GuildStore, i18n, Menu, PermissionsBits, ScrollerThin, Text, Tooltip, useEffect, UserStore, useState, useStateFromStores } from "@webpack/common";
|
||||
import { UnicodeEmoji } from "@webpack/types";
|
||||
import type { Guild, Role, User } from "discord-types/general";
|
||||
import { Clipboard, ContextMenuApi, FluxDispatcher, GuildMemberStore, GuildStore, i18n, Menu, PermissionsBits, Text, Tooltip, useEffect, UserStore, useState, useStateFromStores } from "@webpack/common";
|
||||
import type { Guild } from "discord-types/general";
|
||||
|
||||
import { settings } from "..";
|
||||
import { cl, getPermissionDescription, getPermissionString } from "../utils";
|
||||
|
@ -44,15 +42,15 @@ export interface RoleOrUserPermission {
|
|||
overwriteDeny?: bigint;
|
||||
}
|
||||
|
||||
type GetRoleIconData = (role: Role, size: number) => { customIconSrc?: string; unicodeEmoji?: UnicodeEmoji; };
|
||||
const getRoleIconData: GetRoleIconData = findByCodeLazy("convertSurrogateToName", "customIconSrc", "unicodeEmoji");
|
||||
|
||||
function getRoleIconSrc(role: Role) {
|
||||
const icon = getRoleIconData(role, 20);
|
||||
if (!icon) return;
|
||||
|
||||
const { customIconSrc, unicodeEmoji } = icon;
|
||||
return customIconSrc ?? unicodeEmoji?.url;
|
||||
function openRolesAndUsersPermissionsModal(permissions: Array<RoleOrUserPermission>, guild: Guild, header: string) {
|
||||
return openModal(modalProps => (
|
||||
<RolesAndUsersPermissions
|
||||
modalProps={modalProps}
|
||||
permissions={permissions}
|
||||
guild={guild}
|
||||
header={header}
|
||||
/>
|
||||
));
|
||||
}
|
||||
|
||||
function RolesAndUsersPermissionsComponent({ permissions, guild, modalProps, header }: { permissions: Array<RoleOrUserPermission>; guild: Guild; modalProps: ModalProps; header: string; }) {
|
||||
|
@ -88,34 +86,31 @@ function RolesAndUsersPermissionsComponent({ permissions, guild, modalProps, hea
|
|||
size={ModalSize.LARGE}
|
||||
>
|
||||
<ModalHeader>
|
||||
<Text className={cl("modal-title")} variant="heading-lg/semibold">{header} permissions:</Text>
|
||||
<Text className={cl("perms-title")} variant="heading-lg/semibold">{header} permissions:</Text>
|
||||
<ModalCloseButton onClick={modalProps.onClose} />
|
||||
</ModalHeader>
|
||||
|
||||
<ModalContent className={cl("modal-content")}>
|
||||
<ModalContent>
|
||||
{!selectedItem && (
|
||||
<div className={cl("modal-no-perms")}>
|
||||
<div className={cl("perms-no-perms")}>
|
||||
<Text variant="heading-lg/normal">No permissions to display!</Text>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{selectedItem && (
|
||||
<div className={cl("modal-container")}>
|
||||
<ScrollerThin className={cl("modal-list")} orientation="auto">
|
||||
<div className={cl("perms-container")}>
|
||||
<div className={cl("perms-list")}>
|
||||
{permissions.map((permission, index) => {
|
||||
const user: User | undefined = UserStore.getUser(permission.id ?? "");
|
||||
const role: Role | undefined = roles[permission.id ?? ""];
|
||||
const roleIconSrc = role != null ? getRoleIconSrc(role) : undefined;
|
||||
const user = UserStore.getUser(permission.id ?? "");
|
||||
const role = roles[permission.id ?? ""];
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cl("modal-list-item-btn")}
|
||||
<button
|
||||
className={cl("perms-list-item-btn")}
|
||||
onClick={() => selectItem(index)}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
>
|
||||
<div
|
||||
className={cl("modal-list-item", { "modal-list-item-active": selectedItemIndex === index })}
|
||||
className={cl("perms-list-item", { "perms-list-item-active": selectedItemIndex === index })}
|
||||
onContextMenu={e => {
|
||||
if (permission.type === PermissionType.Role)
|
||||
ContextMenuApi.openContextMenu(e, () => (
|
||||
|
@ -129,6 +124,7 @@ function RolesAndUsersPermissionsComponent({ permissions, guild, modalProps, hea
|
|||
ContextMenuApi.openContextMenu(e, () => (
|
||||
<UserContextMenu
|
||||
userId={permission.id!}
|
||||
onClose={modalProps.onClose}
|
||||
/>
|
||||
));
|
||||
}
|
||||
|
@ -136,19 +132,13 @@ function RolesAndUsersPermissionsComponent({ permissions, guild, modalProps, hea
|
|||
>
|
||||
{(permission.type === PermissionType.Role || permission.type === PermissionType.Owner) && (
|
||||
<span
|
||||
className={cl("modal-role-circle")}
|
||||
className={cl("perms-role-circle")}
|
||||
style={{ backgroundColor: role?.colorString ?? "var(--primary-300)" }}
|
||||
/>
|
||||
)}
|
||||
{permission.type === PermissionType.Role && roleIconSrc != null && (
|
||||
{permission.type === PermissionType.User && user !== undefined && (
|
||||
<img
|
||||
className={cl("modal-role-image")}
|
||||
src={roleIconSrc}
|
||||
/>
|
||||
)}
|
||||
{permission.type === PermissionType.User && user != null && (
|
||||
<img
|
||||
className={cl("modal-user-img")}
|
||||
className={cl("perms-user-img")}
|
||||
src={user.getAvatarURL(void 0, void 0, false)}
|
||||
/>
|
||||
)}
|
||||
|
@ -157,25 +147,28 @@ function RolesAndUsersPermissionsComponent({ permissions, guild, modalProps, hea
|
|||
permission.type === PermissionType.Role
|
||||
? role?.name ?? "Unknown Role"
|
||||
: permission.type === PermissionType.User
|
||||
? (user != null && getUniqueUsername(user)) ?? "Unknown User"
|
||||
? (user && getUniqueUsername(user)) ?? "Unknown User"
|
||||
: (
|
||||
<Flex style={{ gap: "0.2em", justifyItems: "center" }}>
|
||||
@owner
|
||||
<OwnerCrownIcon height={18} width={18} aria-hidden="true" />
|
||||
<OwnerCrownIcon
|
||||
height={18}
|
||||
width={18}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</Flex>
|
||||
)
|
||||
}
|
||||
</Text>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</ScrollerThin>
|
||||
<div className={cl("modal-divider")} />
|
||||
<ScrollerThin className={cl("modal-perms")} orientation="auto">
|
||||
</div>
|
||||
<div className={cl("perms-perms")}>
|
||||
{Object.entries(PermissionsBits).map(([permissionName, bit]) => (
|
||||
<div className={cl("modal-perms-item")}>
|
||||
<div className={cl("modal-perms-item-icon")}>
|
||||
<div className={cl("perms-perms-item")}>
|
||||
<div className={cl("perms-perms-item-icon")}>
|
||||
{(() => {
|
||||
const { permissions, overwriteAllow, overwriteDeny } = selectedItem;
|
||||
|
||||
|
@ -199,7 +192,7 @@ function RolesAndUsersPermissionsComponent({ permissions, guild, modalProps, hea
|
|||
</Tooltip>
|
||||
</div>
|
||||
))}
|
||||
</ScrollerThin>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</ModalContent>
|
||||
|
@ -215,7 +208,7 @@ function RoleContextMenu({ guild, roleId, onClose }: { guild: Guild; roleId: str
|
|||
aria-label="Role Options"
|
||||
>
|
||||
<Menu.MenuItem
|
||||
id={cl("copy-role-id")}
|
||||
id="vc-copy-role-id"
|
||||
label={i18n.Messages.COPY_ID_ROLE}
|
||||
action={() => {
|
||||
Clipboard.copy(roleId);
|
||||
|
@ -224,13 +217,14 @@ function RoleContextMenu({ guild, roleId, onClose }: { guild: Guild; roleId: str
|
|||
|
||||
{(settings.store as any).unsafeViewAsRole && (
|
||||
<Menu.MenuItem
|
||||
id={cl("view-as-role")}
|
||||
id="vc-pw-view-as-role"
|
||||
label={i18n.Messages.VIEW_AS_ROLE}
|
||||
action={() => {
|
||||
const role = GuildStore.getRole(guild.id, roleId);
|
||||
if (!role) return;
|
||||
|
||||
onClose();
|
||||
|
||||
FluxDispatcher.dispatch({
|
||||
type: "IMPERSONATE_UPDATE",
|
||||
guildId: guild.id,
|
||||
|
@ -241,14 +235,15 @@ function RoleContextMenu({ guild, roleId, onClose }: { guild: Guild; roleId: str
|
|||
}
|
||||
}
|
||||
});
|
||||
}}
|
||||
}
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</Menu.Menu>
|
||||
);
|
||||
}
|
||||
|
||||
function UserContextMenu({ userId }: { userId: string; }) {
|
||||
function UserContextMenu({ userId, onClose }: { userId: string; onClose: () => void; }) {
|
||||
return (
|
||||
<Menu.Menu
|
||||
navId={cl("user-context-menu")}
|
||||
|
@ -256,7 +251,7 @@ function UserContextMenu({ userId }: { userId: string; }) {
|
|||
aria-label="User Options"
|
||||
>
|
||||
<Menu.MenuItem
|
||||
id={cl("copy-user-id")}
|
||||
id="vc-copy-user-id"
|
||||
label={i18n.Messages.COPY_ID_USER}
|
||||
action={() => {
|
||||
Clipboard.copy(userId);
|
||||
|
@ -268,13 +263,4 @@ function UserContextMenu({ userId }: { userId: string; }) {
|
|||
|
||||
const RolesAndUsersPermissions = ErrorBoundary.wrap(RolesAndUsersPermissionsComponent);
|
||||
|
||||
export default function openRolesAndUsersPermissionsModal(permissions: Array<RoleOrUserPermission>, guild: Guild, header: string) {
|
||||
return openModal(modalProps => (
|
||||
<RolesAndUsersPermissions
|
||||
modalProps={modalProps}
|
||||
permissions={permissions}
|
||||
guild={guild}
|
||||
header={header}
|
||||
/>
|
||||
));
|
||||
}
|
||||
export default openRolesAndUsersPermissionsModal;
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { ExpandableHeader } from "@components/ExpandableHeader";
|
||||
import { classes } from "@utils/misc";
|
||||
import { filters, findBulk, proxyLazyWebpack } from "@webpack";
|
||||
import { findByProps } from "@webpack";
|
||||
import { i18n, PermissionsBits, Text, Tooltip, useMemo, UserStore } from "@webpack/common";
|
||||
import type { Guild, GuildMember } from "discord-types/general";
|
||||
|
||||
|
@ -29,65 +29,18 @@ import openRolesAndUsersPermissionsModal, { PermissionType, type RoleOrUserPermi
|
|||
|
||||
interface UserPermission {
|
||||
permission: string;
|
||||
roleName: string;
|
||||
roleColor: string;
|
||||
rolePosition: number;
|
||||
}
|
||||
|
||||
type UserPermissions = Array<UserPermission>;
|
||||
|
||||
const { RoleRootClasses, RoleClasses, RoleBorderClasses } = proxyLazyWebpack(() => {
|
||||
const [RoleRootClasses, RoleClasses, RoleBorderClasses] = findBulk(
|
||||
filters.byProps("root", "expandButton", "collapseButton"),
|
||||
filters.byProps("role", "roleCircle", "roleName"),
|
||||
filters.byProps("roleCircle", "dot", "dotBorderColor")
|
||||
) as Record<string, string>[];
|
||||
|
||||
return { RoleRootClasses, RoleClasses, RoleBorderClasses };
|
||||
});
|
||||
|
||||
interface FakeRoleProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
text: string;
|
||||
color: string;
|
||||
}
|
||||
|
||||
function FakeRole({ text, color, ...props }: FakeRoleProps) {
|
||||
return (
|
||||
<div {...props} className={classes(RoleClasses.role)}>
|
||||
<div className={RoleClasses.roleRemoveButton}>
|
||||
<span
|
||||
className={classes(RoleBorderClasses.roleCircle, RoleClasses.roleCircle)}
|
||||
style={{ backgroundColor: color }}
|
||||
/>
|
||||
</div>
|
||||
<div className={RoleClasses.roleName}>
|
||||
<Text
|
||||
className={RoleClasses.roleNameOverflow}
|
||||
variant="text-xs/medium"
|
||||
>
|
||||
{text}
|
||||
</Text>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
interface GrantedByTooltipProps {
|
||||
roleName: string;
|
||||
roleColor: string;
|
||||
}
|
||||
|
||||
function GrantedByTooltip({ roleName, roleColor }: GrantedByTooltipProps) {
|
||||
return (
|
||||
<>
|
||||
<Text variant="text-sm/medium">Granted By</Text>
|
||||
<FakeRole text={roleName} color={roleColor} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
const RoleRootClasses = findByProps("root", "showMoreButton", "collapseButton");
|
||||
const RoleClasses = findByProps("role", "roleCircle", "roleName");
|
||||
const RoleBorderClasses = findByProps("roleCircle", "dot", "dotBorderColor");
|
||||
|
||||
function UserPermissionsComponent({ guild, guildMember, forceOpen = false }: { guild: Guild; guildMember: GuildMember; forceOpen?: boolean; }) {
|
||||
const { permissionsSortOrder } = settings.use(["permissionsSortOrder"]);
|
||||
const stns = settings.use(["permissionsSortOrder"]);
|
||||
|
||||
const [rolePermissions, userPermissions] = useMemo(() => {
|
||||
const userPermissions: UserPermissions = [];
|
||||
|
@ -108,7 +61,6 @@ function UserPermissionsComponent({ guild, guildMember, forceOpen = false }: { g
|
|||
const OWNER = i18n.Messages.GUILD_OWNER || "Server Owner";
|
||||
userPermissions.push({
|
||||
permission: OWNER,
|
||||
roleName: "Owner",
|
||||
roleColor: "var(--primary-300)",
|
||||
rolePosition: Infinity
|
||||
});
|
||||
|
@ -117,11 +69,10 @@ function UserPermissionsComponent({ guild, guildMember, forceOpen = false }: { g
|
|||
sortUserRoles(userRoles);
|
||||
|
||||
for (const [permission, bit] of Object.entries(PermissionsBits)) {
|
||||
for (const { permissions, colorString, position, name } of userRoles) {
|
||||
for (const { permissions, colorString, position } of userRoles) {
|
||||
if ((permissions & bit) === bit) {
|
||||
userPermissions.push({
|
||||
permission: getPermissionString(permission),
|
||||
roleName: name,
|
||||
roleColor: colorString || "var(--primary-300)",
|
||||
rolePosition: position
|
||||
});
|
||||
|
@ -134,7 +85,7 @@ function UserPermissionsComponent({ guild, guildMember, forceOpen = false }: { g
|
|||
userPermissions.sort((a, b) => b.rolePosition - a.rolePosition);
|
||||
|
||||
return [rolePermissions, userPermissions];
|
||||
}, [permissionsSortOrder]);
|
||||
}, [stns.permissionsSortOrder]);
|
||||
|
||||
return (
|
||||
<ExpandableHeader
|
||||
|
@ -151,41 +102,46 @@ function UserPermissionsComponent({ guild, guildMember, forceOpen = false }: { g
|
|||
onDropDownClick={state => settings.store.defaultPermissionsDropdownState = !state}
|
||||
defaultState={settings.store.defaultPermissionsDropdownState}
|
||||
buttons={[
|
||||
<Tooltip text={`Sorting by ${permissionsSortOrder === PermissionsSortOrder.HighestRole ? "Highest Role" : "Lowest Role"}`}>
|
||||
(<Tooltip text={`Sorting by ${stns.permissionsSortOrder === PermissionsSortOrder.HighestRole ? "Highest Role" : "Lowest Role"}`}>
|
||||
{tooltipProps => (
|
||||
<div
|
||||
<button
|
||||
{...tooltipProps}
|
||||
className={cl("user-sortorder-btn")}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
className={cl("userperms-sortorder-btn")}
|
||||
onClick={() => {
|
||||
settings.store.permissionsSortOrder = permissionsSortOrder === PermissionsSortOrder.HighestRole ? PermissionsSortOrder.LowestRole : PermissionsSortOrder.HighestRole;
|
||||
stns.permissionsSortOrder = stns.permissionsSortOrder === PermissionsSortOrder.HighestRole ? PermissionsSortOrder.LowestRole : PermissionsSortOrder.HighestRole;
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 96 960 960"
|
||||
transform={permissionsSortOrder === PermissionsSortOrder.HighestRole ? "scale(1 1)" : "scale(1 -1)"}
|
||||
transform={stns.permissionsSortOrder === PermissionsSortOrder.HighestRole ? "scale(1 1)" : "scale(1 -1)"}
|
||||
>
|
||||
<path fill="var(--text-normal)" d="M440 896V409L216 633l-56-57 320-320 320 320-56 57-224-224v487h-80Z" />
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
)}
|
||||
</Tooltip>
|
||||
</Tooltip>)
|
||||
]}>
|
||||
{userPermissions.length > 0 && (
|
||||
<div className={classes(RoleRootClasses.root)}>
|
||||
{userPermissions.map(({ permission, roleColor, roleName }) => (
|
||||
<Tooltip
|
||||
text={<GrantedByTooltip roleName={roleName} roleColor={roleColor} />}
|
||||
tooltipClassName={cl("granted-by-container")}
|
||||
tooltipContentClassName={cl("granted-by-content")}
|
||||
{userPermissions.map(({ permission, roleColor }) => (
|
||||
<div className={classes(RoleClasses.role)}>
|
||||
<div className={RoleClasses.roleRemoveButton}>
|
||||
<span
|
||||
className={classes(RoleBorderClasses.roleCircle, RoleClasses.roleCircle)}
|
||||
style={{ backgroundColor: roleColor }}
|
||||
/>
|
||||
</div>
|
||||
<div className={RoleClasses.roleName}>
|
||||
<Text
|
||||
className={RoleClasses.roleNameOverflow}
|
||||
variant="text-xs/medium"
|
||||
>
|
||||
{tooltipProps => (
|
||||
<FakeRole {...tooltipProps} text={permission} color={roleColor} />
|
||||
)}
|
||||
</Tooltip>
|
||||
{permission}
|
||||
</Text>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue