From 83275c3c012ce0b2b2e1abf430a70d5622d6b316 Mon Sep 17 00:00:00 2001 From: kmmiio99o Date: Wed, 1 Oct 2025 21:42:32 +0200 Subject: [PATCH] ehh --- plugins/Commands/index.ts | 2 +- plugins/Commands/settings.tsx | 1273 +++++++++++++++++++++++++-------- 2 files changed, 979 insertions(+), 296 deletions(-) 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, + })} + /> + + + + + ); +}