import { ChevronRightIcon, PlusIcon, SaveIcon, Trash2Icon, ArrowDownIcon, ArrowUpIcon, } from "lucide-react" import { useEffect, useState } from "react" import { useNavigate } from "@tanstack/react-router" import { useTranslation } from "react-i18next" import { toast } from "sonner" import { AppMainCard } from "@/components/common/app-main-card" import { MainCardHeader } from "@/components/common/main-card-header" import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" import { ScrollArea } from "@/components/ui/scroll-area" import { Switch } from "@/components/ui/switch" import { Textarea } from "@/components/ui/textarea" import { appActions, settingsStoreActions, useAppStore, useSettingsStore } from "@/store" import { SettingsSidebar } from "./settings-sidebar" import { allSettingsSections, applyUrlRewriteRules, buildSettingsSearchItems, buildSettingsUrlRewriteDraftRules, defaultSettingsConfig, filterSettingsSectionsBySearch, settingsSectionId, stripSettingsUrlRewriteDraftRules, type SettingsUrlRewriteDraftRule, } from "./utils" function createRuleId() { return `rule-${Date.now()}-${Math.floor(Math.random() * 1000)}` } function resolveErrorMessage(error: unknown, fallback: string) { if (error instanceof Error && error.message.trim().length > 0) { return error.message } return fallback } function moveRule( list: SettingsUrlRewriteDraftRule[], index: number, direction: -1 | 1 ) { const nextIndex = index + direction if (nextIndex < 0 || nextIndex >= list.length) { return list } const nextList = [...list] const [current] = nextList.splice(index, 1) nextList.splice(nextIndex, 0, current) return nextList } export function PicGoSettingsUrlRewrite() { const navigate = useNavigate() const { t } = useTranslation() const providers = useAppStore.use.providers() const appConfig = useAppStore.use.appConfig() const settingsSearchValue = useSettingsStore.use.searchValue() const settingsConfig = appConfig?.settings ?? defaultSettingsConfig const picBedProxy = appConfig?.picBed.proxy ?? "" const [draftRules, setDraftRules] = useState(null) const [previewInput, setPreviewInput] = useState("") const persistedRules = settingsConfig.urlRewrite.rules const rules = draftRules ?? buildSettingsUrlRewriteDraftRules(persistedRules) const searchItems = buildSettingsSearchItems(settingsConfig, providers, picBedProxy) const { matchedSections, matchedItemIds, hasQuery } = filterSettingsSectionsBySearch( allSettingsSections, searchItems, settingsSearchValue ) const previewOutput = previewInput.trim().length > 0 ? applyUrlRewriteRules(previewInput, stripSettingsUrlRewriteDraftRules(rules)) : "" useEffect(() => { let isDisposed = false async function bootstrap() { try { await appActions.ensureHydrated() await appActions.ensureSettingsHydrated() } catch (error) { if (!isDisposed) { toast.error(resolveErrorMessage(error, "Failed")) } } } bootstrap() return () => { isDisposed = true } }, []) const updateRules = ( updater: ( current: SettingsUrlRewriteDraftRule[] ) => SettingsUrlRewriteDraftRule[] ) => { setDraftRules((prev) => { const base = prev ?? buildSettingsUrlRewriteDraftRules(persistedRules) return updater(base) }) } const handleSave = async () => { try { await settingsStoreActions.saveUrlRewriteRules( stripSettingsUrlRewriteDraftRules(rules) ) setDraftRules(null) toast.success(t("TIPS_SET_SUCCEED")) } catch (error) { toast.error(resolveErrorMessage(error, t("FAILED"))) } } return ( <> navigate({ to: "/main/settings/settings", search: (prev) => ({ ...prev, section, }), }) } onNavigateRoute={(to) => navigate({ to })} />
{t("SETTINGS")} {t("SETTINGS_URL_REWRITE")} } trailing={ } />
{hasQuery && !matchedItemIds.has("url-rewrite-entry") ? (
{t("SETTINGS_NO_RESULTS_TITLE")}

{t("SETTINGS_NO_RESULTS_DESCRIPTION")}

) : (

{t("URL_REWRITE_HELP")}

{rules.length === 0 ? (
{t("URL_REWRITE_EMPTY")}
) : (
{rules.map((rule, index) => (
{t("URL_REWRITE_ORDER")} {index + 1}
{ const nextValue = event.target.value updateRules((prev) => prev.map((item) => item.id === rule.id ? { ...item, match: nextValue } : item ) ) }} placeholder={t("URL_REWRITE_MATCH_PLACEHOLDER")} />
{ const nextValue = event.target.value updateRules((prev) => prev.map((item) => item.id === rule.id ? { ...item, replace: nextValue } : item ) ) }} placeholder={t("URL_REWRITE_REPLACE_PLACEHOLDER")} />
))}
)}

{t("URL_REWRITE_PREVIEW_TITLE")}

setPreviewInput(event.target.value)} placeholder={t("URL_REWRITE_PREVIEW_PLACEHOLDER")} />