diff --git a/plugins/Commands/index.ts b/plugins/Commands/index.ts
index 855b780..8edbfb1 100644
--- a/plugins/Commands/index.ts
+++ b/plugins/Commands/index.ts
@@ -18,7 +18,7 @@ import {
} from "./src/commands/spotify";
import { garyCommand } from "./src/commands/gary";
import { lovefemboysCommand } from "./src/commands/lovefemboys";
-import settings from "./settings"; // Fixed import path
+import settings from "./settings";
// Initialize storage with default values
if (!storage.factSettings) {
diff --git a/plugins/Commands/settings.tsx b/plugins/Commands/settings.tsx
index 0b2a39e..ab667f0 100644
--- a/plugins/Commands/settings.tsx
+++ b/plugins/Commands/settings.tsx
@@ -1,298 +1,981 @@
-import { findByProps } from "@vendetta/metro";
+import { storage } from "@vendetta/plugin";
+import { useProxy } from "@vendetta/storage";
+import { React, ReactNative as RN, stylesheet } from "@vendetta/metro/common";
+import { NavigationNative } from "@vendetta/metro/common";
+import { semanticColors } from "@vendetta/ui";
+import { getAssetIDByName } from "@vendetta/ui/assets";
+import { Forms } from "@vendetta/ui/components";
+import { alerts } from "@vendetta/ui";
+import { showToast } from "@vendetta/ui/toasts";
-const MessageActions = findByProps("sendMessage");
+const { ScrollView } = RN;
+const { FormRow, FormSwitchRow } = Forms;
-interface RedditPost {
- kind: string;
- data: {
- title: string;
- url: string;
- is_video: boolean;
- over_18: boolean;
- thumbnail: string;
- thumbnail_width?: number;
- thumbnail_height?: number;
- preview?: {
- images: Array<{
- source: {
- url: string;
- width: number;
- height: number;
- };
- resolutions: Array<{
- url: string;
- width: number;
- height: number;
- }>;
- }>;
- };
- secure_media?: {
- oembed?: {
- thumbnail_url: string;
- type: string;
- };
- };
- media_embed?: {
- content: string;
- };
- url_overridden_by_dest?: string;
- domain: string;
- post_hint?: string;
- };
-}
-
-interface RedditResponse {
- kind: string;
- data: {
- children: RedditPost[];
- after: string;
- dist: number;
- };
-}
-
-function isValidImageUrl(url: string): boolean {
- if (!url) return false;
-
- const lowerUrl = url.toLowerCase();
-
- // Check for direct image URLs
- const imageExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.webp'];
- if (imageExtensions.some(ext => lowerUrl.includes(ext))) {
- return true;
- }
-
- // Known image hosting domains
- const imageHosts = [
- 'i.redd.it',
- 'i.imgur.com',
- 'preview.redd.it'
- ];
-
- return imageHosts.some(host => lowerUrl.includes(host));
-}
-
-function extractImageUrl(post: RedditPost): string | null {
- const postData = post.data;
-
- // Skip videos and GIFs from redgifs/etc
- if (postData.is_video || postData.domain.includes('redgifs.com') || postData.domain.includes('gfycat.com')) {
- return null;
- }
-
- // Check post hint for image types
- if (postData.post_hint && !['image', 'hosted:video'].includes(postData.post_hint)) {
- return null;
- }
-
- // Try the main URL first
- const mainUrl = postData.url;
- if (isValidImageUrl(mainUrl)) {
- return mainUrl;
- }
-
- // Try preview images
- if (postData.preview?.images?.[0]) {
- const previewImage = postData.preview.images[0];
-
- // Try source image first (highest quality)
- if (previewImage.source?.url) {
- const sourceUrl = previewImage.source.url.replace(/&/g, '&');
- if (isValidImageUrl(sourceUrl)) {
- return sourceUrl;
- }
- }
-
- // Try highest resolution preview
- if (previewImage.resolutions?.length > 0) {
- const highestRes = previewImage.resolutions[previewImage.resolutions.length - 1];
- if (highestRes?.url) {
- const resUrl = highestRes.url.replace(/&/g, '&');
- if (isValidImageUrl(resUrl)) {
- return resUrl;
- }
- }
- }
- }
-
- // Try secure_media oembed thumbnail for some cases
- if (postData.secure_media?.oembed?.thumbnail_url && postData.secure_media.oembed.type === 'photo') {
- const thumbUrl = postData.secure_media.oembed.thumbnail_url;
- if (isValidImageUrl(thumbUrl)) {
- return thumbUrl;
- }
- }
-
- // Try regular thumbnail as last resort (but only if it's not a default thumbnail)
- if (postData.thumbnail &&
- postData.thumbnail !== 'self' &&
- postData.thumbnail !== 'default' &&
- postData.thumbnail !== 'nsfw' &&
- postData.thumbnail.startsWith('http')) {
- if (isValidImageUrl(postData.thumbnail)) {
- return postData.thumbnail;
- }
- }
-
- return null;
-}
-
-async function getFemboyImage(includeNsfw: boolean = false): Promise<{ url: string; isNsfw: boolean; title: string } | null> {
- try {
- console.log(`[LoveFemboys] Fetching images, includeNsfw: ${includeNsfw}`);
-
- // Fetch more posts to increase chances of finding images
- const response = await fetch('https://reddit.com/r/femboys.json?limit=100&raw_json=1');
- if (!response.ok) {
- console.log('[LoveFemboys] Failed to fetch from Reddit API:', response.status, response.statusText);
- return null;
- }
-
- const data: RedditResponse = await response.json();
- console.log(`[LoveFemboys] Fetched ${data.data.children.length} posts`);
-
- // Filter posts that are images and match NSFW preference
- const validPosts = [];
-
- for (const post of data.data.children) {
- const postData = post.data;
-
- console.log(`[LoveFemboys] Checking post: "${postData.title}" (NSFW: ${postData.over_18}, Domain: ${postData.domain})`);
-
- // Filter by NSFW preference first
- if (!includeNsfw && postData.over_18) {
- console.log(`[LoveFemboys] Skipping NSFW post: ${postData.title}`);
- continue;
- }
-
- // Try to extract image URL
- const imageUrl = extractImageUrl(post);
- if (imageUrl) {
- console.log(`[LoveFemboys] Found valid image: ${imageUrl}`);
- validPosts.push({
- url: imageUrl,
- isNsfw: postData.over_18,
- title: postData.title
- });
- } else {
- console.log(`[LoveFemboys] No valid image URL found for: ${postData.title}`);
- }
- }
-
- console.log(`[LoveFemboys] Found ${validPosts.length} valid image posts (includeNsfw: ${includeNsfw})`);
-
- if (validPosts.length === 0) {
- console.log('[LoveFemboys] No suitable images found');
- return null;
- }
-
- // Get random image
- const randomPost = validPosts[Math.floor(Math.random() * validPosts.length)];
- console.log(`[LoveFemboys] Selected: ${randomPost.title} - ${randomPost.url}`);
-
- return randomPost;
- } catch (error) {
- console.error('[LoveFemboys] Error fetching image:', error);
- return null;
- }
-}
-
-export const lovefemboysCommand = {
- name: "lovefemboys",
- displayName: "lovefemboys",
- description: "Get a random femboy image from r/femboys",
- displayDescription: "Get a random femboy image from r/femboys",
- options: [
- {
- name: "nsfw",
- displayName: "nsfw",
- description: "Include NSFW images",
- displayDescription: "Include NSFW images",
- type: 5, // Boolean
- required: false,
- },
- {
- name: "ephemeral",
- displayName: "ephemeral",
- description: "Send as ephemeral message (only you can see)",
- displayDescription: "Send as ephemeral message (only you can see)",
- type: 5, // Boolean
- required: false,
- }
- ],
- execute: async (args: any, ctx: any) => {
- try {
- const includeNsfw = args.find((arg: any) => arg.name === "nsfw")?.value || false;
- const isEphemeral = args.find((arg: any) => arg.name === "ephemeral")?.value || false;
-
- console.log(`[LoveFemboys] Command executed with nsfw: ${includeNsfw}, ephemeral: ${isEphemeral}`);
-
- const result = await getFemboyImage(includeNsfw);
-
- if (!result) {
- const errorMessage = includeNsfw
- ? "❌ Failed to fetch femboy image. Reddit might be having issues or no images available."
- : "❌ No SFW femboy images found. Try with `nsfw:true` or try again later.";
-
- if (isEphemeral) {
- return {
- type: 4,
- data: {
- content: errorMessage,
- flags: 64, // Ephemeral flag
- },
- };
- } else {
- const fixNonce = Date.now().toString();
- MessageActions.sendMessage(ctx.channel.id, { content: errorMessage }, void 0, {
- nonce: fixNonce,
- });
- return { type: 4 };
- }
- }
-
- const nsfwTag = result.isNsfw ? " 🔞" : " ✅";
- const modeTag = includeNsfw ? " (NSFW allowed)" : " (SFW only)";
- const content = `💖 **Femboy Image**${nsfwTag}${modeTag}\n*${result.title}*\n${result.url}`;
-
- if (isEphemeral) {
- return {
- type: 4,
- data: {
- content,
- flags: 64, // Ephemeral flag
- },
- };
- } else {
- const fixNonce = Date.now().toString();
- MessageActions.sendMessage(ctx.channel.id, { content }, void 0, {
- nonce: fixNonce,
- });
- return { type: 4 };
- }
- } catch (error) {
- console.error('[LoveFemboys] Command error:', error);
- const errorMessage = "❌ An error occurred while fetching femboy image. Check console for details.";
-
- const isEphemeral = args.find((arg: any) => arg.name === "ephemeral")?.value || false;
-
- if (isEphemeral) {
- return {
- type: 4,
- data: {
- content: errorMessage,
- flags: 64, // Ephemeral flag
- },
- };
- } else {
- const fixNonce = Date.now().toString();
- MessageActions.sendMessage(ctx.channel.id, { content: errorMessage }, void 0, {
- nonce: fixNonce,
- });
- return { type: 4 };
- }
- }
- },
- applicationId: "-1",
- inputType: 1,
- type: 1,
+// Initialize storage with default values
+storage.factSettings ??= {
+ sendAsReply: true,
+ includeCitation: false,
};
+
+storage.listSettings ??= {
+ pluginListAlwaysDetailed: false,
+ themeListAlwaysDetailed: false,
+};
+
+storage.garySettings ??= {
+ imageSource: "gary",
+};
+
+storage.enabledCommands ??= {
+ catfact: true,
+ dogfact: true,
+ useless: true,
+ petpet: true,
+ pluginList: true,
+ themeList: true,
+ konoself: true,
+ konosend: true,
+ firstmessage: true,
+ sysinfo: true,
+ spotifyTrack: true,
+ spotifyAlbum: true,
+ spotifyArtists: true,
+ spotifyCover: true,
+ gary: true,
+ lovefemboys: false, // Hidden command - disabled by default
+};
+
+storage.pendingRestart ??= false;
+
+// Hidden settings storage
+storage.hiddenSettings ??= {
+ enabled: false,
+ visible: false,
+};
+
+// Better Table Row Group Component
+function BetterTableRowGroup({
+ title,
+ icon,
+ children,
+ padding = false,
+}: React.PropsWithChildren<{
+ title?: string;
+ icon?: number;
+ padding?: boolean;
+}>) {
+ const styles = stylesheet.createThemedStyleSheet({
+ main: {
+ backgroundColor: semanticColors.CARD_PRIMARY_BG,
+ borderColor: semanticColors.BORDER_FAINT,
+ borderWidth: 1,
+ borderRadius: 16,
+ overflow: "hidden",
+ flex: 1,
+ },
+ titleContainer: {
+ marginBottom: 8,
+ marginHorizontal: 0,
+ marginTop: 8,
+ gap: 4,
+ flexDirection: "row",
+ alignItems: "center",
+ },
+ icon: {
+ width: 16,
+ height: 16,
+ marginTop: 1.5,
+ tintColor: semanticColors.TEXT_MUTED,
+ },
+ titleText: {
+ fontSize: 14,
+ fontWeight: "600",
+ color: semanticColors.TEXT_MUTED,
+ },
+ });
+
+ return (
+
+ {title && (
+
+ {icon && (
+
+ )}
+
+ {title.toUpperCase()}
+
+
+ )}
+
+ {padding ? (
+
+ {children}
+
+ ) : children}
+
+
+ );
+}
+
+// Header Component with secret unlock
+function Header({ onHiddenUnlock }: { onHiddenUnlock?: () => void }) {
+ const [clickCounter, setClickCounter] = React.useState(0);
+ const [clickTimeout, setClickTimeout] = React.useState(null);
+
+ const styles = stylesheet.createThemedStyleSheet({
+ container: {
+ flexDirection: "column",
+ alignItems: "center",
+ paddingVertical: 24,
+ paddingHorizontal: 16,
+ },
+ title: {
+ fontSize: 28,
+ fontWeight: "700",
+ color: semanticColors.TEXT_NORMAL,
+ marginBottom: 4,
+ },
+ subtitle: {
+ fontSize: 16,
+ fontWeight: "500",
+ color: semanticColors.TEXT_MUTED,
+ },
+ iconContainer: {
+ backgroundColor: semanticColors.BACKGROUND_SECONDARY,
+ borderRadius: 16,
+ padding: 12,
+ marginBottom: 16,
+ },
+ icon: {
+ width: 32,
+ height: 32,
+ tintColor: semanticColors.TEXT_NORMAL,
+ },
+ });
+
+ const handleIconPress = () => {
+ if (storage.hiddenSettings.enabled) {
+ storage.hiddenSettings.visible = !storage.hiddenSettings.visible;
+ showToast(
+ `Hidden settings ${storage.hiddenSettings.visible ? "visible" : "hidden"}`,
+ getAssetIDByName("SettingsIcon")
+ );
+ onHiddenUnlock?.();
+ return;
+ }
+
+ if (clickTimeout) {
+ clearTimeout(clickTimeout);
+ }
+
+ const newTimeout = setTimeout(() => {
+ setClickCounter(0);
+ }, 1000);
+ setClickTimeout(newTimeout);
+
+ const newCounter = clickCounter + 1;
+ setClickCounter(newCounter);
+
+ if (newCounter < 10) {
+ return;
+ }
+
+ showToast("🔓 Hidden settings unlocked!", getAssetIDByName("CheckmarkIcon"));
+ storage.hiddenSettings.enabled = true;
+ storage.hiddenSettings.visible = true;
+ setClickCounter(0);
+
+ onHiddenUnlock?.();
+ };
+
+ return (
+
+
+
+
+ Commands
+ A collection of useful commands
+
+ );
+}
+
+// Hidden Commands Settings Page
+function HiddenSettingsPage() {
+ useProxy(storage);
+ const [, forceUpdate] = React.useReducer((x) => x + 1, 0);
+
+ const styles = stylesheet.createThemedStyleSheet({
+ container: {
+ flex: 1,
+ backgroundColor: semanticColors.BACKGROUND_PRIMARY,
+ },
+ warningText: {
+ fontSize: 14,
+ color: semanticColors.TEXT_DANGER,
+ textAlign: "center",
+ lineHeight: 20,
+ fontWeight: "600",
+ },
+ infoText: {
+ fontSize: 14,
+ color: semanticColors.TEXT_MUTED,
+ textAlign: "center",
+ lineHeight: 20,
+ marginTop: 8,
+ },
+ });
+
+ return (
+
+
+
+
+ These are hidden commands that may contain mature content or experimental features.
+
+
+ Use at your own discretion. Commands in this section are disabled by default.
+
+
+
+
+ }
+ value={storage.enabledCommands.lovefemboys}
+ onValueChange={(v) => {
+ storage.enabledCommands.lovefemboys = v;
+ storage.pendingRestart = true;
+ forceUpdate();
+ }}
+ />
+
+
+
+ }
+ value={storage.hiddenSettings.visible}
+ onValueChange={(v) => {
+ storage.hiddenSettings.visible = v;
+ }}
+ />
+ }
+ onPress={() => {
+ alerts.showConfirmationAlert({
+ title: "Reset Hidden Settings",
+ content: "This will hide the hidden settings section and disable all hidden commands. Are you sure?",
+ confirmText: "Reset",
+ onConfirm: () => {
+ storage.hiddenSettings.enabled = false;
+ storage.hiddenSettings.visible = false;
+ storage.enabledCommands.lovefemboys = false;
+ storage.pendingRestart = true;
+ showToast("Hidden settings reset", getAssetIDByName("CheckmarkIcon"));
+ },
+ cancelText: "Cancel",
+ });
+ }}
+ />
+
+
+
+ );
+}
+
+// Facts Commands Settings Page
+function FactsSettingsPage() {
+ useProxy(storage);
+ const [, forceUpdate] = React.useReducer((x) => x + 1, 0);
+
+ return (
+
+
+
+ }
+ value={storage.factSettings.sendAsReply}
+ onValueChange={(v) => {
+ storage.factSettings.sendAsReply = v;
+ }}
+ />
+ }
+ value={storage.factSettings.includeCitation}
+ onValueChange={(v) => {
+ storage.factSettings.includeCitation = v;
+ }}
+ />
+
+
+
+ }
+ value={storage.enabledCommands.catfact}
+ onValueChange={(v) => {
+ storage.enabledCommands.catfact = v;
+ storage.pendingRestart = true;
+ forceUpdate();
+ }}
+ />
+ }
+ value={storage.enabledCommands.dogfact}
+ onValueChange={(v) => {
+ storage.enabledCommands.dogfact = v;
+ storage.pendingRestart = true;
+ forceUpdate();
+ }}
+ />
+ }
+ value={storage.enabledCommands.useless}
+ onValueChange={(v) => {
+ storage.enabledCommands.useless = v;
+ storage.pendingRestart = true;
+ forceUpdate();
+ }}
+ />
+
+
+
+ );
+}
+
+// Gary API Settings Page
+function GaryAPIPage() {
+ useProxy(storage);
+ const [, forceUpdate] = React.useReducer((x) => x + 1, 0);
+
+ const styles = stylesheet.createThemedStyleSheet({
+ container: {
+ flex: 1,
+ backgroundColor: semanticColors.BACKGROUND_PRIMARY,
+ },
+ infoText: {
+ fontSize: 14,
+ color: semanticColors.TEXT_MUTED,
+ marginBottom: 16,
+ textAlign: "center",
+ },
+ currentText: {
+ fontSize: 14,
+ color: semanticColors.TEXT_NORMAL,
+ textAlign: "center",
+ fontWeight: "600",
+ },
+ });
+
+ const currentSource = storage.garySettings.imageSource;
+
+ return (
+
+
+
+ }
+ value={storage.enabledCommands.gary}
+ onValueChange={(v) => {
+ storage.enabledCommands.gary = v;
+ storage.pendingRestart = true;
+ forceUpdate();
+ }}
+ />
+
+
+
+
+ Choose which API the /gary command should use to fetch images.
+
+
+
+
+ }
+ value={currentSource === "gary"}
+ onValueChange={(v) => {
+ if (v) {
+ storage.garySettings.imageSource = "gary";
+ forceUpdate();
+ }
+ }}
+ />
+ }
+ value={currentSource === "catapi"}
+ onValueChange={(v) => {
+ if (v) {
+ storage.garySettings.imageSource = "catapi";
+ forceUpdate();
+ }
+ }}
+ />
+ }
+ value={currentSource === "minker"}
+ onValueChange={(v) => {
+ if (v) {
+ storage.garySettings.imageSource = "minker";
+ forceUpdate();
+ }
+ }}
+ />
+ }
+ value={currentSource === "goober"}
+ onValueChange={(v) => {
+ if (v) {
+ storage.garySettings.imageSource = "goober";
+ forceUpdate();
+ }
+ }}
+ />
+
+
+
+
+ Currently using: {
+ currentSource === "gary" ? "Gary API" :
+ currentSource === "catapi" ? "Cat API" :
+ currentSource === "minker" ? "Minker API" :
+ currentSource === "goober" ? "Goober API" : "Gary API"
+ }
+
+
+
+
+ );
+}
+
+// List Settings Page
+function ListSettingsPage() {
+ useProxy(storage);
+ const [, forceUpdate] = React.useReducer((x) => x + 1, 0);
+
+ return (
+
+
+
+ }
+ value={storage.listSettings.pluginListAlwaysDetailed}
+ onValueChange={(v) => {
+ storage.listSettings.pluginListAlwaysDetailed = v;
+ }}
+ />
+ }
+ value={storage.listSettings.themeListAlwaysDetailed}
+ onValueChange={(v) => {
+ storage.listSettings.themeListAlwaysDetailed = v;
+ }}
+ />
+
+
+
+ }
+ value={storage.enabledCommands.pluginList}
+ onValueChange={(v) => {
+ storage.enabledCommands.pluginList = v;
+ storage.pendingRestart = true;
+ forceUpdate();
+ }}
+ />
+ }
+ value={storage.enabledCommands.themeList}
+ onValueChange={(v) => {
+ storage.enabledCommands.themeList = v;
+ storage.pendingRestart = true;
+ forceUpdate();
+ }}
+ />
+
+
+
+ );
+}
+
+// Image Settings Page
+function ImageSettingsPage() {
+ useProxy(storage);
+ const [, forceUpdate] = React.useReducer((x) => x + 1, 0);
+
+ return (
+
+
+
+ }
+ value={storage.enabledCommands.petpet}
+ onValueChange={(v) => {
+ storage.enabledCommands.petpet = v;
+ storage.pendingRestart = true;
+ forceUpdate();
+ }}
+ />
+
+
+
+ }
+ value={storage.enabledCommands.konoself}
+ onValueChange={(v) => {
+ storage.enabledCommands.konoself = v;
+ storage.pendingRestart = true;
+ forceUpdate();
+ }}
+ />
+ }
+ value={storage.enabledCommands.konosend}
+ onValueChange={(v) => {
+ storage.enabledCommands.konosend = v;
+ storage.pendingRestart = true;
+ forceUpdate();
+ }}
+ />
+
+
+
+ );
+}
+
+// Spotify Settings Page
+function SpotifySettingsPage() {
+ useProxy(storage);
+ const [, forceUpdate] = React.useReducer((x) => x + 1, 0);
+
+ return (
+
+
+
+ }
+ value={storage.enabledCommands.spotifyTrack}
+ onValueChange={(v) => {
+ storage.enabledCommands.spotifyTrack = v;
+ storage.pendingRestart = true;
+ forceUpdate();
+ }}
+ />
+ }
+ value={storage.enabledCommands.spotifyAlbum}
+ onValueChange={(v) => {
+ storage.enabledCommands.spotifyAlbum = v;
+ storage.pendingRestart = true;
+ forceUpdate();
+ }}
+ />
+ }
+ value={storage.enabledCommands.spotifyArtists}
+ onValueChange={(v) => {
+ storage.enabledCommands.spotifyArtists = v;
+ storage.pendingRestart = true;
+ forceUpdate();
+ }}
+ />
+ }
+ value={storage.enabledCommands.spotifyCover}
+ onValueChange={(v) => {
+ storage.enabledCommands.spotifyCover = v;
+ storage.pendingRestart = true;
+ forceUpdate();
+ }}
+ />
+
+
+
+
+ These commands allow you to share your current Spotify activity in Discord. Make sure you have Spotify connected to Discord for these commands to work properly.
+
+
+
+
+ );
+}
+
+// Other Settings Page
+function OtherSettingsPage() {
+ useProxy(storage);
+ const [, forceUpdate] = React.useReducer((x) => x + 1, 0);
+
+ return (
+
+
+
+ }
+ value={storage.enabledCommands.firstmessage}
+ onValueChange={(v) => {
+ storage.enabledCommands.firstmessage = v;
+ storage.pendingRestart = true;
+ forceUpdate();
+ }}
+ />
+
+
+
+ }
+ value={storage.enabledCommands.sysinfo}
+ onValueChange={(v) => {
+ storage.enabledCommands.sysinfo = v;
+ storage.pendingRestart = true;
+ forceUpdate();
+ }}
+ />
+
+
+
+ );
+}
+
+// Credits Page
+function CreditsPage() {
+ const styles = stylesheet.createThemedStyleSheet({
+ container: {
+ flex: 1,
+ backgroundColor: semanticColors.BACKGROUND_PRIMARY,
+ },
+ creditItem: {
+ flexDirection: "row",
+ alignItems: "center",
+ paddingVertical: 16,
+ paddingHorizontal: 20,
+ backgroundColor: semanticColors.CARD_PRIMARY_BG,
+ borderRadius: 12,
+ marginHorizontal: 16,
+ marginVertical: 6,
+ shadowColor: semanticColors.BLACK,
+ shadowOffset: { width: 0, height: 1 },
+ shadowOpacity: 0.1,
+ shadowRadius: 2,
+ elevation: 2,
+ },
+ avatar: {
+ width: 48,
+ height: 48,
+ borderRadius: 24,
+ marginRight: 16,
+ backgroundColor: semanticColors.BACKGROUND_SECONDARY,
+ },
+ textContainer: {
+ flex: 1,
+ },
+ commandText: {
+ fontSize: 17,
+ fontWeight: "600",
+ color: semanticColors.TEXT_NORMAL,
+ marginBottom: 4,
+ },
+ authorText: {
+ fontSize: 15,
+ color: semanticColors.TEXT_MUTED,
+ marginBottom: 2,
+ },
+ linkText: {
+ fontSize: 13,
+ color: semanticColors.TEXT_BRAND,
+ fontWeight: "500",
+ },
+ infoText: {
+ fontSize: 14,
+ color: semanticColors.TEXT_MUTED,
+ marginBottom: 20,
+ textAlign: "center",
+ lineHeight: 20,
+ },
+ versionText: {
+ fontSize: 15,
+ color: semanticColors.TEXT_NORMAL,
+ textAlign: "center",
+ fontWeight: "600",
+ lineHeight: 22,
+ },
+ });
+
+ const credits = [
+ {
+ command: "Facts Commands",
+ author: "jdev082",
+ avatar: "https://github.com/jdev082.png",
+ github: "https://github.com/jdev082"
+ },
+ {
+ command: "List Commands",
+ author: "Kitomanari",
+ avatar: "https://github.com/Kitosight.png",
+ github: "https://github.com/Kitosight"
+ },
+ {
+ command: "PetPet",
+ author: "wolfieeee",
+ avatar: "https://github.com/WolfPlugs.png",
+ github: "https://github.com/WolfPlugs"
+ },
+ {
+ command: "KonoChan Commands",
+ author: "btmc727 & Rico040",
+ avatar: "https://github.com/OTKUSteyler.png",
+ github: "https://github.com/OTKUSteyler"
+ },
+ {
+ command: "FirstMessage Command",
+ author: "sapphire",
+ avatar: "https://github.com/aeongdesu.png",
+ github: "https://github.com/aeongdesu"
+ },
+ {
+ command: "Sysinfo Command",
+ author: "mugman",
+ avatar: "https://github.com/mugman174.png",
+ github: "https://github.com/mugman174"
+ },
+ {
+ command: "Spotify Commands",
+ author: "Kitomanari",
+ avatar: "https://github.com/Kitosight.png",
+ github: "https://github.com/Kitosight"
+ },
+ {
+ command: "Gary Command",
+ author: "Zach Orange",
+ avatar: "https://github.com/Zach11111.png",
+ github: "https://github.com/Zach11111"
+ },
+ ];
+
+ const handleProfilePress = (githubUrl: string) => {
+ RN.Linking.openURL(githubUrl);
+ };
+
+ return (
+
+
+
+
+ Thanks to this developers for creating such a nice plugins!{'\n'}
+ Tap on any developer to visit their GitHub profile.
+
+
+
+ {credits.map((credit, index) => (
+ handleProfilePress(credit.github)}
+ android_ripple={{ color: semanticColors.ANDROID_RIPPLE, radius: 200 }}
+ >
+
+
+ {credit.command}
+ by {credit.author}
+ {credit.github.replace('https://github.com/', '@')}
+
+
+ ))}
+
+
+
+ Commands Plugin Collection{'\n'}
+ Version 1.0.0
+
+
+
+
+ );
+}
+
+// MAIN SETTINGS COMPONENT
+export default function Settings() {
+ useProxy(storage);
+ const navigation = NavigationNative.useNavigation();
+ const [, forceUpdate] = React.useReducer((x) => x + 1, 0);
+
+ React.useEffect(() => {
+ return () => {
+ if (storage.pendingRestart) {
+ storage.pendingRestart = false;
+ alerts.showConfirmationAlert({
+ title: "Restart Required",
+ content:
+ "You have made changes to commands. Please restart Discord to apply these changes.",
+ confirmText: "Okay",
+ cancelText: null,
+ });
+ }
+ };
+ }, []);
+
+ return (
+
+
+
+
+ }
+ trailing={}
+ onPress={() => navigation.push("VendettaCustomPage", {
+ title: "Facts Commands",
+ render: FactsSettingsPage,
+ })}
+ />
+ }
+ trailing={}
+ onPress={() => navigation.push("VendettaCustomPage", {
+ title: "List Commands",
+ render: ListSettingsPage,
+ })}
+ />
+ }
+ trailing={}
+ onPress={() => navigation.push("VendettaCustomPage", {
+ title: "Image Commands",
+ render: ImageSettingsPage,
+ })}
+ />
+ }
+ trailing={}
+ onPress={() => navigation.push("VendettaCustomPage", {
+ title: "Gary Commands",
+ render: GaryAPIPage,
+ })}
+ />
+ }
+ trailing={}
+ onPress={() => navigation.push("VendettaCustomPage", {
+ title: "Spotify Commands",
+ render: SpotifySettingsPage,
+ })}
+ />
+ }
+ trailing={}
+ onPress={() => navigation.push("VendettaCustomPage", {
+ title: "Other Commands",
+ render: OtherSettingsPage,
+ })}
+ />
+
+
+ {storage.hiddenSettings?.enabled && storage.hiddenSettings?.visible && (
+
+ }
+ trailing={}
+ onPress={() => navigation.push("VendettaCustomPage", {
+ title: "Hidden Settings",
+ render: HiddenSettingsPage,
+ })}
+ />
+
+ )}
+
+
+ }
+ trailing={}
+ onPress={() => navigation.push("VendettaCustomPage", {
+ title: "Credits",
+ render: CreditsPage,
+ })}
+ />
+
+
+
+
+ );
+}