index.tsx raw
1 import { Button } from '@/components/ui/button'
2 import { normalizeUrl } from '@/lib/url'
3 import { useNostr } from '@/providers/NostrProvider'
4 import { TMailboxRelay, TMailboxRelayScope } from '@/types'
5 import { useEffect, useState } from 'react'
6 import { useTranslation } from 'react-i18next'
7 import {
8 DndContext,
9 closestCenter,
10 KeyboardSensor,
11 PointerSensor,
12 TouchSensor,
13 useSensor,
14 useSensors,
15 DragEndEvent
16 } from '@dnd-kit/core'
17 import {
18 arrayMove,
19 SortableContext,
20 sortableKeyboardCoordinates,
21 verticalListSortingStrategy
22 } from '@dnd-kit/sortable'
23 import { restrictToVerticalAxis, restrictToParentElement } from '@dnd-kit/modifiers'
24 import MailboxRelay from './MailboxRelay'
25 import NewMailboxRelayInput from './NewMailboxRelayInput'
26 import RelayCountWarning from './RelayCountWarning'
27 import SaveButton from './SaveButton'
28
29 export default function MailboxSetting() {
30 const { t } = useTranslation()
31 const { pubkey, relayList, checkLogin } = useNostr()
32 const [relays, setRelays] = useState<TMailboxRelay[]>([])
33 const [hasChange, setHasChange] = useState(false)
34
35 const sensors = useSensors(
36 useSensor(PointerSensor, {
37 activationConstraint: {
38 distance: 8
39 }
40 }),
41 useSensor(TouchSensor, {
42 activationConstraint: {
43 delay: 200,
44 tolerance: 8
45 }
46 }),
47 useSensor(KeyboardSensor, {
48 coordinateGetter: sortableKeyboardCoordinates
49 })
50 )
51
52 function handleDragEnd(event: DragEndEvent) {
53 const { active, over } = event
54
55 if (active.id !== over?.id) {
56 const oldIndex = relays.findIndex((relay) => relay.url === active.id)
57 const newIndex = relays.findIndex((relay) => relay.url === over?.id)
58
59 if (oldIndex !== -1 && newIndex !== -1) {
60 setRelays((relays) => arrayMove(relays, oldIndex, newIndex))
61 setHasChange(true)
62 }
63 }
64 }
65
66 useEffect(() => {
67 if (!relayList) return
68
69 setRelays(relayList.originalRelays)
70 }, [relayList])
71
72 if (!pubkey) {
73 return (
74 <div className="flex flex-col w-full items-center">
75 <Button size="lg" onClick={() => checkLogin()}>
76 {t('Login to set')}
77 </Button>
78 </div>
79 )
80 }
81
82 if (!relayList) {
83 return <div className="text-center text-sm text-muted-foreground">{t('loading...')}</div>
84 }
85
86 const changeMailboxRelayScope = (url: string, scope: TMailboxRelayScope) => {
87 setRelays((prev) => prev.map((r) => (r.url === url ? { ...r, scope } : r)))
88 setHasChange(true)
89 }
90
91 const removeMailboxRelay = (url: string) => {
92 setRelays((prev) => prev.filter((r) => r.url !== url))
93 setHasChange(true)
94 }
95
96 const saveNewMailboxRelay = (url: string) => {
97 if (url === '') return null
98 const normalizedUrl = normalizeUrl(url)
99 if (!normalizedUrl) {
100 return t('Invalid relay URL')
101 }
102 if (relays.some((r) => r.url === normalizedUrl)) {
103 return t('Relay already exists')
104 }
105 setRelays([...relays, { url: normalizedUrl, scope: 'both' }])
106 setHasChange(true)
107 return null
108 }
109
110 return (
111 <div className="space-y-4">
112 <div className="text-xs text-muted-foreground space-y-1">
113 <div>{t('read relays description')}</div>
114 <div>{t('write relays description')}</div>
115 <div>{t('read & write relays notice')}</div>
116 </div>
117 <RelayCountWarning relays={relays} />
118 <SaveButton mailboxRelays={relays} hasChange={hasChange} setHasChange={setHasChange} />
119 <DndContext
120 sensors={sensors}
121 collisionDetection={closestCenter}
122 onDragEnd={handleDragEnd}
123 modifiers={[restrictToVerticalAxis, restrictToParentElement]}
124 >
125 <SortableContext items={relays.map((r) => r.url)} strategy={verticalListSortingStrategy}>
126 <div className="space-y-2">
127 {relays.map((relay) => (
128 <MailboxRelay
129 key={relay.url}
130 mailboxRelay={relay}
131 changeMailboxRelayScope={changeMailboxRelayScope}
132 removeMailboxRelay={removeMailboxRelay}
133 />
134 ))}
135 </div>
136 </SortableContext>
137 </DndContext>
138 <NewMailboxRelayInput saveNewMailboxRelay={saveNewMailboxRelay} />
139 </div>
140 )
141 }
142