diff --git a/src/components/VencordSettings/PatchHelperTab.tsx b/src/components/VencordSettings/PatchHelperTab.tsx index 9e2980e7e..013e32d7d 100644 --- a/src/components/VencordSettings/PatchHelperTab.tsx +++ b/src/components/VencordSettings/PatchHelperTab.tsx @@ -16,7 +16,6 @@ * along with this program. If not, see . */ -import { CheckedTextInput } from "@components/CheckedTextInput"; import { CodeBlock } from "@components/CodeBlock"; import { debounce } from "@shared/debounce"; import { Margins } from "@utils/margins"; @@ -47,7 +46,7 @@ const findCandidates = debounce(function ({ find, setModule, setError }) { interface ReplacementComponentProps { module: [id: number, factory: Function]; - match: string | RegExp; + match: string; replacement: string | ReplaceFn; setReplacementError(error: any): void; } @@ -58,7 +57,13 @@ function ReplacementComponent({ module, match, replacement, setReplacementError const [patchedCode, matchResult, diff] = React.useMemo(() => { const src: string = fact.toString().replaceAll("\n", ""); - const canonicalMatch = canonicalizeMatch(match); + + try { + new RegExp(match); + } catch (e) { + return ["", [], []]; + } + const canonicalMatch = canonicalizeMatch(new RegExp(match)); try { const canonicalReplace = canonicalizeReplace(replacement, "YourPlugin"); var patched = src.replace(canonicalMatch, canonicalReplace as string); @@ -286,6 +291,7 @@ function PatchHelper() { const [module, setModule] = React.useState<[number, Function]>(); const [findError, setFindError] = React.useState(); + const [matchError, setMatchError] = React.useState(); const code = React.useMemo(() => { return ` @@ -322,12 +328,17 @@ function PatchHelper() { } function onMatchChange(v: string) { + setMatchError(void 0); + setMatch(v); + } + + function onMatchBlur() { try { - new RegExp(v); - setFindError(void 0); - setMatch(v); + new RegExp(match); + setMatchError(void 0); + setMatch(match); } catch (e: any) { - setFindError((e as Error).message); + setMatchError((e as Error).message); } } @@ -351,16 +362,12 @@ function PatchHelper() { /> match - { - try { - return (new RegExp(v), true); - } catch (e) { - return (e as Error).message; - } - }} + onBlur={onMatchBlur} + error={matchError} />
@@ -374,7 +381,7 @@ function PatchHelper() { {module && ( diff --git a/src/plugins/customidle/README.md b/src/plugins/customidle/README.md new file mode 100644 index 000000000..63bf87d89 --- /dev/null +++ b/src/plugins/customidle/README.md @@ -0,0 +1,5 @@ +# CustomIdle + +Lets you change the time until your status gets automatically set to idle. You can also prevent idling altogether. + +![Plugin Configuration](https://github.com/Vendicated/Vencord/assets/45801973/4e5259b2-18e0-42e5-b69f-efc672ce1e0b) diff --git a/src/plugins/customidle/index.ts b/src/plugins/customidle/index.ts new file mode 100644 index 000000000..a59bbcb01 --- /dev/null +++ b/src/plugins/customidle/index.ts @@ -0,0 +1,94 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { Notices } from "@api/index"; +import { definePluginSettings } from "@api/Settings"; +import { makeRange } from "@components/PluginSettings/components"; +import { Devs } from "@utils/constants"; +import definePlugin, { OptionType } from "@utils/types"; +import { FluxDispatcher } from "@webpack/common"; + +const settings = definePluginSettings({ + idleTimeout: { + description: "Minutes before Discord goes idle (0 to disable auto-idle)", + type: OptionType.SLIDER, + markers: makeRange(0, 60, 5), + default: 10, + stickToMarkers: false, + restartNeeded: true // Because of the setInterval patch + }, + remainInIdle: { + description: "When you come back to Discord, remain idle until you confirm you want to go online", + type: OptionType.BOOLEAN, + default: true + } +}); + +export default definePlugin({ + name: "CustomIdle", + description: "Allows you to set the time before Discord goes idle (or disable auto-idle)", + authors: [Devs.newwares], + settings, + patches: [ + { + find: "IDLE_DURATION:function(){return", + replacement: { + match: /(IDLE_DURATION:function\(\){return )\i/, + replace: "$1$self.getIdleTimeout()" + } + }, + { + find: 'type:"IDLE",idle:', + replacement: [ + { + match: /Math\.min\((\i\.AfkTimeout\.getSetting\(\)\*\i\.default\.Millis\.SECOND),\i\.IDLE_DURATION\)/, + replace: "$1" // Decouple idle from afk (phone notifications will remain at user setting or 10 min maximum) + }, + { + match: /\i\.default\.dispatch\({type:"IDLE",idle:!1}\)/, + replace: "$self.handleOnline()" + }, + { + match: /(setInterval\(\i,\.25\*)\i\.IDLE_DURATION/, + replace: "$1$self.getIntervalDelay()" // For web installs + } + ] + } + ], + + getIntervalDelay() { + return Math.min(6e5, this.getIdleTimeout()); + }, + + handleOnline() { + if (!settings.store.remainInIdle) { + FluxDispatcher.dispatch({ + type: "IDLE", + idle: false + }); + return; + } + + const backOnlineMessage = "Welcome back! Click the button to go online. Click the X to stay idle until reload."; + if ( + Notices.currentNotice?.[1] === backOnlineMessage || + Notices.noticesQueue.some(([, noticeMessage]) => noticeMessage === backOnlineMessage) + ) return; + + Notices.showNotice(backOnlineMessage, "Exit idle", () => { + Notices.popNotice(); + FluxDispatcher.dispatch({ + type: "IDLE", + idle: false + }); + }); + }, + + getIdleTimeout() { // milliseconds, default is 6e5 + const { idleTimeout } = settings.store; + return idleTimeout === 0 ? Infinity : idleTimeout * 60000; + } +}); diff --git a/src/plugins/moreUserTags/index.tsx b/src/plugins/moreUserTags/index.tsx index 1a1d2fae3..869319097 100644 --- a/src/plugins/moreUserTags/index.tsx +++ b/src/plugins/moreUserTags/index.tsx @@ -352,6 +352,15 @@ export default definePlugin({ if (location === "chat" && !settings.tagSettings[tag.name].showInChat) continue; if (location === "not-chat" && !settings.tagSettings[tag.name].showInNotChat) continue; + // If the owner tag is disabled, and the user is the owner of the guild, + // avoid adding other tags because the owner will always match the condition for them + if ( + tag.name !== "OWNER" && + GuildStore.getGuild(channel?.guild_id)?.ownerId === user.id && + (location === "chat" && !settings.tagSettings.OWNER.showInChat) || + (location === "not-chat" && !settings.tagSettings.OWNER.showInNotChat) + ) continue; + if ( tag.permissions?.some(perm => perms.includes(perm)) || (tag.condition?.(message!, user, channel)) diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 974758e3a..44d13b54c 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -422,6 +422,10 @@ export const Devs = /* #__PURE__*/ Object.freeze({ name: "Av32000", id: 593436735380127770n, }, + Noxillio: { + name: "Noxillio", + id: 138616536502894592n, + }, Kyuuhachi: { name: "Kyuuhachi", id: 236588665420251137n,