179 lines
5.5 KiB
TypeScript
179 lines
5.5 KiB
TypeScript
import { SEARCHABLE_RELAY_URLS } from '@/constants'
|
|
import { checkAlgoRelay, checkSearchRelay } from '@/lib/relay'
|
|
import { isWebsocketUrl, normalizeUrl } from '@/lib/url'
|
|
import client from '@/services/client.service'
|
|
import storage from '@/services/storage.service'
|
|
import { TRelayGroup } from '@/types'
|
|
import { createContext, Dispatch, useContext, useEffect, useState } from 'react'
|
|
import { useFeed } from './FeedProvider'
|
|
|
|
type TRelaySettingsContext = {
|
|
relayGroups: TRelayGroup[]
|
|
temporaryRelayUrls: string[]
|
|
relayUrls: string[]
|
|
searchableRelayUrls: string[]
|
|
areAlgoRelays: boolean
|
|
switchRelayGroup: (groupName: string) => void
|
|
renameRelayGroup: (oldGroupName: string, newGroupName: string) => string | null
|
|
deleteRelayGroup: (groupName: string) => void
|
|
addRelayGroup: (groupName: string, relayUrls?: string[]) => string | null
|
|
updateRelayGroupRelayUrls: (groupName: string, relayUrls: string[]) => void
|
|
setTemporaryRelayUrls: Dispatch<string[]>
|
|
}
|
|
|
|
const RelaySettingsContext = createContext<TRelaySettingsContext | undefined>(undefined)
|
|
|
|
export const useRelaySettings = () => {
|
|
const context = useContext(RelaySettingsContext)
|
|
if (!context) {
|
|
throw new Error('useRelaySettings must be used within a RelaySettingsProvider')
|
|
}
|
|
return context
|
|
}
|
|
|
|
export function RelaySettingsProvider({ children }: { children: React.ReactNode }) {
|
|
const { setFeedType } = useFeed()
|
|
const [relayGroups, setRelayGroups] = useState<TRelayGroup[]>([])
|
|
const [temporaryRelayUrls, setTemporaryRelayUrls] = useState<string[]>([])
|
|
const [relayUrls, setRelayUrls] = useState<string[]>(
|
|
temporaryRelayUrls.length
|
|
? temporaryRelayUrls
|
|
: (relayGroups.find((group) => group.isActive)?.relayUrls ?? [])
|
|
)
|
|
const [searchableRelayUrls, setSearchableRelayUrls] = useState<string[]>(SEARCHABLE_RELAY_URLS)
|
|
const [areAlgoRelays, setAreAlgoRelays] = useState(false)
|
|
|
|
useEffect(() => {
|
|
const searchParams = new URLSearchParams(window.location.search)
|
|
const tempRelays = searchParams
|
|
.getAll('r')
|
|
.map((url) => (url.startsWith('wss://') || url.startsWith('ws://') ? url : `wss://${url}`))
|
|
.filter((url) => isWebsocketUrl(url))
|
|
.map((url) => normalizeUrl(url))
|
|
if (tempRelays.length) {
|
|
setTemporaryRelayUrls(tempRelays)
|
|
setFeedType('relays')
|
|
}
|
|
const storedGroups = storage.getRelayGroups()
|
|
setRelayGroups(storedGroups)
|
|
}, [])
|
|
|
|
useEffect(() => {
|
|
const handler = async () => {
|
|
const newRelayUrls = temporaryRelayUrls.length
|
|
? temporaryRelayUrls
|
|
: (relayGroups.find((group) => group.isActive)?.relayUrls ?? [])
|
|
|
|
if (JSON.stringify(relayUrls) !== JSON.stringify(newRelayUrls)) {
|
|
setRelayUrls(newRelayUrls)
|
|
}
|
|
const relayInfos = await client.fetchRelayInfos(newRelayUrls)
|
|
const searchableRelayUrls = newRelayUrls.filter((_, index) =>
|
|
checkSearchRelay(relayInfos[index])
|
|
)
|
|
setSearchableRelayUrls(
|
|
searchableRelayUrls.length ? searchableRelayUrls : SEARCHABLE_RELAY_URLS
|
|
)
|
|
const nonAlgoRelayUrls = newRelayUrls.filter((_, index) => !checkAlgoRelay(relayInfos[index]))
|
|
setAreAlgoRelays(newRelayUrls.length > 0 && nonAlgoRelayUrls.length === 0)
|
|
client.setCurrentRelayUrls(nonAlgoRelayUrls)
|
|
}
|
|
handler()
|
|
}, [relayGroups, temporaryRelayUrls, relayUrls])
|
|
|
|
const updateGroups = (fn: (pre: TRelayGroup[]) => TRelayGroup[]) => {
|
|
let newGroups = relayGroups
|
|
setRelayGroups((pre) => {
|
|
newGroups = fn(pre)
|
|
return newGroups
|
|
})
|
|
storage.setRelayGroups(newGroups)
|
|
}
|
|
|
|
const switchRelayGroup = (groupName: string) => {
|
|
updateGroups((pre) =>
|
|
pre.map((group) => ({
|
|
...group,
|
|
isActive: group.groupName === groupName
|
|
}))
|
|
)
|
|
setFeedType('relays')
|
|
setTemporaryRelayUrls([])
|
|
}
|
|
|
|
const deleteRelayGroup = (groupName: string) => {
|
|
updateGroups((pre) => pre.filter((group) => group.groupName !== groupName))
|
|
}
|
|
|
|
const updateRelayGroupRelayUrls = (groupName: string, relayUrls: string[]) => {
|
|
updateGroups((pre) =>
|
|
pre.map((group) => ({
|
|
...group,
|
|
relayUrls: group.groupName === groupName ? relayUrls : group.relayUrls
|
|
}))
|
|
)
|
|
}
|
|
|
|
const renameRelayGroup = (oldGroupName: string, newGroupName: string) => {
|
|
if (newGroupName === '') {
|
|
return null
|
|
}
|
|
if (oldGroupName === newGroupName) {
|
|
return null
|
|
}
|
|
updateGroups((pre) => {
|
|
if (pre.some((group) => group.groupName === newGroupName)) {
|
|
return pre
|
|
}
|
|
return pre.map((group) => ({
|
|
...group,
|
|
groupName: group.groupName === oldGroupName ? newGroupName : group.groupName
|
|
}))
|
|
})
|
|
return null
|
|
}
|
|
|
|
const addRelayGroup = (groupName: string, relayUrls: string[] = []) => {
|
|
if (groupName === '') {
|
|
return null
|
|
}
|
|
const normalizedUrls = relayUrls
|
|
.filter((url) => isWebsocketUrl(url))
|
|
.map((url) => normalizeUrl(url))
|
|
updateGroups((pre) => {
|
|
if (pre.some((group) => group.groupName === groupName)) {
|
|
return pre
|
|
}
|
|
return [
|
|
...pre,
|
|
{
|
|
groupName,
|
|
relayUrls: normalizedUrls,
|
|
isActive: false
|
|
}
|
|
]
|
|
})
|
|
return null
|
|
}
|
|
|
|
return (
|
|
<RelaySettingsContext.Provider
|
|
value={{
|
|
relayGroups,
|
|
temporaryRelayUrls,
|
|
relayUrls,
|
|
searchableRelayUrls,
|
|
areAlgoRelays,
|
|
switchRelayGroup,
|
|
renameRelayGroup,
|
|
deleteRelayGroup,
|
|
addRelayGroup,
|
|
updateRelayGroupRelayUrls,
|
|
setTemporaryRelayUrls
|
|
}}
|
|
>
|
|
{children}
|
|
</RelaySettingsContext.Provider>
|
|
)
|
|
}
|