import { configStorage } from '@/lib/configStorage' import type { RelayConfig } from '@/lib/configStorageTypes' import { t } from '@/lib/i18n' import { relaySessionManager } from '@/lib/relaySessionManager' import { userConfirm } from '@/lib/userConfirm' export async function loadRelays(params: { setRelays: (value: RelayConfig[]) => void setLoading: (value: boolean) => void setError: (value: string | null) => void }): Promise { try { params.setLoading(true) params.setError(null) const config = await configStorage.getConfig() const updated = await maybeDisableFailedRelays(config.relays) params.setRelays(sortByPriority(updated)) } catch (e) { const errorMessage = e instanceof Error ? e.message : t('settings.relay.error.loadFailed') params.setError(errorMessage) console.error('[RelayManager] Error loading relays:', e) } finally { params.setLoading(false) } } export async function toggleEnabled(params: { id: string enabled: boolean setRelays: (value: RelayConfig[]) => void setError: (value: string | null) => void onConfigChange: (() => void) | undefined }): Promise { try { await configStorage.updateRelay(params.id, { enabled: params.enabled }) await reloadRelays({ setRelays: params.setRelays, setError: params.setError }) params.onConfigChange?.() } catch (e) { const errorMessage = e instanceof Error ? e.message : t('settings.relay.error.updateFailed') params.setError(errorMessage) console.error('[RelayManager] Error updating relay:', e) } } export async function updateUrl(params: { id: string url: string setRelays: (value: RelayConfig[]) => void setError: (value: string | null) => void setEditingId: (value: string | null) => void onConfigChange: (() => void) | undefined }): Promise { try { await configStorage.updateRelay(params.id, { url: params.url }) await reloadRelays({ setRelays: params.setRelays, setError: params.setError }) params.setEditingId(null) params.onConfigChange?.() } catch (e) { const errorMessage = e instanceof Error ? e.message : t('settings.relay.error.urlFailed') params.setError(errorMessage) console.error('[RelayManager] Error updating URL:', e) } } export async function addRelay(params: { newUrl: string setError: (value: string | null) => void setNewUrl: (value: string) => void setShowAddForm: (value: boolean | ((prev: boolean) => boolean)) => void setRelays: (value: RelayConfig[]) => void onConfigChange: (() => void) | undefined }): Promise { const normalizedUrl = normalizeRelayUrl(params.newUrl) if (!normalizedUrl) { params.setError(t('settings.relay.error.urlRequired')) return } try { void new URL(normalizedUrl) await configStorage.addRelay(normalizedUrl, true) params.setNewUrl('') params.setShowAddForm(false) await reloadRelays({ setRelays: params.setRelays, setError: params.setError }) params.onConfigChange?.() } catch (e) { params.setError(getAddRelayErrorMessage(e)) console.error('[RelayManager] Error adding relay:', e) } } export async function removeRelay(params: { id: string setRelays: (value: RelayConfig[]) => void setError: (value: string | null) => void onConfigChange: (() => void) | undefined }): Promise { const confirmed = await userConfirm(t('settings.relay.remove.confirm')) if (!confirmed) { return } try { await configStorage.removeRelay(params.id) await reloadRelays({ setRelays: params.setRelays, setError: params.setError }) params.onConfigChange?.() } catch (e) { const errorMessage = e instanceof Error ? e.message : t('settings.relay.error.removeFailed') params.setError(errorMessage) console.error('[RelayManager] Error removing relay:', e) } } export async function updatePriorities(params: { relays: RelayConfig[] setError: (value: string | null) => void onConfigChange: (() => void) | undefined }): Promise { try { const updates = params.relays.map((relay, index) => { const priority = index + 1 return relay.priority !== priority ? configStorage.updateRelay(relay.id, { priority }) : Promise.resolve() }) await Promise.all(updates) params.onConfigChange?.() } catch (e) { const errorMessage = e instanceof Error ? e.message : t('settings.relay.error.priorityFailed') params.setError(errorMessage) console.error('[RelayManager] Error updating priorities:', e) } } export function onDragStart(params: { e: React.DragEvent id: string setDraggedId: (value: string | null) => void }): void { params.setDraggedId(params.id) const { dataTransfer } = params.e dataTransfer.effectAllowed = 'move' dataTransfer.setData('text/plain', params.id) } export function onDragOver(params: { e: React.DragEvent id: string setDragOverId: (value: string | null) => void }): void { params.e.preventDefault() const { dataTransfer } = params.e dataTransfer.dropEffect = 'move' params.setDragOverId(params.id) } export async function onDrop(params: { e: React.DragEvent targetId: string relays: RelayConfig[] draggedId: string | null setRelays: (value: RelayConfig[]) => void setDraggedId: (value: string | null) => void setDragOverId: (value: string | null) => void setError: (value: string | null) => void onConfigChange: (() => void) | undefined }): Promise { params.e.preventDefault() params.setDragOverId(null) if (!params.draggedId || params.draggedId === params.targetId) { params.setDraggedId(null) return } const reordered = reorderRelays({ relays: params.relays, draggedId: params.draggedId, targetId: params.targetId }) if (!reordered) { params.setDraggedId(null) return } params.setRelays(reordered) params.setDraggedId(null) await updatePriorities({ relays: reordered, setError: params.setError, onConfigChange: params.onConfigChange }) } export function getRelayCardClassName(relayId: string, draggedId: string | null, dragOverId: string | null): string { if (draggedId === relayId) { return 'opacity-50 border-neon-cyan' } if (dragOverId === relayId) { return 'border-neon-green shadow-lg' } return 'border-neon-cyan/30' } function sortByPriority(relays: RelayConfig[]): RelayConfig[] { return relays.sort((a, b) => a.priority - b.priority) } async function reloadRelays(params: { setRelays: (value: RelayConfig[]) => void; setError: (value: string | null) => void }): Promise { try { const config = await configStorage.getConfig() params.setRelays(sortByPriority(config.relays)) } catch (e) { const errorMessage = e instanceof Error ? e.message : t('settings.relay.error.loadFailed') params.setError(errorMessage) console.error('[RelayManager] Error reloading relays:', e) } } async function maybeDisableFailedRelays(relays: RelayConfig[]): Promise { const failedRelays = relaySessionManager.getFailedRelays() if (failedRelays.length === 0) { return relays } const toDisable = relays.filter((relay) => relay.enabled && failedRelays.includes(relay.url)) if (toDisable.length === 0) { return relays } for (const relay of toDisable) { await configStorage.updateRelay(relay.id, { enabled: false }) } const updated = await configStorage.getConfig() return updated.relays } function normalizeRelayUrl(rawUrl: string): string | null { const trimmed = rawUrl.trim() if (trimmed.length === 0) { return null } if (trimmed.startsWith('ws://') || trimmed.startsWith('wss://')) { return trimmed } return `wss://${trimmed}` } function getAddRelayErrorMessage(error: unknown): string { if (error instanceof TypeError && error.message.includes('Invalid URL')) { return t('settings.relay.error.invalidUrl') } if (error instanceof Error) { return error.message } return t('settings.relay.error.addFailed') } function reorderRelays(params: { relays: RelayConfig[]; draggedId: string; targetId: string }): RelayConfig[] | null { const draggedIndex = params.relays.findIndex((relay) => relay.id === params.draggedId) const targetIndex = params.relays.findIndex((relay) => relay.id === params.targetId) if (draggedIndex === -1 || targetIndex === -1) { return null } const next = [...params.relays] const removed = next[draggedIndex] if (!removed) { return null } next.splice(draggedIndex, 1) next.splice(targetIndex, 0, removed) return next }