feat: share relays
This commit is contained in:
@@ -15,7 +15,9 @@ import { TRelayGroup } from './types'
|
|||||||
|
|
||||||
export default function RelayGroup({ group }: { group: TRelayGroup }) {
|
export default function RelayGroup({ group }: { group: TRelayGroup }) {
|
||||||
const { expandedRelayGroup } = useRelaySettingsComponent()
|
const { expandedRelayGroup } = useRelaySettingsComponent()
|
||||||
const { groupName, isActive, relayUrls } = group
|
const { temporaryRelayUrls } = useRelaySettings()
|
||||||
|
const { groupName, relayUrls } = group
|
||||||
|
const isActive = temporaryRelayUrls.length === 0 && group.isActive
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -23,14 +25,18 @@ export default function RelayGroup({ group }: { group: TRelayGroup }) {
|
|||||||
>
|
>
|
||||||
<div className="flex justify-between items-center">
|
<div className="flex justify-between items-center">
|
||||||
<div className="flex space-x-2 items-center">
|
<div className="flex space-x-2 items-center">
|
||||||
<RelayGroupActiveToggle groupName={groupName} />
|
<RelayGroupActiveToggle
|
||||||
|
groupName={groupName}
|
||||||
|
isActive={isActive}
|
||||||
|
canActive={relayUrls.length > 0}
|
||||||
|
/>
|
||||||
<RelayGroupName groupName={groupName} />
|
<RelayGroupName groupName={groupName} />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-1">
|
<div className="flex gap-1">
|
||||||
<RelayUrlsExpandToggle groupName={groupName}>
|
<RelayUrlsExpandToggle groupName={groupName}>
|
||||||
{relayUrls.length} relays
|
{relayUrls.length} relays
|
||||||
</RelayUrlsExpandToggle>
|
</RelayUrlsExpandToggle>
|
||||||
<RelayGroupOptions groupName={groupName} />
|
<RelayGroupOptions group={group} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{expandedRelayGroup === groupName && <RelayUrls groupName={groupName} />}
|
{expandedRelayGroup === groupName && <RelayUrls groupName={groupName} />}
|
||||||
@@ -38,20 +44,25 @@ export default function RelayGroup({ group }: { group: TRelayGroup }) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function RelayGroupActiveToggle({ groupName }: { groupName: string }) {
|
function RelayGroupActiveToggle({
|
||||||
const { relayGroups, switchRelayGroup } = useRelaySettings()
|
groupName,
|
||||||
|
isActive,
|
||||||
const isActive = relayGroups.find((group) => group.groupName === groupName)?.isActive
|
canActive
|
||||||
const hasRelayUrls = relayGroups.find((group) => group.groupName === groupName)?.relayUrls.length
|
}: {
|
||||||
|
groupName: string
|
||||||
|
isActive: boolean
|
||||||
|
canActive: boolean
|
||||||
|
}) {
|
||||||
|
const { switchRelayGroup } = useRelaySettings()
|
||||||
|
|
||||||
return isActive ? (
|
return isActive ? (
|
||||||
<CircleCheck size={18} className="text-highlight shrink-0" />
|
<CircleCheck size={18} className="text-highlight shrink-0" />
|
||||||
) : (
|
) : (
|
||||||
<Circle
|
<Circle
|
||||||
size={18}
|
size={18}
|
||||||
className={`text-muted-foreground shrink-0 ${hasRelayUrls ? 'cursor-pointer hover:text-foreground ' : ''}`}
|
className={`text-muted-foreground shrink-0 ${canActive ? 'cursor-pointer hover:text-foreground ' : ''}`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (hasRelayUrls) {
|
if (canActive) {
|
||||||
switchRelayGroup(groupName)
|
switchRelayGroup(groupName)
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
@@ -68,6 +79,9 @@ function RelayGroupName({ groupName }: { groupName: string }) {
|
|||||||
const hasRelayUrls = relayGroups.find((group) => group.groupName === groupName)?.relayUrls.length
|
const hasRelayUrls = relayGroups.find((group) => group.groupName === groupName)?.relayUrls.length
|
||||||
|
|
||||||
const saveNewGroupName = () => {
|
const saveNewGroupName = () => {
|
||||||
|
if (relayGroups.find((group) => group.groupName === newGroupName)) {
|
||||||
|
return setNewNameError('already exists')
|
||||||
|
}
|
||||||
const errMsg = renameRelayGroup(groupName, newGroupName)
|
const errMsg = renameRelayGroup(groupName, newGroupName)
|
||||||
if (errMsg) {
|
if (errMsg) {
|
||||||
setNewNameError(errMsg)
|
setNewNameError(errMsg)
|
||||||
@@ -138,10 +152,9 @@ function RelayUrlsExpandToggle({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function RelayGroupOptions({ groupName }: { groupName: string }) {
|
function RelayGroupOptions({ group }: { group: TRelayGroup }) {
|
||||||
const { relayGroups, deleteRelayGroup } = useRelaySettings()
|
const { deleteRelayGroup } = useRelaySettings()
|
||||||
const { setRenamingGroup } = useRelaySettingsComponent()
|
const { setRenamingGroup } = useRelaySettingsComponent()
|
||||||
const isActive = relayGroups.find((group) => group.groupName === groupName)?.isActive
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
@@ -151,11 +164,21 @@ function RelayGroupOptions({ groupName }: { groupName: string }) {
|
|||||||
</Button>
|
</Button>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent>
|
<DropdownMenuContent>
|
||||||
<DropdownMenuItem onClick={() => setRenamingGroup(groupName)}>Rename</DropdownMenuItem>
|
<DropdownMenuItem onClick={() => setRenamingGroup(group.groupName)}>
|
||||||
|
Rename
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem
|
||||||
|
onClick={() => {
|
||||||
|
navigator.clipboard.writeText(
|
||||||
|
`https://jumble.social/?${group.relayUrls.map((url) => 'r=' + url).join('&')}`
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Copy share link
|
||||||
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
className="text-destructive focus:text-destructive"
|
className="text-destructive focus:text-destructive"
|
||||||
disabled={isActive}
|
onClick={() => deleteRelayGroup(group.groupName)}
|
||||||
onClick={() => deleteRelayGroup(groupName)}
|
|
||||||
>
|
>
|
||||||
Delete
|
Delete
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
import { useRelaySettings } from '@renderer/providers/RelaySettingsProvider'
|
||||||
|
import client from '@renderer/services/client.service'
|
||||||
|
import { Save } from 'lucide-react'
|
||||||
|
import { useEffect, useState } from 'react'
|
||||||
|
import { Button } from '../ui/button'
|
||||||
|
|
||||||
|
export default function TemporaryRelayGroup() {
|
||||||
|
const { temporaryRelayUrls, relayGroups, addRelayGroup, switchRelayGroup } = useRelaySettings()
|
||||||
|
const [relays, setRelays] = useState<
|
||||||
|
{
|
||||||
|
url: string
|
||||||
|
isConnected: boolean
|
||||||
|
}[]
|
||||||
|
>(temporaryRelayUrls.map((url) => ({ url, isConnected: false })))
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
const connectionStatusMap = client.listConnectionStatus()
|
||||||
|
setRelays((pre) => {
|
||||||
|
return pre.map((relay) => {
|
||||||
|
const isConnected = connectionStatusMap.get(relay.url) || false
|
||||||
|
return { ...relay, isConnected }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}, 1000)
|
||||||
|
|
||||||
|
return () => clearInterval(interval)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setRelays(temporaryRelayUrls.map((url) => ({ url, isConnected: false })))
|
||||||
|
}, [temporaryRelayUrls])
|
||||||
|
|
||||||
|
if (!relays.length) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSave = () => {
|
||||||
|
const existingTemporaryIndexes = relayGroups
|
||||||
|
.filter((group) => /^Temporary \d+$/.test(group.groupName))
|
||||||
|
.map((group) => group.groupName.split(' ')[1])
|
||||||
|
.map(Number)
|
||||||
|
.filter((index) => !isNaN(index))
|
||||||
|
const nextIndex = Math.max(...existingTemporaryIndexes, 0) + 1
|
||||||
|
const groupName = `Temporary ${nextIndex}`
|
||||||
|
addRelayGroup(groupName, temporaryRelayUrls)
|
||||||
|
switchRelayGroup(groupName)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`w-full border border-dashed rounded-lg p-4 border-highlight bg-highlight/5`}>
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<div className="h-8 font-semibold">Temporary</div>
|
||||||
|
<Button title="save" size="icon" variant="ghost" onClick={handleSave}>
|
||||||
|
<Save />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
{relays.map((relay, index) => (
|
||||||
|
<div key={index} className="flex items-center justify-between">
|
||||||
|
<div className="flex gap-2 items-center">
|
||||||
|
{relay.isConnected ? (
|
||||||
|
<div className="text-green-500 text-xs">●</div>
|
||||||
|
) : (
|
||||||
|
<div className="text-red-500 text-xs">●</div>
|
||||||
|
)}
|
||||||
|
<div className="text-muted-foreground text-sm">{relay.url}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ import { useRelaySettings } from '@renderer/providers/RelaySettingsProvider'
|
|||||||
import { useEffect, useRef, useState } from 'react'
|
import { useEffect, useRef, useState } from 'react'
|
||||||
import { RelaySettingsComponentProvider } from './provider'
|
import { RelaySettingsComponentProvider } from './provider'
|
||||||
import RelayGroup from './RelayGroup'
|
import RelayGroup from './RelayGroup'
|
||||||
|
import TemporaryRelayGroup from './TemporaryRelayGroup'
|
||||||
|
|
||||||
export default function RelaySettings() {
|
export default function RelaySettings() {
|
||||||
const { relayGroups, addRelayGroup } = useRelaySettings()
|
const { relayGroups, addRelayGroup } = useRelaySettings()
|
||||||
@@ -19,6 +20,9 @@ export default function RelaySettings() {
|
|||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const saveRelayGroup = () => {
|
const saveRelayGroup = () => {
|
||||||
|
if (relayGroups.find((group) => group.groupName === newGroupName)) {
|
||||||
|
return setNewNameError('already exists')
|
||||||
|
}
|
||||||
const errMsg = addRelayGroup(newGroupName)
|
const errMsg = addRelayGroup(newGroupName)
|
||||||
if (errMsg) {
|
if (errMsg) {
|
||||||
return setNewNameError(errMsg)
|
return setNewNameError(errMsg)
|
||||||
@@ -43,6 +47,7 @@ export default function RelaySettings() {
|
|||||||
<div ref={dummyRef} tabIndex={-1} style={{ position: 'absolute', opacity: 0 }}></div>
|
<div ref={dummyRef} tabIndex={-1} style={{ position: 'absolute', opacity: 0 }}></div>
|
||||||
<div className="text-lg font-semibold mb-4">Relay Settings</div>
|
<div className="text-lg font-semibold mb-4">Relay Settings</div>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
|
<TemporaryRelayGroup />
|
||||||
{relayGroups.map((group, index) => (
|
{relayGroups.map((group, index) => (
|
||||||
<RelayGroup key={index} group={group} />
|
<RelayGroup key={index} group={group} />
|
||||||
))}
|
))}
|
||||||
@@ -63,7 +68,7 @@ export default function RelaySettings() {
|
|||||||
onKeyDown={handleNewGroupNameKeyDown}
|
onKeyDown={handleNewGroupNameKeyDown}
|
||||||
onBlur={saveRelayGroup}
|
onBlur={saveRelayGroup}
|
||||||
/>
|
/>
|
||||||
<Button>Add</Button>
|
<Button onClick={saveRelayGroup}>Add</Button>
|
||||||
</div>
|
</div>
|
||||||
{newNameError && <div className="text-xs text-destructive mt-1">{newNameError}</div>}
|
{newNameError && <div className="text-xs text-destructive mt-1">{newNameError}</div>}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -10,7 +10,10 @@ import { forwardRef, useImperativeHandle, useRef } from 'react'
|
|||||||
|
|
||||||
const PrimaryPageLayout = forwardRef(
|
const PrimaryPageLayout = forwardRef(
|
||||||
(
|
(
|
||||||
{ children, titlebarContent }: { children: React.ReactNode; titlebarContent?: React.ReactNode },
|
{
|
||||||
|
children,
|
||||||
|
titlebarContent
|
||||||
|
}: { children?: React.ReactNode; titlebarContent?: React.ReactNode },
|
||||||
ref
|
ref
|
||||||
) => {
|
) => {
|
||||||
const scrollAreaRef = useRef<HTMLDivElement>(null)
|
const scrollAreaRef = useRef<HTMLDivElement>(null)
|
||||||
|
|||||||
@@ -1,10 +1,36 @@
|
|||||||
import NoteList from '@renderer/components/NoteList'
|
import NoteList from '@renderer/components/NoteList'
|
||||||
|
import RelaySettings from '@renderer/components/RelaySettings'
|
||||||
|
import { Button } from '@renderer/components/ui/button'
|
||||||
|
import { Popover, PopoverContent, PopoverTrigger } from '@renderer/components/ui/popover'
|
||||||
|
import { ScrollArea } from '@renderer/components/ui/scroll-area'
|
||||||
import PrimaryPageLayout from '@renderer/layouts/PrimaryPageLayout'
|
import PrimaryPageLayout from '@renderer/layouts/PrimaryPageLayout'
|
||||||
import { useRelaySettings } from '@renderer/providers/RelaySettingsProvider'
|
import { useRelaySettings } from '@renderer/providers/RelaySettingsProvider'
|
||||||
|
|
||||||
export default function NoteListPage() {
|
export default function NoteListPage() {
|
||||||
const { relayUrls } = useRelaySettings()
|
const { relayUrls } = useRelaySettings()
|
||||||
if (!relayUrls.length) return null
|
|
||||||
|
if (!relayUrls.length) {
|
||||||
|
return (
|
||||||
|
<PrimaryPageLayout>
|
||||||
|
<div className="w-full text-center">
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger asChild>
|
||||||
|
<Button title="relay settings" size="lg">
|
||||||
|
Choose a relay group
|
||||||
|
</Button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent className="w-96 h-[450px] p-0">
|
||||||
|
<ScrollArea className="h-full">
|
||||||
|
<div className="p-4">
|
||||||
|
<RelaySettings />
|
||||||
|
</div>
|
||||||
|
</ScrollArea>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
|
</PrimaryPageLayout>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PrimaryPageLayout>
|
<PrimaryPageLayout>
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
import { TRelayGroup } from '@common/types'
|
import { TRelayGroup } from '@common/types'
|
||||||
|
import { isWebsocketUrl, normalizeUrl } from '@renderer/lib/url'
|
||||||
import storage from '@renderer/services/storage.service'
|
import storage from '@renderer/services/storage.service'
|
||||||
import { createContext, useContext, useEffect, useState } from 'react'
|
import { createContext, useContext, useEffect, useState } from 'react'
|
||||||
|
|
||||||
type TRelaySettingsContext = {
|
type TRelaySettingsContext = {
|
||||||
relayGroups: TRelayGroup[]
|
relayGroups: TRelayGroup[]
|
||||||
|
temporaryRelayUrls: string[]
|
||||||
relayUrls: string[]
|
relayUrls: string[]
|
||||||
switchRelayGroup: (groupName: string) => void
|
switchRelayGroup: (groupName: string) => void
|
||||||
renameRelayGroup: (oldGroupName: string, newGroupName: string) => string | null
|
renameRelayGroup: (oldGroupName: string, newGroupName: string) => string | null
|
||||||
deleteRelayGroup: (groupName: string) => void
|
deleteRelayGroup: (groupName: string) => void
|
||||||
addRelayGroup: (groupName: string) => string | null
|
addRelayGroup: (groupName: string, relayUrls?: string[]) => string | null
|
||||||
updateRelayGroupRelayUrls: (groupName: string, relayUrls: string[]) => void
|
updateRelayGroupRelayUrls: (groupName: string, relayUrls: string[]) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,12 +26,31 @@ export const useRelaySettings = () => {
|
|||||||
|
|
||||||
export function RelaySettingsProvider({ children }: { children: React.ReactNode }) {
|
export function RelaySettingsProvider({ children }: { children: React.ReactNode }) {
|
||||||
const [relayGroups, setRelayGroups] = useState<TRelayGroup[]>([])
|
const [relayGroups, setRelayGroups] = useState<TRelayGroup[]>([])
|
||||||
|
const [temporaryRelayUrls, setTemporaryRelayUrls] = useState<string[]>([])
|
||||||
const [relayUrls, setRelayUrls] = useState<string[]>(
|
const [relayUrls, setRelayUrls] = useState<string[]>(
|
||||||
relayGroups.find((group) => group.isActive)?.relayUrls ?? []
|
temporaryRelayUrls.length
|
||||||
|
? temporaryRelayUrls
|
||||||
|
: (relayGroups.find((group) => group.isActive)?.relayUrls ?? [])
|
||||||
)
|
)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const init = async () => {
|
const init = async () => {
|
||||||
|
const searchParams = new URLSearchParams(window.location.search)
|
||||||
|
const tempRelays = searchParams
|
||||||
|
.getAll('r')
|
||||||
|
.filter((url) => isWebsocketUrl(url))
|
||||||
|
.map((url) => normalizeUrl(url))
|
||||||
|
if (tempRelays.length) {
|
||||||
|
setTemporaryRelayUrls(tempRelays)
|
||||||
|
// remove relay urls from query string
|
||||||
|
searchParams.delete('r')
|
||||||
|
const newSearch = searchParams.toString()
|
||||||
|
window.history.replaceState(
|
||||||
|
{},
|
||||||
|
'',
|
||||||
|
`${window.location.pathname}${newSearch.length ? `?${newSearch}` : ''}`
|
||||||
|
)
|
||||||
|
}
|
||||||
const storedGroups = await storage.getRelayGroups()
|
const storedGroups = await storage.getRelayGroups()
|
||||||
setRelayGroups(storedGroups)
|
setRelayGroups(storedGroups)
|
||||||
}
|
}
|
||||||
@@ -38,30 +59,39 @@ export function RelaySettingsProvider({ children }: { children: React.ReactNode
|
|||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setRelayUrls(relayGroups.find((group) => group.isActive)?.relayUrls ?? [])
|
setRelayUrls(
|
||||||
}, [relayGroups])
|
temporaryRelayUrls.length
|
||||||
|
? temporaryRelayUrls
|
||||||
|
: (relayGroups.find((group) => group.isActive)?.relayUrls ?? [])
|
||||||
|
)
|
||||||
|
}, [relayGroups, temporaryRelayUrls])
|
||||||
|
|
||||||
const updateGroups = async (newGroups: TRelayGroup[]) => {
|
const updateGroups = async (fn: (pre: TRelayGroup[]) => TRelayGroup[]) => {
|
||||||
setRelayGroups(newGroups)
|
let newGroups = relayGroups
|
||||||
|
setRelayGroups((pre) => {
|
||||||
|
newGroups = fn(pre)
|
||||||
|
return newGroups
|
||||||
|
})
|
||||||
await storage.setRelayGroups(newGroups)
|
await storage.setRelayGroups(newGroups)
|
||||||
}
|
}
|
||||||
|
|
||||||
const switchRelayGroup = (groupName: string) => {
|
const switchRelayGroup = (groupName: string) => {
|
||||||
updateGroups(
|
updateGroups((pre) =>
|
||||||
relayGroups.map((group) => ({
|
pre.map((group) => ({
|
||||||
...group,
|
...group,
|
||||||
isActive: group.groupName === groupName
|
isActive: group.groupName === groupName
|
||||||
}))
|
}))
|
||||||
)
|
)
|
||||||
|
setTemporaryRelayUrls([])
|
||||||
}
|
}
|
||||||
|
|
||||||
const deleteRelayGroup = (groupName: string) => {
|
const deleteRelayGroup = (groupName: string) => {
|
||||||
updateGroups(relayGroups.filter((group) => group.groupName !== groupName || group.isActive))
|
updateGroups((pre) => pre.filter((group) => group.groupName !== groupName))
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateRelayGroupRelayUrls = (groupName: string, relayUrls: string[]) => {
|
const updateRelayGroupRelayUrls = (groupName: string, relayUrls: string[]) => {
|
||||||
updateGroups(
|
updateGroups((pre) =>
|
||||||
relayGroups.map((group) => ({
|
pre.map((group) => ({
|
||||||
...group,
|
...group,
|
||||||
relayUrls: group.groupName === groupName ? relayUrls : group.relayUrls
|
relayUrls: group.groupName === groupName ? relayUrls : group.relayUrls
|
||||||
}))
|
}))
|
||||||
@@ -75,33 +105,38 @@ export function RelaySettingsProvider({ children }: { children: React.ReactNode
|
|||||||
if (oldGroupName === newGroupName) {
|
if (oldGroupName === newGroupName) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
if (relayGroups.some((group) => group.groupName === newGroupName)) {
|
updateGroups((pre) => {
|
||||||
return 'already exists'
|
if (pre.some((group) => group.groupName === newGroupName)) {
|
||||||
|
return pre
|
||||||
}
|
}
|
||||||
updateGroups(
|
return pre.map((group) => ({
|
||||||
relayGroups.map((group) => ({
|
|
||||||
...group,
|
...group,
|
||||||
groupName: group.groupName === oldGroupName ? newGroupName : group.groupName
|
groupName: group.groupName === oldGroupName ? newGroupName : group.groupName
|
||||||
}))
|
}))
|
||||||
)
|
})
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
const addRelayGroup = (groupName: string) => {
|
const addRelayGroup = (groupName: string, relayUrls: string[] = []) => {
|
||||||
if (groupName === '') {
|
if (groupName === '') {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
if (relayGroups.some((group) => group.groupName === groupName)) {
|
const normalizedUrls = relayUrls
|
||||||
return 'already exists'
|
.filter((url) => isWebsocketUrl(url))
|
||||||
|
.map((url) => normalizeUrl(url))
|
||||||
|
updateGroups((pre) => {
|
||||||
|
if (pre.some((group) => group.groupName === groupName)) {
|
||||||
|
return pre
|
||||||
}
|
}
|
||||||
updateGroups([
|
return [
|
||||||
...relayGroups,
|
...pre,
|
||||||
{
|
{
|
||||||
groupName,
|
groupName,
|
||||||
relayUrls: [],
|
relayUrls: normalizedUrls,
|
||||||
isActive: false
|
isActive: false
|
||||||
}
|
}
|
||||||
])
|
]
|
||||||
|
})
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,6 +144,7 @@ export function RelaySettingsProvider({ children }: { children: React.ReactNode
|
|||||||
<RelaySettingsContext.Provider
|
<RelaySettingsContext.Provider
|
||||||
value={{
|
value={{
|
||||||
relayGroups,
|
relayGroups,
|
||||||
|
temporaryRelayUrls,
|
||||||
relayUrls,
|
relayUrls,
|
||||||
switchRelayGroup,
|
switchRelayGroup,
|
||||||
renameRelayGroup,
|
renameRelayGroup,
|
||||||
|
|||||||
Reference in New Issue
Block a user