"use client"; import { useEffect, useMemo, useState } from "react"; import { NotificationSettings, NotificationProvider } from "@/types/notifications"; import { scheduleTestNotification, scheduleTestBackupNotification } from "@/utils/scheduler"; export interface SettingsModalProps { open: boolean; initial?: NotificationSettings; onClose: () => void; onSave: (settings: NotificationSettings) => void; } const ENV_NTFY = (process.env.NEXT_PUBLIC_NTFY_URL || '/ntfy').replace(/\/$/, ''); const defaultSettings: NotificationSettings = { provider: "", ntfyServer: ENV_NTFY, ntfyTopic: "", gotifyServer: "", gotifyToken: "", email: "", backupEmail: "", backupDelayDays: 1, daysBefore: 10, }; export default function SettingsModal({ open, initial, onClose, onSave }: SettingsModalProps) { const [form, setForm] = useState(initial || defaultSettings); const [testStatus, setTestStatus] = useState(""); const [rpcBase, setRpcBase] = useState(""); const [rpcArbitrum, setRpcArbitrum] = useState(""); const [rpcMainnet, setRpcMainnet] = useState(""); const ENV_NFTCACHE = (process.env.NEXT_PUBLIC_NFTCACHE_URL || '/nftcache').replace(/\/$/, ''); const [nftcacheEnabled, setNftcacheEnabled] = useState(false); const [nftcacheBaseUrl, setNftcacheBaseUrl] = useState(ENV_NFTCACHE); const [nftcacheApiKey, setNftcacheApiKey] = useState(''); useEffect(() => { if (open) { const merged = { ...defaultSettings, ...(initial || {}) } as NotificationSettings; if (!merged.ntfyServer) merged.ntfyServer = ENV_NTFY; setForm(merged); try { const ls = typeof window !== 'undefined' ? window.localStorage : undefined; const b = ls?.getItem('rpc:base') || ''; const a = ls?.getItem('rpc:arbitrum') || ''; const m = ls?.getItem('rpc:mainnet') || ''; setRpcBase(b || 'https://base.llamarpc.com'); setRpcArbitrum(a || ''); setRpcMainnet(m || ''); const nEn = ls?.getItem('nftcache:enabled'); const nUrl = ls?.getItem('nftcache:baseUrl'); const nKey = ls?.getItem('nftcache:apiKey'); setNftcacheEnabled(nEn === '1'); setNftcacheBaseUrl((nUrl && nUrl.trim()) || ENV_NFTCACHE); setNftcacheApiKey(nKey || ''); } catch {} } // eslint-disable-next-line react-hooks/exhaustive-deps }, [open, initial]); const provider: NotificationProvider | '' = form.provider || ''; const saveDisabled = useMemo(() => { if (!form.email) return true; const db = form.daysBefore ?? 10; if (Number(db) < 0) return true; const hasBackup = Boolean((form.backupEmail || '').trim()); if (hasBackup) { const bdd = Number(form.backupDelayDays); if (!Number.isFinite(bdd) || bdd < 1) return true; } if (!provider) return true; if (provider === 'ntfy') return !(form.ntfyServer && form.ntfyTopic); if (provider === 'gotify') return !(form.gotifyServer && form.gotifyToken); return false; }, [form, provider]); const canTest = useMemo(() => { if (saveDisabled) return false; return provider === 'ntfy'; }, [saveDisabled, provider]); const onTest = async () => { setTestStatus('Scheduling test…'); try { const { jobId } = await scheduleTestNotification(form); setTestStatus(`Test scheduled (job ${jobId}). You should receive a notification shortly.`); } catch (e: any) { setTestStatus(`Test failed: ${e?.message || String(e)}`); } }; const onTestBackup = async () => { setTestStatus('Scheduling backup test…'); try { const { jobId } = await scheduleTestBackupNotification(form); setTestStatus(`Backup test scheduled (job ${jobId}). You should receive a backup email shortly.`); } catch (e: any) { setTestStatus(`Backup test failed: ${e?.message || String(e)}`); } }; if (!open) return null; return (
🔔Notification Settings

Configure your notification provider, RPC and NFTCache.

Settings are stored locally in your browser. Do not use this on shared computers.
{/* Section: Basics */}
Basics
{/* Section: Email & Timing */}
Email & Timing
{/* Section: Provider-specific */}
Provider Settings
{provider === 'ntfy' && ( <> )} {provider === 'gotify' && ( <> )}
{/* Section: RPC endpoints */}
RPC endpoints
Only use HTTPS RPC endpoints you trust. Malicious RPCs can show fake balances and hide liquidation warnings.
{/* Section: NFTCache */}
NFTCache
{testStatus && (
{testStatus}
)}
); }