Refactor settings UI and improve long list handling

- Rewrite settings page using TableRowGroup and TableSwitchRow - Add
commands overview and about sections to settings - Replace deprecated
Forms components with updated UI components - Add dialogs for
installing/enabling Split Large Messages plugin - Improve alerts for
sending long plugin/theme lists
This commit is contained in:
kmmiio99o 2025-09-27 20:10:37 +02:00
parent cd4d414a0c
commit 7c3a5bf31c
2 changed files with 170 additions and 73 deletions

View file

@ -1,89 +1,153 @@
import { ReactNative } from "@vendetta/metro/common";
import { storage } from "@vendetta/plugin";
import { useProxy } from "@vendetta/storage";
import { plugin } from "@vendetta";
import { getAssetIDByName } from "@vendetta/ui/assets";
import { ErrorBoundary, Forms } from "@vendetta/ui/components";
import { React } from "@vendetta/metro/common";
import { findByProps } from "@vendetta/metro";
const { FormSwitchRow, FormIcon, FormSection, FormDivider } = Forms;
const { ScrollView } = findByProps("ScrollView");
const { TableRowGroup, TableSwitchRow, Stack } = findByProps(
"TableSwitchRow",
"TableRowGroup",
"Stack",
);
const Icons = {
List: getAssetIDByName("ic_list"),
Message: getAssetIDByName("ic_message"),
Copy: getAssetIDByName("ic_copy_message_link"),
Info: getAssetIDByName("ic_info"),
Theme: getAssetIDByName("ic_theme"),
Plugin: getAssetIDByName("ic_plugins"),
};
const get = (k: string, fallback: any = "") => plugin.storage[k] ?? fallback;
const set = (k: string, v: any) => (plugin.storage[k] = v);
export default () => {
useProxy(storage);
if (!storage.factSettings) {
storage.factSettings = {
sendAsReply: true,
includeCitation: false,
};
}
if (!storage.listSettings) {
storage.listSettings = {
pluginListAlwaysDetailed: false,
themeListAlwaysDetailed: false,
};
}
export default function Settings() {
const [_, forceUpdate] = React.useReducer((x) => ~x, 0);
const update = () => forceUpdate();
return (
<ErrorBoundary>
<ReactNative.ScrollView style={{ flex: 1 }}>
{/* Fact Commands Settings */}
<FormSection title="Fact Commands" titleStyleType="no_border">
<FormSwitchRow
<ScrollView style={{ flex: 1 }} contentContainerStyle={{ padding: 10 }}>
<Stack spacing={8}>
{/* Facts Commands Settings */}
<TableRowGroup title="Facts Settings">
<TableSwitchRow
label="Send as Reply"
subLabel="Send facts as a reply to the command message"
leading={<FormIcon source={Icons.Message} />}
icon={getAssetIDByName("ic_reply_24px")}
value={get("factSettings.sendAsReply", true)}
onValueChange={(value: boolean) => {
storage.factSettings.sendAsReply = value;
set("factSettings", {
...get("factSettings", {}),
sendAsReply: value,
});
update();
}}
value={storage.factSettings.sendAsReply}
/>
<FormDivider />
<FormSwitchRow
<TableSwitchRow
label="Include Source Citation"
subLabel="Include the source of facts when available"
leading={<FormIcon source={Icons.Info} />}
icon={getAssetIDByName("ic_info")}
value={get("factSettings.includeCitation", false)}
onValueChange={(value: boolean) => {
storage.factSettings.includeCitation = value;
set("factSettings", {
...get("factSettings", {}),
includeCitation: value,
});
update();
}}
value={storage.factSettings.includeCitation}
/>
</FormSection>
</TableRowGroup>
{/* Plugin List Settings */}
<FormSection title="Plugin List" titleStyleType="no_border">
<FormSwitchRow
<TableRowGroup title="Plugin List Settings">
<TableSwitchRow
label="Always Send Detailed List"
subLabel="Always use detailed mode when listing plugins"
leading={<FormIcon source={Icons.Plugin} />}
icon={getAssetIDByName("ic_message_copy")}
value={get("listSettings.pluginListAlwaysDetailed", false)}
onValueChange={(value: boolean) => {
storage.listSettings.pluginListAlwaysDetailed = value;
set("listSettings", {
...get("listSettings", {}),
pluginListAlwaysDetailed: value,
});
update();
}}
value={storage.listSettings.pluginListAlwaysDetailed}
/>
</FormSection>
</TableRowGroup>
{/* Theme List Settings */}
<FormSection title="Theme List" titleStyleType="no_border">
<FormSwitchRow
<TableRowGroup title="Theme List Settings">
<TableSwitchRow
label="Always Send Detailed List"
subLabel="Always use detailed mode when listing themes"
leading={<FormIcon source={Icons.Theme} />}
icon={getAssetIDByName("ic_theme")}
value={get("listSettings.themeListAlwaysDetailed", false)}
onValueChange={(value: boolean) => {
storage.listSettings.themeListAlwaysDetailed = value;
set("listSettings", {
...get("listSettings", {}),
themeListAlwaysDetailed: value,
});
update();
}}
value={storage.listSettings.themeListAlwaysDetailed}
/>
</FormSection>
</ReactNative.ScrollView>
</ErrorBoundary>
</TableRowGroup>
{/* Commands Overview */}
<TableRowGroup title="Available Commands">
<TableSwitchRow
label="/catfact"
subLabel="Get random cat facts"
icon={getAssetIDByName("ic_info")}
value={true}
disabled={true}
/>
<TableSwitchRow
label="/dogfact"
subLabel="Get random dog facts"
icon={getAssetIDByName("ic_info")}
value={true}
disabled={true}
/>
<TableSwitchRow
label="/useless"
subLabel="Get random useless facts"
icon={getAssetIDByName("ic_info")}
value={true}
disabled={true}
/>
<TableSwitchRow
label="/petpet"
subLabel="Create pet-pet GIF of a user"
icon={getAssetIDByName("ic_image")}
value={true}
disabled={true}
/>
<TableSwitchRow
label="/plugin-list"
subLabel="List all installed plugins"
icon={getAssetIDByName("ic_plugins")}
value={true}
disabled={true}
/>
<TableSwitchRow
label="/theme-list"
subLabel="List all installed themes"
icon={getAssetIDByName("ic_theme")}
value={true}
disabled={true}
/>
<TableSwitchRow
label="/konoself, /konosend"
subLabel="Get random images from KonoChan"
icon={getAssetIDByName("ic_image")}
value={true}
disabled={true}
/>
</TableRowGroup>
{/* About Section */}
<TableRowGroup title="About">
<TableSwitchRow
label="All-In-One Commands"
subLabel="A collection of useful commands"
icon={getAssetIDByName("ic_badge_staff")}
value={true}
disabled={true}
/>
</TableRowGroup>
</Stack>
</ScrollView>
);
};
}

