/* * Vencord, a Discord client mod * Copyright (c) 2024 Vendicated and contributors * SPDX-License-Identifier: GPL-3.0-or-later */ import { definePluginSettings } from "@api/Settings"; import { Button } from "@components/Button"; import { SettingsSection } from "@components/settings/tabs/plugins/components/Common"; import { Switch } from "@components/Switch"; import { useForceUpdater } from "@utils/react"; import { OptionType } from "@utils/types"; import { React, Select, showToast, TextArea, TextInput, Toasts } from "@webpack/common"; import { ServiceType } from "./types"; import { parseShareXConfig } from "./utils/sharex"; const serviceOptions = [ { label: "Zipline", value: ServiceType.ZIPLINE, default: true }, { label: "E-Z Host", value: ServiceType.EZHOST }, { label: "Nest", value: ServiceType.NEST }, { label: "S3-Compatible", value: ServiceType.S3 }, { label: "Catbox.moe", value: ServiceType.CATBOX }, ...(IS_DISCORD_DESKTOP ? [{ label: "0x0.st", value: ServiceType.ZEROX0 }] : []), { label: "Litterbox", value: ServiceType.LITTERBOX }, { label: "ShareX Custom Uploader", value: ServiceType.SHAREX } ]; const litterboxOptions = [ { label: "1 hour", value: "1h" }, { label: "12 hours", value: "12h" }, { label: "24 hours", value: "24h", default: true }, { label: "72 hours", value: "72h" } ]; export const settings = definePluginSettings({ serviceType: { type: OptionType.SELECT, description: "Selected uploader service", options: serviceOptions, hidden: true }, serviceUrl: { type: OptionType.STRING, description: "Zipline service URL", default: "", hidden: true }, ziplineToken: { type: OptionType.STRING, description: "Zipline auth token", default: "", hidden: true }, folderId: { type: OptionType.STRING, description: "Optional Zipline folder ID", default: "", hidden: true }, ezHostKey: { type: OptionType.STRING, description: "E-Z Host API key", default: "", hidden: true }, nestToken: { type: OptionType.STRING, description: "Nest API token", default: "", hidden: true }, s3Endpoint: { type: OptionType.STRING, description: "S3-compatible endpoint URL", default: "", hidden: true }, s3Bucket: { type: OptionType.STRING, description: "S3 bucket name", default: "", hidden: true }, s3Region: { type: OptionType.STRING, description: "S3 region (use auto for R2)", default: "auto", hidden: true }, s3AccessKeyId: { type: OptionType.STRING, description: "S3 access key ID", default: "", hidden: true }, s3SecretAccessKey: { type: OptionType.STRING, description: "S3 secret access key", default: "", hidden: true }, s3SessionToken: { type: OptionType.STRING, description: "Optional S3 session token", default: "", hidden: true }, s3PublicUrl: { type: OptionType.STRING, description: "Optional public base URL", default: "", hidden: true }, s3Prefix: { type: OptionType.STRING, description: "Optional S3 object key prefix", default: "", hidden: true }, s3ForcePathStyle: { type: OptionType.BOOLEAN, description: "Use path-style S3 URLs", default: true, hidden: true }, litterboxExpiry: { type: OptionType.SELECT, description: "Litterbox retention window", options: litterboxOptions, default: "24h", hidden: true }, catboxUserhash: { type: OptionType.STRING, description: "Catbox userhash for account binding", default: "", hidden: true }, sharexConfig: { type: OptionType.STRING, description: "ShareX custom uploader JSON", default: "", hidden: true }, stripQueryParams: { type: OptionType.BOOLEAN, description: "Strip query params from uploaded URLs", default: false, hidden: true }, apngToGif: { type: OptionType.BOOLEAN, description: "Convert APNG uploads to GIF", default: false, hidden: true }, autoCopy: { type: OptionType.BOOLEAN, description: "Auto copy upload URL", default: true, hidden: true }, settingsComponent: { type: OptionType.COMPONENT, description: "Settings", component: SettingsComponent } }); function SettingTextInput(props: { description: string; name: string; onChange: (value: string) => void; placeholder: string; value: string; }) { const { description, name, onChange, placeholder, value } = props; return ( ); } export function SettingsComponent() { const update = useForceUpdater(); const { store } = settings; const sharexFileInputRef = React.useRef(null); const isNest = store.serviceType === ServiceType.NEST; const isEzHost = store.serviceType === ServiceType.EZHOST; const isS3 = store.serviceType === ServiceType.S3; const isZipline = store.serviceType === ServiceType.ZIPLINE; const isCatbox = store.serviceType === ServiceType.CATBOX; const isLitterbox = store.serviceType === ServiceType.LITTERBOX; const isShareX = store.serviceType === ServiceType.SHAREX; const validateShareXConfig = () => { try { parseShareXConfig(store.sharexConfig || ""); showToast("ShareX config is valid", Toasts.Type.SUCCESS); } catch (error) { const message = error instanceof Error ? error.message : "Invalid ShareX config"; showToast(message, Toasts.Type.FAILURE); } }; const triggerShareXFileUpload = () => { sharexFileInputRef.current?.click(); }; const handleShareXFileUpload = (event: React.ChangeEvent) => { const file = event.target.files?.[0]; if (!file) return; const reader = new FileReader(); reader.onload = (e: ProgressEvent) => { try { const content = String(e.target?.result || ""); const parsed = parseShareXConfig(content); store.sharexConfig = JSON.stringify(parsed, null, 2); update(); showToast("Imported ShareX config", Toasts.Type.SUCCESS); } catch (error) { const message = error instanceof Error ? error.message : "Failed to import ShareX config"; showToast(message, Toasts.Type.FAILURE); } }; reader.readAsText(file); event.target.value = ""; }; return ( <> v === store.litterboxExpiry} select={v => { store.litterboxExpiry = v; update(); }} serialize={v => v} placeholder="Select expiry" /> )} {isShareX && ( <>