2026-01-14 11:05:27 +01:00

137 lines
4.9 KiB
TypeScript

import type { RelayConfig } from '@/lib/configStorageTypes'
import { Button, Card, Input } from '../ui'
import { t } from '@/lib/i18n'
import { DragHandle } from '../DragHandle'
import { getRelayCardClassName } from './controller'
export function RelayCard(params: {
relay: RelayConfig
isEditing: boolean
draggedId: string | null
dragOverId: string | null
onStartEditing: (id: string) => void
onStopEditing: () => void
onUpdateUrl: (id: string, url: string) => void
onToggleEnabled: (id: string, enabled: boolean) => void
onRemoveRelay: (id: string) => void
onDragStart: (e: React.DragEvent<HTMLDivElement>, id: string) => void
onDragOver: (e: React.DragEvent<HTMLDivElement>, id: string) => void
onDragLeave: () => void
onDrop: (e: React.DragEvent<HTMLDivElement>, targetId: string) => void
onDragEnd: () => void
}): React.ReactElement {
return (
<div
onDragOver={(e: React.DragEvent<HTMLDivElement>) => params.onDragOver(e, params.relay.id)}
onDragLeave={params.onDragLeave}
onDrop={(e: React.DragEvent<HTMLDivElement>) => params.onDrop(e, params.relay.id)}
className={getRelayCardClassName(params.relay.id, params.draggedId, params.dragOverId)}
>
<Card variant="default" className="bg-cyber-dark space-y-3 transition-all">
<div className="flex items-start justify-between gap-4">
<div className="flex items-center gap-3 flex-1">
<DragGrip relayId={params.relay.id} onDragStart={params.onDragStart} onDragEnd={params.onDragEnd} />
<UrlCell relay={params.relay} isEditing={params.isEditing} onStartEditing={params.onStartEditing} onStopEditing={params.onStopEditing} onUpdateUrl={params.onUpdateUrl} />
{params.relay.lastSyncDate ? <LastSync lastSyncDate={params.relay.lastSyncDate} /> : null}
</div>
<ActionsCell relay={params.relay} onToggleEnabled={params.onToggleEnabled} onRemoveRelay={params.onRemoveRelay} />
</div>
</Card>
</div>
)
}
function DragGrip(params: {
relayId: string
onDragStart: (e: React.DragEvent<HTMLDivElement>, id: string) => void
onDragEnd: () => void
}): React.ReactElement {
return (
<div
className="drag-handle cursor-grab active:cursor-grabbing"
draggable
onDragStart={(e) => params.onDragStart(e, params.relayId)}
onDragEnd={params.onDragEnd}
onMouseDown={(e) => e.stopPropagation()}
>
<DragHandle />
</div>
)
}
function UrlCell(params: {
relay: RelayConfig
isEditing: boolean
onStartEditing: (id: string) => void
onStopEditing: () => void
onUpdateUrl: (id: string, url: string) => void
}): React.ReactElement {
if (params.isEditing) {
return <UrlEditor relay={params.relay} onStop={params.onStopEditing} onUpdate={params.onUpdateUrl} />
}
return (
<div className="text-neon-cyan cursor-pointer hover:text-neon-green transition-colors" onClick={() => params.onStartEditing(params.relay.id)} title={t('settings.relay.list.editUrl')}>
{params.relay.url}
</div>
)
}
function UrlEditor(params: { relay: RelayConfig; onStop: () => void; onUpdate: (id: string, url: string) => void }): React.ReactElement {
return (
<Input
type="text"
defaultValue={params.relay.url}
onBlur={(e) => {
const next = e.target.value
if (next !== params.relay.url) {
params.onUpdate(params.relay.id, next)
} else {
params.onStop()
}
}}
onKeyDown={(e) => {
if (e.key === 'Enter') {
e.currentTarget.blur()
} else if (e.key === 'Escape') {
params.onStop()
}
}}
className="w-full bg-cyber-darker border-neon-cyan/50 text-cyber-light"
autoFocus
/>
)
}
function LastSync(params: { lastSyncDate: number }): React.ReactElement {
return (
<div className="text-xs text-cyber-accent/70 mt-1">
{t('settings.relay.list.lastSync')}: {new Date(params.lastSyncDate).toLocaleString()}
</div>
)
}
function ActionsCell(params: { relay: RelayConfig; onToggleEnabled: (id: string, enabled: boolean) => void; onRemoveRelay: (id: string) => void }): React.ReactElement {
return (
<div className="flex items-center gap-2">
<label className="flex items-center gap-2 cursor-pointer">
<input
type="checkbox"
checked={params.relay.enabled}
onChange={(e) => params.onToggleEnabled(params.relay.id, e.target.checked)}
className="w-4 h-4 text-neon-cyan bg-cyber-darker border-cyber-accent rounded focus:ring-neon-cyan"
/>
<span className="text-sm text-cyber-accent">{params.relay.enabled ? t('settings.relay.list.enabled') : t('settings.relay.list.disabled')}</span>
</label>
<Button
type="button"
variant="danger"
size="small"
onClick={() => params.onRemoveRelay(params.relay.id)}
aria-label={t('settings.relay.list.remove')}
>
{t('settings.relay.list.remove')}
</Button>
</div>
)
}