View file

@ -3,7 +3,8 @@ import { themes } from "@vendetta/themes";
import { plugins } from "@vendetta/plugins";
import { storage } from "@vendetta/plugin";
import { alerts } from "@vendetta/ui";
import { showAlert } from "../utils/messages";
import { sendMessage } from "../utils/messages";
import { openURL } from "@vendetta/metro/common";
const SPLIT_LARGE_MESSAGES_PLUGIN =
"https://vd-plugins.github.io/proxy/actuallythesun.github.io/vendetta-plugins/SplitLargeMessages/";
@ -30,6 +31,40 @@ const isSLMPluginEnabled = () =>
Object.values(plugins).find((p) => p.id === SPLIT_LARGE_MESSAGES_PLUGIN)
?.enabled ?? false;
const showSplitPluginDialog = () => {
return alerts.showConfirmationAlert({
title: "Large Message Warning",
content:
"This list is too long to send! Would you like to install the Split Large Messages plugin?",
confirmText: "Install Plugin",
cancelText: "Cancel",
confirmColor: "brand",
onConfirm: () => {
openURL(SPLIT_LARGE_MESSAGES_PLUGIN);
},
});
};
const showEnablePluginDialog = () => {
return alerts.showConfirmationAlert({
title: "Plugin Disabled",
content:
"The Split Large Messages plugin is installed but disabled. Would you like to enable it now?",
confirmText: "Enable Plugin",
cancelText: "Cancel",
confirmColor: "brand",
onConfirm: () => {
// Enable the plugin
const slmPlugin = Object.values(plugins).find(
(p) => p.id === SPLIT_LARGE_MESSAGES_PLUGIN,
);
if (slmPlugin) {
slmPlugin.enabled = true;
}
},
});
};
// Command handlers
async function handlePluginList(detailed: boolean, ctx: any) {
const alwaysDetailed =
@ -61,18 +96,17 @@ async function handlePluginList(detailed: boolean, ctx: any) {
if (getListLength(pluginList) > 2000) {
if (!isSLMPluginInstalled()) {
showAlert("List too long! Install Split Large Messages plugin");
return { type: 4 };
return showSplitPluginDialog();
}
if (!isSLMPluginEnabled()) {
showAlert("Enable Split Large Messages plugin to send long lists!");
return { type: 4 };
return showEnablePluginDialog();
}
return alerts.showConfirmationAlert({
content: "Your list is over 2000 characters. Send anyway?",
confirmText: "Yes",
cancelText: "No",
title: "Large Message",
content: "This list is over 2000 characters. Send anyway?",
confirmText: "Send",
cancelText: "Cancel",
onConfirm: () => sendMessage(ctx.channel.id, formatList(pluginList)),
});
}
@ -114,18 +148,17 @@ async function handleThemeList(detailed: boolean, ctx: any) {
if (getListLength(themeList) > 2000) {
if (!isSLMPluginInstalled()) {
showAlert("List too long! Install Split Large Messages plugin");
return { type: 4 };
return showSplitPluginDialog();
}
if (!isSLMPluginEnabled()) {
showAlert("Enable Split Large Messages plugin to send long lists!");
return { type: 4 };
return showEnablePluginDialog();
}
return alerts.showConfirmationAlert({
content: "Your list is over 2000 characters. Send anyway?",
confirmText: "Yes",
cancelText: "No",
title: "Large Message",
content: "This list is over 2000 characters. Send anyway?",
confirmText: "Send",
cancelText: "Cancel",
onConfirm: () => sendMessage(ctx.channel.id, formatList(themeList)),
});
}