mirror of
https://github.com/Equicord/Equicord.git
synced 2025-02-21 15:48:52 -05:00
Fixes
This commit is contained in:
parent
65bb12c33b
commit
76746c0c4a
17 changed files with 74 additions and 548 deletions
|
@ -21,7 +21,7 @@ An enhanced version of [Vencord](https://github.com/Vendicated/Vencord) by [Vend
|
||||||
- Request for plugins from Discord.
|
- Request for plugins from Discord.
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>Extra included plugins (61 additional plugins)</summary>
|
<summary>Extra included plugins (59 additional plugins)</summary>
|
||||||
|
|
||||||
- AllCallTimers by MaxHerbold and D3SOX
|
- AllCallTimers by MaxHerbold and D3SOX
|
||||||
- AltKrispSwitch by newwares
|
- AltKrispSwitch by newwares
|
||||||
|
@ -32,7 +32,6 @@ An enhanced version of [Vencord](https://github.com/Vendicated/Vencord) by [Vend
|
||||||
- BlockKrsip by D3SOX
|
- BlockKrsip by D3SOX
|
||||||
- BypassDND by Inbestigator
|
- BypassDND by Inbestigator
|
||||||
- CleanChannelName by AutumnVN
|
- CleanChannelName by AutumnVN
|
||||||
- ColorMessage by Kyuuhachi
|
|
||||||
- CopyUserMention by Cortex and castdrian
|
- CopyUserMention by Cortex and castdrian
|
||||||
- CustomAppIcons by Happy Enderman and SerStars
|
- CustomAppIcons by Happy Enderman and SerStars
|
||||||
- DNDWhilePlaying by thororen
|
- DNDWhilePlaying by thororen
|
||||||
|
@ -70,7 +69,6 @@ An enhanced version of [Vencord](https://github.com/Vendicated/Vencord) by [Vend
|
||||||
- Search by JacobTm and thororen
|
- Search by JacobTm and thororen
|
||||||
- SearchFix by Jaxx
|
- SearchFix by Jaxx
|
||||||
- Sekai Stickers by MaiKokain
|
- Sekai Stickers by MaiKokain
|
||||||
- ServerProfilesToolbox by D3SOX
|
|
||||||
- ShowBadgesInChat by Inbestigator and KrystalSkull
|
- ShowBadgesInChat by Inbestigator and KrystalSkull
|
||||||
- Slap by Korbo
|
- Slap by Korbo
|
||||||
- SoundBoardLogger by Moxxie, fres, echo, thororen
|
- SoundBoardLogger by Moxxie, fres, echo, thororen
|
||||||
|
|
|
@ -15,7 +15,7 @@ export default definePlugin({
|
||||||
{
|
{
|
||||||
find: ",setNoiseCancellation(",
|
find: ",setNoiseCancellation(",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(}\),)(.{1,2}\.default\.dispatch\({type:"AUDIO_SET_NOISE_SUPPRESSION",)/,
|
match: /(}\),)(.{1,2}\.\i\.dispatch\({type:"AUDIO_SET_NOISE_SUPPRESSION",)/,
|
||||||
replace: "$1!$self.shouldCancelSuppression(arguments)&&$2"
|
replace: "$1!$self.shouldCancelSuppression(arguments)&&$2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,14 +57,14 @@ export default definePlugin({
|
||||||
predicate: () => settings.store.dms,
|
predicate: () => settings.store.dms,
|
||||||
},
|
},
|
||||||
{ // Above DMs, keyboard nav
|
{ // Above DMs, keyboard nav
|
||||||
find: ".default.hasLibraryApplication()&&!",
|
find: ".hasLibraryApplication()&&!",
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
match: /\i\.Routes\.APPLICATION_STORE,/,
|
match: /\i\.\i\.APPLICATION_STORE,/,
|
||||||
replace: "/*$&*/",
|
replace: "/*$&*/",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
match: /\i\.Routes\.COLLECTIBLES_SHOP,/,
|
match: /\i\.\i\.COLLECTIBLES_SHOP,/,
|
||||||
replace: "/*$&*/",
|
replace: "/*$&*/",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -87,9 +87,9 @@ export default definePlugin({
|
||||||
predicate: () => settings.store.gift,
|
predicate: () => settings.store.gift,
|
||||||
},
|
},
|
||||||
{ // Emoji list
|
{ // Emoji list
|
||||||
find: "useEmojiGrid:function()",
|
find: /\i\.\i\i\.getEmojiUnavailableReason/,
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(\w+)=!\w+&&\w+.default.isEmojiCategoryNitroLocked\(\{[^}]*\}\);/,
|
match: /(\w+)=!\w+&&\w+.\i.isEmojiCategoryNitroLocked\(\{[^}]*\}\);/,
|
||||||
replace: "$&$1||"
|
replace: "$&$1||"
|
||||||
},
|
},
|
||||||
predicate: () => settings.store.emojiList,
|
predicate: () => settings.store.emojiList,
|
||||||
|
|
|
@ -255,7 +255,7 @@ export default definePlugin({
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Show all activities in the profile panel
|
// Show all activities in the profile panel
|
||||||
find: /.UserProfileTypes.PANEL,themeOverride:\i\i/,
|
find: /\i\.\i\i\.PANEL,themeOverride:\i\i,/,
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(?<=\(0,\i\.jsx\)\()\i\.\i(?=,{activity:.+?,user:\i,channelId:\i.id,)/,
|
match: /(?<=\(0,\i\.jsx\)\()\i\.\i(?=,{activity:.+?,user:\i,channelId:\i.id,)/,
|
||||||
replace: "$self.showAllActivitiesComponent"
|
replace: "$self.showAllActivitiesComponent"
|
||||||
|
|
|
@ -9,8 +9,8 @@ import "./style.css";
|
||||||
import ErrorBoundary from "@components/ErrorBoundary";
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { EquicordDevs } from "@utils/constants";
|
import { EquicordDevs } from "@utils/constants";
|
||||||
import definePlugin from "@utils/types";
|
import definePlugin from "@utils/types";
|
||||||
import { extractAndLoadChunksLazy, findByPropsLazy, findComponentByCodeLazy, findExportedComponentLazy, findStoreLazy } from "@webpack";
|
import { extractAndLoadChunksLazy, findComponentByCodeLazy, findExportedComponentLazy, findStoreLazy } from "@webpack";
|
||||||
import { useEffect, useState } from "@webpack/common";
|
import { NavigationRouter, useEffect, useState } from "@webpack/common";
|
||||||
|
|
||||||
|
|
||||||
const LinkButton = findExportedComponentLazy("LinkButton"); // let {route: e, selected: t, icon: n, iconClassName: a, interactiveClassName: s, text: r, children: o, locationState: d, onClick: f, className: p, role: m, "aria-posinset": C, "aria-setsize": g, ...E} = this.props;
|
const LinkButton = findExportedComponentLazy("LinkButton"); // let {route: e, selected: t, icon: n, iconClassName: a, interactiveClassName: s, text: r, children: o, locationState: d, onClick: f, className: p, role: m, "aria-posinset": C, "aria-setsize": g, ...E} = this.props;
|
||||||
|
@ -20,7 +20,6 @@ const QuestsComponent = findComponentByCodeLazy(".questsContainer"); // No nesse
|
||||||
const questsStore = findStoreLazy("QuestsStore");
|
const questsStore = findStoreLazy("QuestsStore");
|
||||||
|
|
||||||
const requireSettingsMenu = extractAndLoadChunksLazy(['name:"UserSettings"'], /createPromise:.{0,20}Promise\.all\((\[\i\.\i\(".+?"\).+?\])\).then\(\i\.bind\(\i,"(.+?)"\)\).{0,50}"UserSettings"/);
|
const requireSettingsMenu = extractAndLoadChunksLazy(['name:"UserSettings"'], /createPromise:.{0,20}Promise\.all\((\[\i\.\i\(".+?"\).+?\])\).then\(\i\.bind\(\i,"(.+?)"\)\).{0,50}"UserSettings"/);
|
||||||
const nav: NavigationSettings = findByPropsLazy("transitionTo", "transitionToGuild", "getHistory");
|
|
||||||
|
|
||||||
|
|
||||||
// Routes used in this plugin (in case someone wants to add new ones)
|
// Routes used in this plugin (in case someone wants to add new ones)
|
||||||
|
@ -82,14 +81,10 @@ const QuestButtonComponent = () => {
|
||||||
|
|
||||||
const redirectRoute = (ev: BeforeUnloadEvent) => {
|
const redirectRoute = (ev: BeforeUnloadEvent) => {
|
||||||
const paths = Array.from(routes.keys());
|
const paths = Array.from(routes.keys());
|
||||||
const path = nav.getHistory().location.pathname;
|
|
||||||
|
|
||||||
if (paths.includes(path)) {
|
ev.preventDefault();
|
||||||
const data = routes.get(path);
|
NavigationRouter.transitionTo("/quests/@me");
|
||||||
ev.preventDefault();
|
setTimeout(() => window.location.reload(), 0);
|
||||||
nav.transitionTo(data?.redirectTo ?? "/channels/@me");
|
|
||||||
setTimeout(() => window.location.reload(), 0);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
|
@ -110,23 +105,23 @@ export default definePlugin({
|
||||||
|
|
||||||
patches: [
|
patches: [
|
||||||
{ // Add new quest button
|
{ // Add new quest button
|
||||||
find: "\"discord-shop\"",
|
find: "\"discord-shop\"),",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /"discord-shop"\),/,
|
match: /"discord-shop"\),/,
|
||||||
replace: "$&,$self.QuestButtonComponent(),"
|
replace: "$&,$self.QuestButtonComponent(),"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ // Add new route
|
{ // Add new route
|
||||||
find: "Routes.MESSAGE_REQUESTS,render:",
|
find: ".MESSAGE_REQUESTS,render:",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /\((0,.{0,10}\.jsx\)\(.{0,10}\.default,){path:.{0,10}\.Routes\.MESSAGE_REQUESTS,.{0,100}?\),/,
|
match: /\((0,.{0,10}\.jsx\)\(.{0,10}\.\i,){path:.{0,10}\.\i\.MESSAGE_REQUESTS,.{0,100}?\),/,
|
||||||
replace: "$&...$self.routes.map(r => (($1r))),"
|
replace: "$&...$self.routes.map(r => (($1r))),"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
find: 'on("LAUNCH_APPLICATION"',
|
find: 'on("LAUNCH_APPLICATION"',
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /path:\[.{0,500}Routes\.MESSAGE_REQUESTS,/,
|
match: /path:\[.{0,500}\i\.MESSAGE_REQUESTS,/,
|
||||||
replace: "$&...$self.paths,"
|
replace: "$&...$self.paths,"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,62 +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 * as Styles from "@api/Styles";
|
|
||||||
import { makeRange } from "@components/PluginSettings/components";
|
|
||||||
import { Devs } from "@utils/constants";
|
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
|
||||||
import { findByPropsLazy } from "@webpack";
|
|
||||||
|
|
||||||
const AuthorStore = findByPropsLazy("useNullableMessageAuthor", "useNullableMessageAuthor");
|
|
||||||
|
|
||||||
import style from "./style.css?managed";
|
|
||||||
|
|
||||||
export const settings = definePluginSettings({
|
|
||||||
saturation: {
|
|
||||||
type: OptionType.SLIDER,
|
|
||||||
description: "Message color saturation",
|
|
||||||
markers: makeRange(0, 100, 10),
|
|
||||||
default: 20,
|
|
||||||
onChange() {
|
|
||||||
updateStyle();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
function updateStyle() {
|
|
||||||
(Styles.requireStyle(style).dom!.sheet!.cssRules[0] as CSSStyleRule)
|
|
||||||
.style.setProperty("--98-message-color-saturation", `${settings.store.saturation}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default definePlugin({
|
|
||||||
name: "ColorMessage",
|
|
||||||
description: "Colors message content with author's role color",
|
|
||||||
authors: [Devs.Kyuuhachi],
|
|
||||||
settings,
|
|
||||||
|
|
||||||
patches: [
|
|
||||||
{
|
|
||||||
find: 'default.Messages.MESSAGE_EDITED,")"',
|
|
||||||
replacement: {
|
|
||||||
match: /id:\(0,\w+.getMessageContentId\)\((\w+)\),/,
|
|
||||||
replace: '$&style:{"--98-message-color":$self.getMessageColor($1)},'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
getMessageColor(messageId: string) {
|
|
||||||
return AuthorStore.default(messageId).colorString;
|
|
||||||
},
|
|
||||||
|
|
||||||
start() {
|
|
||||||
Styles.enableStyle(style);
|
|
||||||
updateStyle();
|
|
||||||
},
|
|
||||||
stop() {
|
|
||||||
Styles.disableStyle(style);
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,12 +0,0 @@
|
||||||
/* stylelint-disable custom-property-pattern */
|
|
||||||
:root {
|
|
||||||
--98-message-color-saturation: /*DYNAMIC*/;
|
|
||||||
}
|
|
||||||
|
|
||||||
div[class*="messageContent_"] {
|
|
||||||
color: color-mix(
|
|
||||||
in lab,
|
|
||||||
var(--98-message-color, var(--text-normal)) calc(var(--98-message-color-saturation) * 1%),
|
|
||||||
var(--text-normal)
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -62,8 +62,8 @@ export default definePlugin({
|
||||||
{
|
{
|
||||||
find: "fetchRelationships(){",
|
find: "fetchRelationships(){",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /\.then\(e=>o\.default\.dispatch\({type:"LOAD_RELATIONSHIPS_SUCCESS",relationships:e\.body}\)/,
|
match: /\.then\(\i=>\i\.\i\.dispatch\({type:"LOAD_RELATIONSHIPS_SUCCESS",relationships:(\i\.body)}\);/,
|
||||||
replace: ".then(e=>{o.default.dispatch({type:\"LOAD_RELATIONSHIPS_SUCCESS\",relationships:e.body}); $self.getContacts(e.body)}"
|
replace: "$&$self.getContacts($2)}"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -8,6 +8,7 @@ import "./style.css";
|
||||||
|
|
||||||
import { DataStore } from "@api/index";
|
import { DataStore } from "@api/index";
|
||||||
import { definePluginSettings } from "@api/Settings";
|
import { definePluginSettings } from "@api/Settings";
|
||||||
|
import { classNameFactory } from "@api/Styles";
|
||||||
import { Flex } from "@components/Flex";
|
import { Flex } from "@components/Flex";
|
||||||
import { DeleteIcon } from "@components/Icons";
|
import { DeleteIcon } from "@components/Icons";
|
||||||
import { EquicordDevs } from "@utils/constants";
|
import { EquicordDevs } from "@utils/constants";
|
||||||
|
@ -15,31 +16,32 @@ import { Margins } from "@utils/margins";
|
||||||
import { useForceUpdater } from "@utils/react";
|
import { useForceUpdater } from "@utils/react";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { findByCodeLazy, findByPropsLazy } from "@webpack";
|
import { findByCodeLazy, findByPropsLazy } from "@webpack";
|
||||||
import { Button, ChannelStore, Forms, SearchableSelect, SelectedChannelStore, TabBar, TextInput, UserStore, UserUtils, useState } from "@webpack/common";
|
import { Button, ChannelStore, Forms, SearchableSelect, SelectedChannelStore, TabBar, TextInput, UserStore, useState } from "@webpack/common";
|
||||||
import { Message, User } from "discord-types/general/index.js";
|
import { Message, User } from "discord-types/general/index.js";
|
||||||
|
|
||||||
let keywordEntries: Array<{ regex: string, listIds: Array<string>, listType: ListType; }> = [];
|
let keywordEntries: Array<{ regex: string, listIds: Array<string>, listType: ListType; }> = [];
|
||||||
let currentUser: User;
|
let currentUser: User;
|
||||||
let keywordLog: Array<any> = [];
|
let keywordLog: Array<any> = [];
|
||||||
|
|
||||||
const MenuHeader = findByCodeLazy(".useInDesktopNotificationCenterExperiment)()?");
|
const MenuHeader = findByCodeLazy(".sv)()?(0,");
|
||||||
const Popout = findByCodeLazy("let{analyticsName:");
|
const Popout = findByCodeLazy(".loadingMore&&null==");
|
||||||
const recentMentionsPopoutClass = findByPropsLazy("recentMentionsPopout");
|
const recentMentionsPopoutClass = findByPropsLazy("recentMentionsPopout");
|
||||||
|
const createMessageRecord = findByCodeLazy("THREAD_CREATED?[]:(0,");
|
||||||
const KEYWORD_ENTRIES_KEY = "KeywordNotify_keywordEntries";
|
const KEYWORD_ENTRIES_KEY = "KeywordNotify_keywordEntries";
|
||||||
const KEYWORD_LOG_KEY = "KeywordNotify_log";
|
const KEYWORD_LOG_KEY = "KeywordNotify_log";
|
||||||
|
|
||||||
const { createMessageRecord } = findByPropsLazy("createMessageRecord", "updateMessageRecord");
|
const cl = classNameFactory("vc-keywordnotify-");
|
||||||
|
|
||||||
async function addKeywordEntry(updater: () => void) {
|
async function addKeywordEntry(forceUpdate: () => void) {
|
||||||
keywordEntries.push({ regex: "", listIds: [], listType: ListType.BlackList });
|
keywordEntries.push({ regex: "", listIds: [], listType: ListType.BlackList });
|
||||||
await DataStore.set(KEYWORD_ENTRIES_KEY, keywordEntries);
|
await DataStore.set(KEYWORD_ENTRIES_KEY, keywordEntries);
|
||||||
updater();
|
forceUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function removeKeywordEntry(idx: number, updater: () => void) {
|
async function removeKeywordEntry(idx: number, forceUpdate: () => void) {
|
||||||
keywordEntries.splice(idx, 1);
|
keywordEntries.splice(idx, 1);
|
||||||
await DataStore.set(KEYWORD_ENTRIES_KEY, keywordEntries);
|
await DataStore.set(KEYWORD_ENTRIES_KEY, keywordEntries);
|
||||||
updater();
|
forceUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
function safeMatchesRegex(s: string, r: string) {
|
function safeMatchesRegex(s: string, r: string) {
|
||||||
|
@ -50,7 +52,6 @@ function safeMatchesRegex(s: string, r: string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
enum ListType {
|
enum ListType {
|
||||||
BlackList = "BlackList",
|
BlackList = "BlackList",
|
||||||
Whitelist = "Whitelist"
|
Whitelist = "Whitelist"
|
||||||
|
@ -90,7 +91,7 @@ function Collapsible({ title, children }) {
|
||||||
onClick={() => setIsOpen(!isOpen)}
|
onClick={() => setIsOpen(!isOpen)}
|
||||||
look={Button.Looks.BLANK}
|
look={Button.Looks.BLANK}
|
||||||
size={Button.Sizes.ICON}
|
size={Button.Sizes.ICON}
|
||||||
className="keywordnotify-collapsible">
|
className={cl("collapsible")}>
|
||||||
<div style={{ display: "flex", alignItems: "center" }}>
|
<div style={{ display: "flex", alignItems: "center" }}>
|
||||||
<div style={{ marginLeft: "auto", color: "var(--text-muted)", paddingRight: "5px" }}>{isOpen ? "▼" : "▶"}</div>
|
<div style={{ marginLeft: "auto", color: "var(--text-muted)", paddingRight: "5px" }}>{isOpen ? "▼" : "▶"}</div>
|
||||||
<Forms.FormTitle tag="h4">{title}</Forms.FormTitle>
|
<Forms.FormTitle tag="h4">{title}</Forms.FormTitle>
|
||||||
|
@ -130,7 +131,7 @@ function ListedIds({ listIds, setListIds }) {
|
||||||
}}
|
}}
|
||||||
look={Button.Looks.BLANK}
|
look={Button.Looks.BLANK}
|
||||||
size={Button.Sizes.ICON}
|
size={Button.Sizes.ICON}
|
||||||
className="keywordnotify-delete">
|
className={cl("delete")}>
|
||||||
<DeleteIcon />
|
<DeleteIcon />
|
||||||
</Button>
|
</Button>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
@ -200,18 +201,18 @@ function KeywordEntries() {
|
||||||
onClick={() => removeKeywordEntry(i, update)}
|
onClick={() => removeKeywordEntry(i, update)}
|
||||||
look={Button.Looks.BLANK}
|
look={Button.Looks.BLANK}
|
||||||
size={Button.Sizes.ICON}
|
size={Button.Sizes.ICON}
|
||||||
className="keywordnotify-delete">
|
className={cl("delete")}>
|
||||||
<DeleteIcon />
|
<DeleteIcon />
|
||||||
</Button>
|
</Button>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Forms.FormDivider className={Margins.top8 + " " + Margins.bottom8} />
|
<Forms.FormDivider className={[Margins.top8, Margins.bottom8].join(" ")} />
|
||||||
<Forms.FormTitle tag="h5">Whitelist/Blacklist</Forms.FormTitle>
|
<Forms.FormTitle tag="h5">Whitelist/Blacklist</Forms.FormTitle>
|
||||||
<Flex flexDirection="row">
|
<Flex flexDirection="row">
|
||||||
<div style={{ flexGrow: 1 }}>
|
<div style={{ flexGrow: 1 }}>
|
||||||
<ListedIds listIds={values[i].listIds} setListIds={e => setListIds(i, e)} />
|
<ListedIds listIds={values[i].listIds} setListIds={e => setListIds(i, e)} />
|
||||||
</div>
|
</div>
|
||||||
</Flex>
|
</Flex>
|
||||||
<div className={Margins.top8 + " " + Margins.bottom8} />
|
<div className={[Margins.top8, Margins.bottom8].join(" ")} />
|
||||||
<Flex flexDirection="row">
|
<Flex flexDirection="row">
|
||||||
<Button onClick={() => {
|
<Button onClick={() => {
|
||||||
values[i].listIds.push("");
|
values[i].listIds.push("");
|
||||||
|
@ -254,7 +255,7 @@ export default definePlugin({
|
||||||
settings,
|
settings,
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
find: "}_dispatch(",
|
find: "Dispatch.dispatch(...) called without an action type",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /}_dispatch\((\i),\i\){/,
|
match: /}_dispatch\((\i),\i\){/,
|
||||||
replace: "$&$1=$self.modify($1);"
|
replace: "$&$1=$self.modify($1);"
|
||||||
|
@ -268,9 +269,9 @@ export default definePlugin({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
find: "InboxTab.TODOS?(",
|
find: "location:\"RecentsPopout\"",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /:\i&&(\i)===\i\.InboxTab\.TODOS.{1,50}setTab:(\i),onJump:(\i),closePopout:(\i)/,
|
match: /:(\i)===\i\.\i\.MENTIONS\?\(0,.+?setTab:(\i),onJump:(\i),badgeState:\i,closePopout:(\i)/,
|
||||||
replace: ": $1 === 5 ? $self.tryKeywordMenu($2, $3, $4) $&"
|
replace: ": $1 === 5 ? $self.tryKeywordMenu($2, $3, $4) $&"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -280,12 +281,19 @@ export default definePlugin({
|
||||||
match: /function (\i)\(\i\){let{message:\i,gotoMessage/,
|
match: /function (\i)\(\i\){let{message:\i,gotoMessage/,
|
||||||
replace: "$self.renderMsg = $1; $&"
|
replace: "$self.renderMsg = $1; $&"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
find: ".guildFilter:null",
|
||||||
|
replacement: {
|
||||||
|
match: /onClick:\(\)=>(\i\.\i\.deleteRecentMention\((\i)\.id\))/,
|
||||||
|
replace: "onClick: () => $2._keyword ? $self.deleteKeyword($2.id) : $1"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
async start() {
|
async start() {
|
||||||
keywordEntries = await DataStore.get(KEYWORD_ENTRIES_KEY) ?? [];
|
keywordEntries = await DataStore.get(KEYWORD_ENTRIES_KEY) ?? [];
|
||||||
currentUser = await UserUtils.getUser(UserStore.getCurrentUser().id);
|
currentUser = UserStore.getCurrentUser();
|
||||||
this.onUpdate = () => null;
|
this.onUpdate = () => null;
|
||||||
|
|
||||||
(await DataStore.get(KEYWORD_LOG_KEY) ?? []).map(e => JSON.parse(e)).forEach(e => {
|
(await DataStore.get(KEYWORD_LOG_KEY) ?? []).map(e => JSON.parse(e)).forEach(e => {
|
||||||
|
@ -353,6 +361,10 @@ export default definePlugin({
|
||||||
if (m == null || keywordLog.some(e => e.id === m.id))
|
if (m == null || keywordLog.some(e => e.id === m.id))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
DataStore.get(KEYWORD_LOG_KEY).then(log => {
|
||||||
|
DataStore.set(KEYWORD_LOG_KEY, [...log, JSON.stringify(m)]);
|
||||||
|
});
|
||||||
|
|
||||||
const thing = createMessageRecord(m);
|
const thing = createMessageRecord(m);
|
||||||
keywordLog.push(thing);
|
keywordLog.push(thing);
|
||||||
keywordLog.sort((a, b) => b.timestamp - a.timestamp);
|
keywordLog.sort((a, b) => b.timestamp - a.timestamp);
|
||||||
|
@ -363,6 +375,10 @@ export default definePlugin({
|
||||||
this.onUpdate();
|
this.onUpdate();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
deleteKeyword(id) {
|
||||||
|
keywordLog = keywordLog.filter(e => e.id !== id);
|
||||||
|
this.onUpdate();
|
||||||
|
},
|
||||||
|
|
||||||
keywordTabBar() {
|
keywordTabBar() {
|
||||||
return (
|
return (
|
||||||
|
@ -381,33 +397,23 @@ export default definePlugin({
|
||||||
|
|
||||||
const [tempLogs, setKeywordLog] = useState(keywordLog);
|
const [tempLogs, setKeywordLog] = useState(keywordLog);
|
||||||
this.onUpdate = () => {
|
this.onUpdate = () => {
|
||||||
const newLog = [...keywordLog];
|
const newLog = Array.from(keywordLog);
|
||||||
setKeywordLog(newLog);
|
setKeywordLog(newLog);
|
||||||
|
|
||||||
DataStore.set(KEYWORD_LOG_KEY, newLog.map(e => JSON.stringify(e)));
|
|
||||||
};
|
|
||||||
|
|
||||||
const onDelete = m => {
|
|
||||||
keywordLog = keywordLog.filter(e => e.id !== m.id);
|
|
||||||
this.onUpdate();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const messageRender = (e, t) => {
|
const messageRender = (e, t) => {
|
||||||
console.log(this);
|
e._keyword = true;
|
||||||
|
|
||||||
|
e.customRenderedContent = {
|
||||||
|
content: highlightKeywords(e.content, keywordEntries.map(e => e.regex))
|
||||||
|
};
|
||||||
|
|
||||||
const msg = this.renderMsg({
|
const msg = this.renderMsg({
|
||||||
message: e,
|
message: e,
|
||||||
gotoMessage: t,
|
gotoMessage: t,
|
||||||
dismissible: true
|
dismissible: true
|
||||||
});
|
});
|
||||||
|
|
||||||
if (msg == null)
|
|
||||||
return [null];
|
|
||||||
|
|
||||||
msg.props.children[0].props.children.props.onClick = () => onDelete(e);
|
|
||||||
msg.props.children[1].props.children[1].props.message.customRenderedContent = {
|
|
||||||
content: highlightKeywords(e.content, keywordEntries.map(e => e.regex))
|
|
||||||
};
|
|
||||||
|
|
||||||
return [msg];
|
return [msg];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -420,7 +426,7 @@ export default definePlugin({
|
||||||
channel={channel}
|
channel={channel}
|
||||||
onJump={onJump}
|
onJump={onJump}
|
||||||
onFetch={() => null}
|
onFetch={() => null}
|
||||||
onCloseMessage={onDelete}
|
onCloseMessage={this.deleteKeyword}
|
||||||
loadMore={() => null}
|
loadMore={() => null}
|
||||||
messages={tempLogs}
|
messages={tempLogs}
|
||||||
renderEmptyState={() => null}
|
renderEmptyState={() => null}
|
||||||
|
|
|
@ -38,7 +38,7 @@ const { Spinner } = proxyLazy(() => Forms as any as {
|
||||||
SpinnerTypes: typeof SpinnerTypes;
|
SpinnerTypes: typeof SpinnerTypes;
|
||||||
});
|
});
|
||||||
|
|
||||||
const ChannelMessage = findComponentByCodeLazy("renderSimpleAccessories)") as ComponentType<any>;
|
const ChannelMessage = findComponentByCodeLazy("childrenExecutedCommand:", ".hideAccessories");
|
||||||
|
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "MessageLinkTooltip",
|
name: "MessageLinkTooltip",
|
||||||
|
@ -49,7 +49,7 @@ export default definePlugin({
|
||||||
{
|
{
|
||||||
find: ',className:"channelMention",children:[',
|
find: ',className:"channelMention",children:[',
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(?<=\.jsxs\)\()(\i\.default)/,
|
match: /(?<=\.jsxs\)\()(\i\.\i)/,
|
||||||
replace: "$self.wrapComponent(arguments[0], $1)"
|
replace: "$self.wrapComponent(arguments[0], $1)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,190 +0,0 @@
|
||||||
/*
|
|
||||||
* Vencord, a Discord client mod
|
|
||||||
* Copyright (c) 2024 Vendicated and contributors
|
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { Devs } from "@utils/constants";
|
|
||||||
import definePlugin from "@utils/types";
|
|
||||||
import { findByPropsLazy, findComponentByCodeLazy } from "@webpack";
|
|
||||||
import {
|
|
||||||
Button,
|
|
||||||
Clipboard,
|
|
||||||
GuildMemberStore,
|
|
||||||
Text,
|
|
||||||
Toasts,
|
|
||||||
UserProfileStore,
|
|
||||||
UserStore
|
|
||||||
} from "@webpack/common";
|
|
||||||
import { GuildMember } from "discord-types/general";
|
|
||||||
|
|
||||||
const SummaryItem = findComponentByCodeLazy("borderType", "showBorder", "hideDivider");
|
|
||||||
|
|
||||||
interface SavedProfile {
|
|
||||||
nick: string | null;
|
|
||||||
pronouns: string | null;
|
|
||||||
bio: string | null;
|
|
||||||
themeColors: number[] | undefined;
|
|
||||||
banner: string | undefined;
|
|
||||||
avatar: string | undefined;
|
|
||||||
profileEffectId: string | undefined;
|
|
||||||
avatarDecoration: string | undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
const savedProfile: SavedProfile = {
|
|
||||||
nick: null,
|
|
||||||
pronouns: null,
|
|
||||||
bio: null,
|
|
||||||
themeColors: undefined,
|
|
||||||
banner: undefined,
|
|
||||||
avatar: undefined,
|
|
||||||
profileEffectId: undefined,
|
|
||||||
avatarDecoration: undefined,
|
|
||||||
};
|
|
||||||
|
|
||||||
const {
|
|
||||||
setPendingAvatar,
|
|
||||||
setPendingBanner,
|
|
||||||
setPendingBio,
|
|
||||||
setPendingNickname,
|
|
||||||
setPendingPronouns,
|
|
||||||
setPendingThemeColors,
|
|
||||||
setPendingProfileEffectId,
|
|
||||||
setPendingAvatarDecoration,
|
|
||||||
}: {
|
|
||||||
setPendingAvatar: (a: string | undefined) => void;
|
|
||||||
setPendingBanner: (a: string | undefined) => void;
|
|
||||||
setPendingBio: (a: string | null) => void;
|
|
||||||
setPendingNickname: (a: string | null) => void;
|
|
||||||
setPendingPronouns: (a: string | null) => void;
|
|
||||||
setPendingThemeColors: (a: number[] | undefined) => void;
|
|
||||||
setPendingProfileEffectId: (a: string | undefined) => void;
|
|
||||||
setPendingAvatarDecoration: (a: string | undefined) => void;
|
|
||||||
} = findByPropsLazy("setPendingNickname", "setPendingPronouns");
|
|
||||||
|
|
||||||
export default definePlugin({
|
|
||||||
name: "ServerProfilesToolbox",
|
|
||||||
authors: [Devs.D3SOX],
|
|
||||||
description: "Adds a copy/paste/reset button to the server profiles editor",
|
|
||||||
|
|
||||||
patchServerProfiles({ guildId }: { guildId: string; }) {
|
|
||||||
const currentUser = UserStore.getCurrentUser();
|
|
||||||
const premiumType = currentUser.premiumType ?? 0;
|
|
||||||
|
|
||||||
const copy = () => {
|
|
||||||
const profile = UserProfileStore.getGuildMemberProfile(currentUser.id, guildId);
|
|
||||||
const nick = GuildMemberStore.getNick(guildId, currentUser.id);
|
|
||||||
const selfMember = GuildMemberStore.getMember(guildId, currentUser.id) as GuildMember & { avatarDecoration: string | undefined; };
|
|
||||||
savedProfile.nick = nick ?? "";
|
|
||||||
savedProfile.pronouns = profile.pronouns;
|
|
||||||
savedProfile.bio = profile.bio;
|
|
||||||
savedProfile.themeColors = profile.themeColors;
|
|
||||||
savedProfile.banner = profile.banner;
|
|
||||||
savedProfile.avatar = selfMember.avatar;
|
|
||||||
savedProfile.profileEffectId = profile.profileEffectId;
|
|
||||||
savedProfile.avatarDecoration = selfMember.avatarDecoration;
|
|
||||||
};
|
|
||||||
|
|
||||||
const paste = () => {
|
|
||||||
setPendingNickname(savedProfile.nick);
|
|
||||||
setPendingPronouns(savedProfile.pronouns);
|
|
||||||
if (premiumType === 2) {
|
|
||||||
setPendingBio(savedProfile.bio);
|
|
||||||
setPendingThemeColors(savedProfile.themeColors);
|
|
||||||
setPendingBanner(savedProfile.banner);
|
|
||||||
setPendingAvatar(savedProfile.avatar);
|
|
||||||
setPendingProfileEffectId(savedProfile.profileEffectId);
|
|
||||||
setPendingAvatarDecoration(savedProfile.avatarDecoration);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const reset = () => {
|
|
||||||
setPendingNickname(null);
|
|
||||||
setPendingPronouns("");
|
|
||||||
if (premiumType === 2) {
|
|
||||||
setPendingBio(null);
|
|
||||||
setPendingThemeColors([]);
|
|
||||||
setPendingBanner(undefined);
|
|
||||||
setPendingAvatar(undefined);
|
|
||||||
setPendingProfileEffectId(undefined);
|
|
||||||
setPendingAvatarDecoration(undefined);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const copyToClipboard = () => {
|
|
||||||
copy();
|
|
||||||
Clipboard.copy(JSON.stringify(savedProfile));
|
|
||||||
};
|
|
||||||
|
|
||||||
const pasteFromClipboard = async () => {
|
|
||||||
try {
|
|
||||||
const clip = await navigator.clipboard.readText();
|
|
||||||
if (!clip) {
|
|
||||||
Toasts.show({
|
|
||||||
message: "Clipboard is empty",
|
|
||||||
type: Toasts.Type.FAILURE,
|
|
||||||
id: Toasts.genId(),
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const clipboardProfile: SavedProfile = JSON.parse(clip);
|
|
||||||
|
|
||||||
if (!("nick" in clipboardProfile)) {
|
|
||||||
Toasts.show({
|
|
||||||
message: "Data is not in correct format",
|
|
||||||
type: Toasts.Type.FAILURE,
|
|
||||||
id: Toasts.genId(),
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Object.assign(savedProfile, JSON.parse(clip));
|
|
||||||
paste();
|
|
||||||
} catch (e) {
|
|
||||||
Toasts.show({
|
|
||||||
message: `Failed to read clipboard data: ${e}`,
|
|
||||||
type: Toasts.Type.FAILURE,
|
|
||||||
id: Toasts.genId(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return <SummaryItem title="Server Profiles Toolbox" hideDivider={false} forcedDivider>
|
|
||||||
<div style={{ display: "flex", alignItems: "center", flexDirection: "column", gap: "5px" }}>
|
|
||||||
<Text variant="text-md/normal">
|
|
||||||
Use the following buttons to mange the currently selected server
|
|
||||||
</Text>
|
|
||||||
<div style={{ display: "flex", gap: "5px" }}>
|
|
||||||
<Button onClick={copy}>
|
|
||||||
Copy profile
|
|
||||||
</Button>
|
|
||||||
<Button onClick={paste}>
|
|
||||||
Paste profile
|
|
||||||
</Button>
|
|
||||||
<Button onClick={reset}>
|
|
||||||
Reset profile
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<div style={{ display: "flex", gap: "5px" }}>
|
|
||||||
<Button onClick={copyToClipboard}>
|
|
||||||
Copy to clipboard
|
|
||||||
</Button>
|
|
||||||
<Button onClick={pasteFromClipboard}>
|
|
||||||
Paste from clipboard
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</SummaryItem>;
|
|
||||||
},
|
|
||||||
|
|
||||||
patches: [
|
|
||||||
{
|
|
||||||
find: ".PROFILE_CUSTOMIZATION_GUILD_SELECT_TITLE",
|
|
||||||
replacement: {
|
|
||||||
match: /return\(0(.{10,350})\}\)\}\)\}/,
|
|
||||||
replace: "return [(0$1})}),$self.patchServerProfiles(e)]}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
|
|
||||||
});
|
|
|
@ -1,193 +0,0 @@
|
||||||
/*
|
|
||||||
* Vencord, a Discord client mod
|
|
||||||
* Copyright (c) 2023 Vendicated and contributors
|
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { Flex } from "@components/Flex";
|
|
||||||
import { Margins } from "@utils/margins";
|
|
||||||
import { classes } from "@utils/misc";
|
|
||||||
import { closeModal, ModalCloseButton,ModalContent, ModalHeader, ModalRoot, openModal } from "@utils/modal";
|
|
||||||
import { LazyComponent } from "@utils/react";
|
|
||||||
import { find, findByPropsLazy } from "@webpack";
|
|
||||||
import { Button, Clickable, Forms, GuildStore, PermissionsBits, PermissionStore, Popout, SearchableSelect, showToast, Text, TextInput, Toasts, useMemo, UserStore, useState } from "@webpack/common";
|
|
||||||
import { Guild } from "discord-types/general";
|
|
||||||
import { HtmlHTMLAttributes } from "react";
|
|
||||||
|
|
||||||
import { cl, getEmojiUrl,SoundEvent } from "../utils";
|
|
||||||
|
|
||||||
export function openCloneSoundModal(item) {
|
|
||||||
const key = openModal(props =>
|
|
||||||
<ModalRoot {...props}>
|
|
||||||
<CloneSoundModal item={item} closeModal={() => closeModal(key)} />
|
|
||||||
</ModalRoot>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Thanks https://github.com/Vendicated/Vencord/blob/ea11f2244fde469ce308f8a4e7224430be62f8f1/src/plugins/emoteCloner/index.tsx#L173-L177
|
|
||||||
const getFontSize = (s: string, small: boolean = false) => {
|
|
||||||
const sizes = [20, 20, 18, 18, 16, 14, 12];
|
|
||||||
return sizes[s.length + (small ? 1 : 0)] ?? 8;
|
|
||||||
};
|
|
||||||
|
|
||||||
function GuildAcronym({ acronym, small, style = {} }) {
|
|
||||||
return (
|
|
||||||
<Flex style={{ alignItems: "center", justifyContent: "center", overflow: "hidden", fontSize: getFontSize(acronym, small), ...style }}>
|
|
||||||
<Text>{acronym}</Text>
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function CustomInput({ children, className = "", ...props }: HtmlHTMLAttributes<HTMLDivElement>) {
|
|
||||||
return <div className={classes(cl("clone-input"), className)} {...props}>
|
|
||||||
{children}
|
|
||||||
</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const EmojiPicker = LazyComponent(() => find(e => e.default?.type?.render?.toString?.().includes?.(".updateNewlyAddedLastSeen)")).default);
|
|
||||||
|
|
||||||
const sounds = findByPropsLazy("uploadSound", "updateSound");
|
|
||||||
|
|
||||||
export function CloneSoundModal({ item, closeModal }: { item: SoundEvent, closeModal: () => void; }) {
|
|
||||||
const ownedGuilds = useMemo(() => {
|
|
||||||
return Object.values(GuildStore.getGuilds()).filter(guild =>
|
|
||||||
guild.ownerId === UserStore.getCurrentUser().id ||
|
|
||||||
(PermissionStore.getGuildPermissions({ id: guild.id }) & PermissionsBits.CREATE_GUILD_EXPRESSIONS) === PermissionsBits.CREATE_GUILD_EXPRESSIONS);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const [selectedGuild, setSelectedGuild] = useState<Guild | null>(null);
|
|
||||||
const [soundName, setSoundName] = useState<string | undefined>(undefined);
|
|
||||||
const [soundEmoji, setSoundEmoji] = useState<any | undefined>(undefined);
|
|
||||||
const [loadingButton, setLoadingButton] = useState<boolean>(false);
|
|
||||||
const [show, setShow] = useState(false);
|
|
||||||
|
|
||||||
const isEmojiValid = soundEmoji?.guildId ? soundEmoji.guildId === selectedGuild?.id : true;
|
|
||||||
|
|
||||||
const styles = {
|
|
||||||
selected: { height: "24px", width: "24px" },
|
|
||||||
nonselected: { width: "36px", height: "36px", marginTop: "4px" }
|
|
||||||
};
|
|
||||||
const getStyle = key => key === "selected" ? styles.selected : styles.nonselected;
|
|
||||||
|
|
||||||
function onSelectEmoji(emoji) {
|
|
||||||
setShow(false);
|
|
||||||
setSoundEmoji(emoji);
|
|
||||||
}
|
|
||||||
|
|
||||||
return <>
|
|
||||||
<ModalHeader>
|
|
||||||
<Flex style={{ width: "100%", justifyContent: "center" }}>
|
|
||||||
<Text variant="heading-lg/semibold" style={{ flexGrow: 1 }}>Clone Sound</Text>
|
|
||||||
<ModalCloseButton onClick={closeModal} />
|
|
||||||
</Flex>
|
|
||||||
</ModalHeader>
|
|
||||||
<ModalContent>
|
|
||||||
<Forms.FormTitle className={Margins.top16}>Cloning Sound</Forms.FormTitle>
|
|
||||||
<CustomInput style={{ display: "flex", flexDirection: "row", gap: "10px", alignItems: "center" }} className={Margins.bottom16}>
|
|
||||||
<img src={getEmojiUrl(item.emoji)} width="24" height="24" />
|
|
||||||
<Text>{item.soundId}</Text>
|
|
||||||
</CustomInput>
|
|
||||||
<Forms.FormTitle required={true} aria-required="true">Add to server:</Forms.FormTitle>
|
|
||||||
<SearchableSelect
|
|
||||||
options={
|
|
||||||
ownedGuilds.map(guild => ({
|
|
||||||
label: guild.name,
|
|
||||||
value: guild
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
placeholder="Select a server"
|
|
||||||
value={selectedGuild ? ({
|
|
||||||
label: selectedGuild.name,
|
|
||||||
value: selectedGuild,
|
|
||||||
key: "selected"
|
|
||||||
}) : undefined}
|
|
||||||
|
|
||||||
onChange={v => setSelectedGuild(v)}
|
|
||||||
closeOnSelect={true}
|
|
||||||
renderOptionPrefix={v => v ? (
|
|
||||||
v.value.icon ?
|
|
||||||
<img width={36} height={36} src={v.value.getIconURL(96, true)} style={{ borderRadius: "50%", ...getStyle(v.key) }} /> :
|
|
||||||
<GuildAcronym acronym={v.value.acronym} style={getStyle(v.key)} small={v.key === "selected"} />
|
|
||||||
) : null}
|
|
||||||
/>
|
|
||||||
<Flex flexDirection="row" style={{ gap: "10px", justifyContent: "space-between" }} className={classes(Margins.top16, Margins.bottom16)}>
|
|
||||||
<div style={{ flex: 1 }}>
|
|
||||||
<Forms.FormTitle required={true} aria-required="true">Sound Name</Forms.FormTitle>
|
|
||||||
<TextInput value={soundName} onChange={v => setSoundName(v)} placeholder="Sound Name" />
|
|
||||||
</div>
|
|
||||||
<div style={{ flex: 1 }}>
|
|
||||||
<Forms.FormTitle>Related Emoji</Forms.FormTitle>
|
|
||||||
<Popout
|
|
||||||
position="bottom"
|
|
||||||
align="right"
|
|
||||||
animation={Popout.Animation.NONE}
|
|
||||||
shouldShow={show}
|
|
||||||
onRequestClose={() => setShow(false)}
|
|
||||||
renderPopout={() => <EmojiPicker pickerIntention={2} channel={{ getGuildId: () => selectedGuild?.id }} onSelectEmoji={onSelectEmoji} />}
|
|
||||||
>
|
|
||||||
{() => (
|
|
||||||
<Clickable onClick={() => setShow(v => !v)}>
|
|
||||||
<CustomInput style={{ display: "flex", flexDirection: "row", gap: "10px", alignItems: "center", cursor: "pointer" }}>
|
|
||||||
{soundEmoji ?
|
|
||||||
<>
|
|
||||||
<img src={getEmojiUrl({ name: soundEmoji.surrogates, id: soundEmoji.id })} width="24" height="24" style={{ cursor: "pointer" }} />
|
|
||||||
<Text style={{ color: "var(--text-muted)", cursor: "pointer" }}>:{soundEmoji.name ? soundEmoji.name.split("~")[0] : soundEmoji.uniqueName}:</Text>
|
|
||||||
</> :
|
|
||||||
<>
|
|
||||||
<img src={getEmojiUrl({ name: "😊" })} width="24" height="24" style={{ filter: "grayscale(100%)", cursor: "pointer" }} />
|
|
||||||
<Text style={{ color: "var(--text-muted)", cursor: "pointer" }}>Click to Select</Text>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
</CustomInput>
|
|
||||||
</Clickable>
|
|
||||||
)}
|
|
||||||
</Popout>
|
|
||||||
</div>
|
|
||||||
</Flex>
|
|
||||||
{!isEmojiValid && <Forms.FormText style={{ color: "var(--text-danger)" }} className={Margins.bottom16}>You can't use that emoji in that server</Forms.FormText>}
|
|
||||||
<Button onClick={() => {
|
|
||||||
setLoadingButton(true);
|
|
||||||
fetch(`https://cdn.discordapp.com/soundboard-sounds/${item.soundId}`).then(function (response) {
|
|
||||||
if (!response.body) {
|
|
||||||
setLoadingButton(false);
|
|
||||||
showToast("Error fetching the sound", Toasts.Type.FAILURE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
response.body.getReader().read().then(function (result) {
|
|
||||||
if (!result.value) {
|
|
||||||
setLoadingButton(false);
|
|
||||||
showToast("Error reading the sound content", Toasts.Type.FAILURE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return btoa(String.fromCharCode(...result.value));
|
|
||||||
}).then(function (b64) {
|
|
||||||
|
|
||||||
sounds.uploadSound({
|
|
||||||
guildId: selectedGuild?.id,
|
|
||||||
name: soundName,
|
|
||||||
sound: `data:audio/ogg;base64,${b64}`,
|
|
||||||
...(soundEmoji.id ? { emojiId: soundEmoji.id } : { emojiName: soundEmoji.surrogates }),
|
|
||||||
volume: 1
|
|
||||||
}).then(() => {
|
|
||||||
showToast(`Sound added to ${selectedGuild?.name}`, Toasts.Type.SUCCESS);
|
|
||||||
closeModal();
|
|
||||||
}).catch(() => {
|
|
||||||
setLoadingButton(false);
|
|
||||||
showToast("Error while adding sound", Toasts.Type.FAILURE);
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
}).catch(e => {
|
|
||||||
setLoadingButton(false);
|
|
||||||
showToast("Error fetching the sound", Toasts.Type.FAILURE);
|
|
||||||
return;
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
disabled={(!(selectedGuild && soundName && isEmojiValid)) || loadingButton}
|
|
||||||
size={Button.Sizes.MEDIUM}
|
|
||||||
style={{ width: "100%" }}
|
|
||||||
className={Margins.bottom16}>Add to Server</Button>
|
|
||||||
</ModalContent>
|
|
||||||
</>;
|
|
||||||
}
|
|
|
@ -13,8 +13,7 @@ import { Button, Clickable, ContextMenuApi, FluxDispatcher, Forms, Menu, Text, T
|
||||||
import { User } from "discord-types/general";
|
import { User } from "discord-types/general";
|
||||||
|
|
||||||
import { clearLoggedSounds, getLoggedSounds } from "../store";
|
import { clearLoggedSounds, getLoggedSounds } from "../store";
|
||||||
import { addListener, AvatarStyles, cl, downloadAudio, getEmojiUrl, getSoundboardVolume, playSound, removeListener, SoundLogEntry, UserSummaryItem } from "../utils";
|
import { addListener, AvatarStyles, cl, downloadAudio, getEmojiUrl, playSound, removeListener, SoundLogEntry, UserSummaryItem } from "../utils";
|
||||||
import { openCloneSoundModal } from "./CloneSoundModal";
|
|
||||||
import { openMoreUsersModal } from "./MoreUsersModal";
|
import { openMoreUsersModal } from "./MoreUsersModal";
|
||||||
import { openUserModal } from "./UserModal";
|
import { openUserModal } from "./UserModal";
|
||||||
|
|
||||||
|
@ -100,13 +99,6 @@ export default function SoundBoardLog({ data, closeModal }) {
|
||||||
navId="soundboardlogger-sound-menu"
|
navId="soundboardlogger-sound-menu"
|
||||||
onClose={() => FluxDispatcher.dispatch({ type: "CONTEXT_MENU_CLOSE" })}
|
onClose={() => FluxDispatcher.dispatch({ type: "CONTEXT_MENU_CLOSE" })}
|
||||||
>
|
>
|
||||||
<Menu.MenuGroup label="Extra buttons">
|
|
||||||
<Menu.MenuItem
|
|
||||||
id={label("clone")}
|
|
||||||
label="Clone sound"
|
|
||||||
action={() => openCloneSoundModal(item)}
|
|
||||||
/>
|
|
||||||
</Menu.MenuGroup>
|
|
||||||
</Menu.Menu>
|
</Menu.Menu>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -164,7 +156,7 @@ export default function SoundBoardLog({ data, closeModal }) {
|
||||||
<Flex flexDirection="row" className={cl("sound-buttons")}>
|
<Flex flexDirection="row" className={cl("sound-buttons")}>
|
||||||
<Button color={Button.Colors.PRIMARY} size={Button.Sizes.SMALL} onClick={() => downloadAudio(item.soundId)}>Download</Button>
|
<Button color={Button.Colors.PRIMARY} size={Button.Sizes.SMALL} onClick={() => downloadAudio(item.soundId)}>Download</Button>
|
||||||
<Button color={Button.Colors.GREEN} size={Button.Sizes.SMALL} onClick={() => copyWithToast(item.soundId, "ID copied to clipboard!")}>Copy ID</Button>
|
<Button color={Button.Colors.GREEN} size={Button.Sizes.SMALL} onClick={() => copyWithToast(item.soundId, "ID copied to clipboard!")}>Copy ID</Button>
|
||||||
<Tooltip text={`Soundboard volume: ${Math.floor(getSoundboardVolume())}%`}>
|
<Tooltip text={"Soundboard volume: currently broken"}>
|
||||||
{({ onMouseEnter, onMouseLeave }) =>
|
{({ onMouseEnter, onMouseLeave }) =>
|
||||||
<Button color={Button.Colors.BRAND} size={Button.Sizes.SMALL} onClick={() => playSound(item.soundId)} onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave}>Play Sound</Button>
|
<Button color={Button.Colors.BRAND} size={Button.Sizes.SMALL} onClick={() => playSound(item.soundId)} onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave}>Play Sound</Button>
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,14 +39,9 @@ export function getEmojiUrl(emoji) {
|
||||||
return emoji.id ? `https://cdn.discordapp.com/emojis/${emoji.id}.png?size=32` : getURL(emoji.name);
|
return emoji.id ? `https://cdn.discordapp.com/emojis/${emoji.id}.png?size=32` : getURL(emoji.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
const v1 = findByPropsLazy("amplitudeToPerceptual");
|
|
||||||
const v2 = findByPropsLazy("getAmplitudinalSoundboardVolume");
|
|
||||||
|
|
||||||
export const getSoundboardVolume = () => v1.amplitudeToPerceptual(v2.getAmplitudinalSoundboardVolume());
|
|
||||||
|
|
||||||
export const playSound = id => {
|
export const playSound = id => {
|
||||||
const audio = new Audio(`https://cdn.discordapp.com/soundboard-sounds/${id}`);
|
const audio = new Audio(`https://cdn.discordapp.com/soundboard-sounds/${id}`);
|
||||||
audio.volume = getSoundboardVolume() / 100;
|
audio.volume = 75 / 100;
|
||||||
audio.play();
|
audio.play();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -62,9 +62,6 @@ function Watching({ userIds, guildId }: WatchingProps): JSX.Element {
|
||||||
}
|
}
|
||||||
|
|
||||||
const ApplicationStreamingStore = findStoreLazy("ApplicationStreamingStore");
|
const ApplicationStreamingStore = findStoreLazy("ApplicationStreamingStore");
|
||||||
const { encodeStreamKey }: {
|
|
||||||
encodeStreamKey: (any) => string;
|
|
||||||
} = findByPropsLazy("encodeStreamKey");
|
|
||||||
|
|
||||||
const UserSummaryItem = findComponentByCodeLazy("defaultRenderUser", "showDefaultAvatarsForNullUsers");
|
const UserSummaryItem = findComponentByCodeLazy("defaultRenderUser", "showDefaultAvatarsForNullUsers");
|
||||||
const AvatarStyles = findByPropsLazy("moreUsers", "emptyUser", "avatarContainer", "clickableAvatar");
|
const AvatarStyles = findByPropsLazy("moreUsers", "emptyUser", "avatarContainer", "clickableAvatar");
|
||||||
|
@ -80,8 +77,8 @@ export default definePlugin({
|
||||||
{
|
{
|
||||||
find: ".Masks.STATUS_SCREENSHARE,width:32",
|
find: ".Masks.STATUS_SCREENSHARE,width:32",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /default:function\(\)\{return (\i)\}/,
|
match: /(\i):function\(\)\{return (\i)\}/,
|
||||||
replace: "default:function(){return $self.component({OriginalComponent:$1})}"
|
replace: "$1:function(){return $self.component({OriginalComponent:$2})}"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -98,7 +95,7 @@ export default definePlugin({
|
||||||
|
|
||||||
if (!stream) return <div {...props}>{props.children}</div>;
|
if (!stream) return <div {...props}>{props.children}</div>;
|
||||||
|
|
||||||
const userIds = ApplicationStreamingStore.getViewerIds(encodeStreamKey(stream));
|
const userIds = ApplicationStreamingStore.getViewerIds(stream);
|
||||||
|
|
||||||
let missingUsers = 0;
|
let missingUsers = 0;
|
||||||
const users = userIds.map(id => UserStore.getUser(id)).filter(user => Boolean(user) ? true : (missingUsers += 1, false));
|
const users = userIds.map(id => UserStore.getUser(id)).filter(user => Boolean(user) ? true : (missingUsers += 1, false));
|
||||||
|
@ -160,7 +157,7 @@ export default definePlugin({
|
||||||
component: function ({ OriginalComponent }) {
|
component: function ({ OriginalComponent }) {
|
||||||
return (props: any) => {
|
return (props: any) => {
|
||||||
const stream = useStateFromStores([ApplicationStreamingStore], () => ApplicationStreamingStore.getCurrentUserActiveStream());
|
const stream = useStateFromStores([ApplicationStreamingStore], () => ApplicationStreamingStore.getCurrentUserActiveStream());
|
||||||
const viewers = ApplicationStreamingStore.getViewerIds(encodeStreamKey(stream));
|
const viewers = ApplicationStreamingStore.getViewerIds(stream);
|
||||||
return <Tooltip text={<Watching userIds={viewers} guildId={stream.guildId} />}>
|
return <Tooltip text={<Watching userIds={viewers} guildId={stream.guildId} />}>
|
||||||
{({ onMouseEnter, onMouseLeave }) => (
|
{({ onMouseEnter, onMouseLeave }) => (
|
||||||
<div onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave}>
|
<div onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave}>
|
||||||
|
|
|
@ -20,7 +20,7 @@ export default definePlugin({
|
||||||
{
|
{
|
||||||
find: ".Messages.SUPPRESS_ALL_EMBEDS",
|
find: ".Messages.SUPPRESS_ALL_EMBEDS",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /case (\i\.MessageEmbedTypes\.VIDEO):(case \i\.MessageEmbedTypes\.\i:)*break;default:(\i)=(?:(this\.renderDescription)\(\))\}/,
|
match: /case (\i\.\i\.VIDEO):(case \i\.\i\.\i:)*break;default:(\i)=(?:(this\.renderDescription)\(\))\}/,
|
||||||
replace: "$2 break; case $1: $3 = $self.ToggleableDescriptionWrapper({ embed: this.props.embed, original: $4.bind(this) }); break; default: $3 = $4() }"
|
replace: "$2 break; case $1: $3 = $self.ToggleableDescriptionWrapper({ embed: this.props.embed, original: $4.bind(this) }); break; default: $3 = $4() }"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,15 +65,15 @@ export default definePlugin({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
find: "Messages.USER_PROFILE_MUTUAL_GUILDS_PLACEHOLDER).with",
|
find: /Messages\.USER_PROFILE_MUTUAL_GUILDS_PLACEHOLDER\)\.with\(0,\(\)=>\i\.\i\.Messages\.USER_PROFILE_NO_MUTUAL_SERVERS/,
|
||||||
group: true,
|
group: true,
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
match: /(user:(\i),.+?=\i,)(.+?)(\i\.push)(.+?UserProfileSections\.MUTUAL_GUILDS,text:.{0,250}}\)\)}\))/,
|
match: /(user:(\i),.+?=\i,)(.+?)(\i\.push)(.+?\i\.MUTUAL_GUILDS,text:.{0,250}}\)\)}\))/,
|
||||||
replace: '$1vencordMutualGroupsTabLabel=$self.useGDMCount($2.id),$3$5,$4({section:"MUTUAL_GDMS",text:vencordMutualGroupsTabLabel})'
|
replace: '$1vencordMutualGroupsTabLabel=$self.useGDMCount($2.id),$3$5,$4({section:"MUTUAL_GDMS",text:vencordMutualGroupsTabLabel})'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
match: /(?<=(\i)===\i\.UserProfileSections\.MUTUAL_GUILDS?.{0,150}\}\):)/,
|
match: /(?<=(\i)===\i\.\i\i\.MUTUAL_GUILDS?.{0,150}\}\):)/,
|
||||||
replace: '$1==="MUTUAL_GDMS"?$self.renderMutualGDMs(arguments[0]):'
|
replace: '$1==="MUTUAL_GDMS"?$self.renderMutualGDMs(arguments[0]):'
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
Loading…
Add table
Reference in a new issue