133 lines
4.9 KiB
TypeScript
133 lines
4.9 KiB
TypeScript
import type { RelayConfig } from '@/lib/configStorageTypes'
|
|
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) => params.onDragOver(e, params.relay.id)}
|
|
onDragLeave={params.onDragLeave}
|
|
onDrop={(e) => params.onDrop(e, params.relay.id)}
|
|
className={`bg-cyber-dark border rounded p-4 space-y-3 transition-all ${getRelayCardClassName(params.relay.id, params.draggedId, params.dragOverId)}`}
|
|
>
|
|
<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>
|
|
</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 px-3 py-2 bg-cyber-darker border border-neon-cyan/50 rounded text-cyber-light focus:border-neon-cyan focus:outline-none"
|
|
autoFocus
|
|
/>
|
|
)
|
|
}
|
|
|
|
function LastSync(params: { lastSyncDate: string }): 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"
|
|
onClick={() => params.onRemoveRelay(params.relay.id)}
|
|
className="px-3 py-1 text-sm bg-red-900/30 hover:bg-red-900/50 text-red-300 border border-red-500/50 rounded transition-colors"
|
|
title={t('settings.relay.list.remove')}
|
|
>
|
|
{t('settings.relay.list.remove')}
|
|
</button>
|
|
</div>
|
|
)
|
|
}
|