feat: support dnd to reorder relay sets
This commit is contained in:
17
src/components/FavoriteRelaysSetting/FavoriteRelayList.tsx
Normal file
17
src/components/FavoriteRelaysSetting/FavoriteRelayList.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import { useFavoriteRelays } from '@/providers/FavoriteRelaysProvider'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import RelayItem from './RelayItem'
|
||||
|
||||
export default function FavoriteRelayList() {
|
||||
const { t } = useTranslation()
|
||||
const { favoriteRelays } = useFavoriteRelays()
|
||||
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
<div className="text-muted-foreground font-semibold select-none">{t('Relays')}</div>
|
||||
{favoriteRelays.map((relay) => (
|
||||
<RelayItem key={relay} relay={relay} />
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -10,12 +10,15 @@ import { Input } from '@/components/ui/input'
|
||||
import { useFavoriteRelays } from '@/providers/FavoriteRelaysProvider'
|
||||
import { useScreenSize } from '@/providers/ScreenSizeProvider'
|
||||
import { TRelaySet } from '@/types'
|
||||
import { useSortable } from '@dnd-kit/sortable'
|
||||
import { CSS } from '@dnd-kit/utilities'
|
||||
import {
|
||||
Check,
|
||||
ChevronDown,
|
||||
Edit,
|
||||
EllipsisVertical,
|
||||
FolderClosed,
|
||||
GripVertical,
|
||||
Link,
|
||||
Trash2
|
||||
} from 'lucide-react'
|
||||
@@ -28,24 +31,44 @@ import { useRelaySetsSettingComponent } from './provider'
|
||||
export default function RelaySet({ relaySet }: { relaySet: TRelaySet }) {
|
||||
const { t } = useTranslation()
|
||||
const { expandedRelaySetId } = useRelaySetsSettingComponent()
|
||||
const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({
|
||||
id: relaySet.id
|
||||
})
|
||||
|
||||
const style = {
|
||||
transform: CSS.Transform.toString(transform),
|
||||
transition,
|
||||
opacity: isDragging ? 0.5 : 1
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="w-full border rounded-lg pl-4 pr-2 py-2.5">
|
||||
<div className="flex justify-between items-center">
|
||||
<div className="flex gap-2 items-center">
|
||||
<div className="flex justify-center items-center w-6 h-6 shrink-0">
|
||||
<FolderClosed className="size-4" />
|
||||
<div ref={setNodeRef} style={style} className="relative group">
|
||||
<div className="w-full border rounded-lg px-2 py-2.5">
|
||||
<div className="flex justify-between items-center">
|
||||
<div className="flex items-center">
|
||||
<div
|
||||
className="cursor-grab active:cursor-grabbing p-2 hover:bg-muted rounded touch-none"
|
||||
{...attributes}
|
||||
{...listeners}
|
||||
>
|
||||
<GripVertical className="size-4 text-muted-foreground" />
|
||||
</div>
|
||||
<div className="flex gap-2 items-center">
|
||||
<div className="flex justify-center items-center w-6 h-6 shrink-0">
|
||||
<FolderClosed className="size-4" />
|
||||
</div>
|
||||
<RelaySetName relaySet={relaySet} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-1">
|
||||
<RelayUrlsExpandToggle relaySetId={relaySet.id}>
|
||||
{t('n relays', { n: relaySet.relayUrls.length })}
|
||||
</RelayUrlsExpandToggle>
|
||||
<RelaySetOptions relaySet={relaySet} />
|
||||
</div>
|
||||
<RelaySetName relaySet={relaySet} />
|
||||
</div>
|
||||
<div className="flex gap-1">
|
||||
<RelayUrlsExpandToggle relaySetId={relaySet.id}>
|
||||
{t('n relays', { n: relaySet.relayUrls.length })}
|
||||
</RelayUrlsExpandToggle>
|
||||
<RelaySetOptions relaySet={relaySet} />
|
||||
</div>
|
||||
{expandedRelaySetId === relaySet.id && <RelayUrls relaySetId={relaySet.id} />}
|
||||
</div>
|
||||
{expandedRelaySetId === relaySet.id && <RelayUrls relaySetId={relaySet.id} />}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
72
src/components/FavoriteRelaysSetting/RelaySetList.tsx
Normal file
72
src/components/FavoriteRelaysSetting/RelaySetList.tsx
Normal file
@@ -0,0 +1,72 @@
|
||||
import { useFavoriteRelays } from '@/providers/FavoriteRelaysProvider'
|
||||
import {
|
||||
closestCenter,
|
||||
DndContext,
|
||||
DragEndEvent,
|
||||
KeyboardSensor,
|
||||
PointerSensor,
|
||||
useSensor,
|
||||
useSensors
|
||||
} from '@dnd-kit/core'
|
||||
import { restrictToParentElement, restrictToVerticalAxis } from '@dnd-kit/modifiers'
|
||||
import {
|
||||
arrayMove,
|
||||
SortableContext,
|
||||
sortableKeyboardCoordinates,
|
||||
verticalListSortingStrategy
|
||||
} from '@dnd-kit/sortable'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import PullRelaySetsButton from './PullRelaySetsButton'
|
||||
import RelaySet from './RelaySet'
|
||||
|
||||
export default function RelaySetList() {
|
||||
const { t } = useTranslation()
|
||||
const { relaySets, reorderRelaySets } = useFavoriteRelays()
|
||||
|
||||
const sensors = useSensors(
|
||||
useSensor(PointerSensor),
|
||||
useSensor(KeyboardSensor, {
|
||||
coordinateGetter: sortableKeyboardCoordinates
|
||||
})
|
||||
)
|
||||
|
||||
const handleDragEnd = (event: DragEndEvent) => {
|
||||
const { active, over } = event
|
||||
|
||||
if (over && active.id !== over.id) {
|
||||
const oldIndex = relaySets.findIndex((item) => item.id === active.id)
|
||||
const newIndex = relaySets.findIndex((item) => item.id === over.id)
|
||||
|
||||
const reorderedSets = arrayMove(relaySets, oldIndex, newIndex)
|
||||
reorderRelaySets(reorderedSets)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
<div className="flex flex-wrap items-center justify-between gap-2">
|
||||
<div className="text-muted-foreground font-semibold select-none shrink-0">
|
||||
{t('Relay sets')}
|
||||
</div>
|
||||
<PullRelaySetsButton />
|
||||
</div>
|
||||
<DndContext
|
||||
sensors={sensors}
|
||||
collisionDetection={closestCenter}
|
||||
onDragEnd={handleDragEnd}
|
||||
modifiers={[restrictToVerticalAxis, restrictToParentElement]}
|
||||
>
|
||||
<SortableContext
|
||||
items={relaySets.map((set) => set.id)}
|
||||
strategy={verticalListSortingStrategy}
|
||||
>
|
||||
<div className="grid gap-2">
|
||||
{relaySets.map((relaySet) => (
|
||||
<RelaySet key={relaySet.id} relaySet={relaySet} />
|
||||
))}
|
||||
</div>
|
||||
</SortableContext>
|
||||
</DndContext>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,39 +1,18 @@
|
||||
import { useFavoriteRelays } from '@/providers/FavoriteRelaysProvider'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import AddNewRelay from './AddNewRelay'
|
||||
import AddNewRelaySet from './AddNewRelaySet'
|
||||
import FavoriteRelayList from './FavoriteRelayList'
|
||||
import { RelaySetsSettingComponentProvider } from './provider'
|
||||
import RelayItem from './RelayItem'
|
||||
import RelaySet from './RelaySet'
|
||||
import RelaySetList from './RelaySetList'
|
||||
import TemporaryRelaySet from './TemporaryRelaySet'
|
||||
import PullRelaySetsButton from './PullRelaySetsButton'
|
||||
|
||||
export default function FavoriteRelaysSetting() {
|
||||
const { t } = useTranslation()
|
||||
const { relaySets, favoriteRelays } = useFavoriteRelays()
|
||||
|
||||
return (
|
||||
<RelaySetsSettingComponentProvider>
|
||||
<div className="space-y-4">
|
||||
<TemporaryRelaySet />
|
||||
<div className="space-y-2">
|
||||
<div className="flex flex-wrap items-center justify-between gap-2">
|
||||
<div className="text-muted-foreground font-semibold select-none shrink-0">
|
||||
{t('Relay sets')}
|
||||
</div>
|
||||
<PullRelaySetsButton />
|
||||
</div>
|
||||
{relaySets.map((relaySet) => (
|
||||
<RelaySet key={relaySet.id} relaySet={relaySet} />
|
||||
))}
|
||||
</div>
|
||||
<RelaySetList />
|
||||
<AddNewRelaySet />
|
||||
<div className="space-y-2">
|
||||
<div className="text-muted-foreground font-semibold select-none">{t('Relays')}</div>
|
||||
{favoriteRelays.map((relay) => (
|
||||
<RelayItem key={relay} relay={relay} />
|
||||
))}
|
||||
</div>
|
||||
<FavoriteRelayList />
|
||||
<AddNewRelay />
|
||||
</div>
|
||||
</RelaySetsSettingComponentProvider>
|
||||
|
||||
Reference in New Issue
Block a user