BetterBanReasons: Add per-reason message delete duration option (#1024)
* Add per-reason message delete duration option * Update index.tsx * Update index.tsx --------- Co-authored-by: thororen <78185467+thororen1234@users.noreply.github.com>
This commit is contained in:
parent
ae72ab6a51
commit
c2e966e50d
3 changed files with 105 additions and 34 deletions
|
|
@ -9,48 +9,82 @@ import "./styles.css";
|
|||
import { definePluginSettings } from "@api/Settings";
|
||||
import { Heading } from "@components/Heading";
|
||||
import { DeleteIcon, PlusIcon } from "@components/Icons";
|
||||
import { Devs } from "@utils/constants";
|
||||
import { Devs, EquicordDevs } from "@utils/constants";
|
||||
import { classNameFactory } from "@utils/css";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { Button, TextInput } from "@webpack/common";
|
||||
import { Button, Select, TextInput, useState } from "@webpack/common";
|
||||
|
||||
const cl = classNameFactory("vc-bbr-");
|
||||
|
||||
interface BanReason {
|
||||
text: string;
|
||||
deleteSeconds?: number;
|
||||
}
|
||||
|
||||
const DELETE_DURATION_OPTIONS = [
|
||||
{ label: "Discord default", value: null },
|
||||
{ label: "Don't delete any", value: 0 },
|
||||
{ label: "Previous hour", value: 3600 },
|
||||
{ label: "Previous 6 hours", value: 21600 },
|
||||
{ label: "Previous 12 hours", value: 43200 },
|
||||
{ label: "Previous 24 hours", value: 86400 },
|
||||
{ label: "Previous 3 days", value: 259200 },
|
||||
{ label: "Previous 7 days", value: 604800 },
|
||||
];
|
||||
|
||||
function normalizeReason(r: BanReason | string): BanReason {
|
||||
return typeof r === "string" ? { text: r } : r;
|
||||
}
|
||||
|
||||
function getStoredReasons(): BanReason[] {
|
||||
return (settings.store.reasons as (BanReason | string)[]).map(normalizeReason);
|
||||
}
|
||||
|
||||
function ReasonsComponent() {
|
||||
const { reasons } = settings.store;
|
||||
const [reasons, setReasons] = useState<BanReason[]>(getStoredReasons);
|
||||
|
||||
const update = (next: BanReason[]) => {
|
||||
setReasons(next);
|
||||
settings.store.reasons = next;
|
||||
};
|
||||
|
||||
return (
|
||||
<section>
|
||||
<Heading>Reasons</Heading>
|
||||
{reasons.map((r, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className={cl("reason-wrapper")}
|
||||
>
|
||||
<div key={i} className={cl("reason-wrapper")}>
|
||||
<TextInput
|
||||
value={r}
|
||||
onChange={v => {
|
||||
reasons[i] = v;
|
||||
settings.store.reasons = reasons;
|
||||
}}
|
||||
value={r.text}
|
||||
onChange={v => update(reasons.map((x, j) => j === i ? { ...x, text: v } : x))}
|
||||
placeholder="Reason"
|
||||
/>
|
||||
<Button
|
||||
className={cl("remove-button")}
|
||||
color={Button.Colors.TRANSPARENT}
|
||||
onClick={() => {
|
||||
reasons.splice(i, 1);
|
||||
settings.store.reasons = reasons;
|
||||
}}
|
||||
onClick={() => update(reasons.filter((_, j) => j !== i))}
|
||||
look={Button.Looks.FILLED}
|
||||
size={Button.Sizes.MIN}
|
||||
>
|
||||
<DeleteIcon />
|
||||
</Button>
|
||||
<div className={cl("duration-row")}>
|
||||
<span className="vc-text-base vc-text-sm vc-text-normal vc-text-defaultColor vc-plugins-setting-description">Message Auto-delete duration</span>
|
||||
<Select
|
||||
options={DELETE_DURATION_OPTIONS}
|
||||
select={v => update(reasons.map((x, j) => j === i ? { ...x, deleteSeconds: v === null ? undefined : v } : x))}
|
||||
isSelected={v => (v ?? null) === (r.deleteSeconds ?? null)}
|
||||
serialize={v => v == null ? "default" : String(v)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
<div className={cl("reason-wrapper")}>
|
||||
<Button onClick={() => settings.store.reasons.push("")} className={cl("add-button")} size={Button.Sizes.LARGE} color={Button.Colors.TRANSPARENT}>
|
||||
<Button
|
||||
onClick={() => update([...reasons, { text: "" }])}
|
||||
className={cl("add-button")}
|
||||
size={Button.Sizes.LARGE}
|
||||
color={Button.Colors.TRANSPARENT}
|
||||
>
|
||||
<PlusIcon /> Add another reason
|
||||
</Button>
|
||||
</div>
|
||||
|
|
@ -62,7 +96,7 @@ const settings = definePluginSettings({
|
|||
reasons: {
|
||||
description: "Your custom reasons",
|
||||
type: OptionType.COMPONENT,
|
||||
default: [] as string[],
|
||||
default: [] as BanReason[],
|
||||
component: ReasonsComponent,
|
||||
},
|
||||
isTextInputDefault: {
|
||||
|
|
@ -75,30 +109,56 @@ export default definePlugin({
|
|||
name: "BetterBanReasons",
|
||||
description: "Create custom reasons to use in the Discord ban modal, and/or show a text input by default instead of the options.",
|
||||
tags: ["Appearance", "Customisation"],
|
||||
authors: [Devs.Inbestigator],
|
||||
authors: [Devs.Inbestigator, EquicordDevs.yonn2222],
|
||||
|
||||
durationSetter: null as ((v: number) => void) | null,
|
||||
|
||||
patches: [
|
||||
{
|
||||
find: "#{intl::BAN_REASON_OPTION_SPAM_ACCOUNT}",
|
||||
replacement: [{
|
||||
match: /(\[\{name:\i\.\i\.\i\(\i\.\i\.\i\),.+?"other"\}\])/,
|
||||
replace: "$self.getReasons($1)"
|
||||
},
|
||||
{
|
||||
match: /useState\(null\)(?=.{0,300}targetUserId:)/,
|
||||
replace: "useState($self.getDefaultState())"
|
||||
}]
|
||||
replacement: [
|
||||
{
|
||||
match: /(\[\{name:\i\.\i\.\i\(\i\.\i\.\i\),.+?"other"\}\])/,
|
||||
replace: "$self.getReasons($1)"
|
||||
},
|
||||
{
|
||||
match: /useState\(null\)(?=.{0,300}targetUserId:)/,
|
||||
replace: "useState($self.getDefaultState())"
|
||||
},
|
||||
{
|
||||
match: /(\[\i,\i\])=(\i)\.useState\((null!=\i\?\i:\i)\)/,
|
||||
replace: "$1=$self.captureDeleteState($2.useState,$3)"
|
||||
},
|
||||
{
|
||||
match: /\i=\i\.useCallback\((\i)=>\{.{0,10},\i\(null\)(?=\},\[\]\))/,
|
||||
replace: "$&,$self.onReasonSelect($1)"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
getReasons(defaults) {
|
||||
const storedReasons = settings.store.reasons.filter((r: string) => r.trim());
|
||||
const reasons: string[] = storedReasons.length
|
||||
? storedReasons
|
||||
: [];
|
||||
|
||||
getReasons(defaults: { name: string; value: string; }[]) {
|
||||
const stored = getStoredReasons().filter(r => r.text.trim());
|
||||
return [
|
||||
...reasons.map(s => ({ name: s, value: s })),
|
||||
...defaults
|
||||
...stored.map(r => ({ name: r.text, value: r.text })),
|
||||
...defaults,
|
||||
];
|
||||
},
|
||||
|
||||
getDefaultState: () => settings.store.isTextInputDefault ? 1 : 0,
|
||||
|
||||
captureDeleteState(hook: (initial: number) => [number, (v: number) => void], initial: number) {
|
||||
const result = hook(initial);
|
||||
this.durationSetter = result[1];
|
||||
return result;
|
||||
},
|
||||
|
||||
onReasonSelect(value: string) {
|
||||
const reason = getStoredReasons().find(r => r.text === value);
|
||||
if (reason?.deleteSeconds !== undefined) {
|
||||
this.durationSetter?.(reason.deleteSeconds);
|
||||
}
|
||||
},
|
||||
|
||||
settings,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -8,6 +8,13 @@
|
|||
|
||||
.vc-bbr-remove-button {
|
||||
color: var(--text-muted);
|
||||
align-self: start;
|
||||
}
|
||||
|
||||
.vc-bbr-duration-row {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.vc-bbr-remove-button:hover {
|
||||
|
|
|
|||
|
|
@ -1296,6 +1296,10 @@ export const EquicordDevs = Object.freeze({
|
|||
name: "DKA",
|
||||
id: 119386840624005121n
|
||||
},
|
||||
yonn2222: {
|
||||
name: "yonn2222",
|
||||
id: 821835831844012103n
|
||||
},
|
||||
} satisfies Record<string, Dev>);
|
||||
|
||||
// iife so #__PURE__ works correctly
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue