257 lines
8.4 KiB
TypeScript
257 lines
8.4 KiB
TypeScript
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<void> {
|
|
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<void> {
|
|
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<void> {
|
|
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<void> {
|
|
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<void> {
|
|
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<void> {
|
|
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<HTMLDivElement>
|
|
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<HTMLDivElement>
|
|
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<HTMLDivElement>
|
|
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<void> {
|
|
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<void> {
|
|
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<RelayConfig[]> {
|
|
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
|
|
}
|