wip: relay manegament screen

This commit is contained in:
Ren Amamiya
2023-09-30 19:07:17 +07:00
parent a2e3247432
commit 11ed618a7f
10 changed files with 248 additions and 38 deletions

View File

@@ -0,0 +1,29 @@
import { NDKEvent, NDKKind } from '@nostr-dev-kit/ndk';
import { useQuery } from '@tanstack/react-query';
import { useNDK } from '@libs/ndk/provider';
export function RelayEventList({ relayUrl }: { relayUrl: string }) {
const { fetcher } = useNDK();
const { status, data } = useQuery(
['relay-event'],
async () => {
const events = await fetcher.fetchLatestEvents(
[relayUrl],
{
kinds: [NDKKind.Text, NDKKind.Repost, 1063, NDKKind.Article],
},
100
);
return events as unknown as NDKEvent[];
},
{ refetchOnWindowFocus: false }
);
return (
<div>
<p>TODO</p>
</div>
);
}

View File

@@ -0,0 +1,66 @@
import { useQueryClient } from '@tanstack/react-query';
import { useState } from 'react';
import { useStorage } from '@libs/storage/provider';
import { PlusIcon } from '@shared/icons';
const domainRegex = /^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$/;
export function RelayForm() {
const { db } = useStorage();
const queryClient = useQueryClient();
const [url, setUrl] = useState('');
const [error, setError] = useState('');
const createRelay = async () => {
if (url.length < 1) return setError('Please enter relay url');
try {
const relay = new URL(url.replace(/\s/g, ''));
if (
domainRegex.test(relay.host) &&
(relay.protocol === 'wss:' || relay.protocol === 'ws:')
) {
const res = await db.createRelay(url);
if (!res) return setError("You're already using this relay");
queryClient.invalidateQueries(['user-relay']);
setError('');
setUrl('');
} else {
return setError(
'URL is invalid, a relay must use websocket protocol (start with wss:// or ws://). Please check again'
);
}
} catch {
return setError('Relay URL is not valid. Please check again');
}
};
return (
<div className="flex flex-col gap-1">
<div className="flex h-10 items-center justify-between rounded-lg bg-white/10 pr-1.5">
<input
className="h-full w-full bg-transparent pl-3 pr-1.5 placeholder:text-white/70 focus:outline-none"
type="url"
placeholder="wss://"
spellCheck={false}
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
value={url}
onChange={(e) => setUrl(e.target.value)}
/>
<button
type="button"
onClick={() => createRelay()}
className="inline-flex h-6 w-6 items-center justify-center rounded bg-fuchsia-500 text-white hover:bg-fuchsia-600"
>
<PlusIcon className="h-4 w-4" />
</button>
</div>
<span className="text-sm text-red-400">{error}</span>
</div>
);
}

View File

@@ -1,7 +1,11 @@
import { useQuery } from '@tanstack/react-query';
import { useQuery, useQueryClient } from '@tanstack/react-query';
import { message } from '@tauri-apps/api/dialog';
import { normalizeRelayUrl } from 'nostr-fetch';
import { useNavigate } from 'react-router-dom';
import { VList } from 'virtua';
import { useStorage } from '@libs/storage/provider';
import { LoaderIcon, PlusIcon, ShareIcon } from '@shared/icons';
import { User } from '@shared/user';
@@ -9,21 +13,31 @@ import { useNostr } from '@utils/hooks/useNostr';
export function RelayList() {
const navigate = useNavigate();
const queryClient = useQueryClient();
const { getAllRelaysByUsers } = useNostr();
const { db } = useStorage();
const { status, data } = useQuery(
['relays'],
async () => {
return await getAllRelaysByUsers();
},
{ refetchOnMount: false }
{ refetchOnWindowFocus: false }
);
const openRelay = (relayUrl: string) => {
const inspectRelay = (relayUrl: string) => {
const url = new URL(relayUrl);
navigate(`/relays/${url.hostname}`);
};
const connectRelay = async (relayUrl: string) => {
const url = normalizeRelayUrl(relayUrl);
const res = await db.createRelay(url);
if (!res) await message("You're aldready connected to this relay");
queryClient.invalidateQueries(['user-relay']);
};
return (
<div className="col-span-2 border-r border-white/5">
{status === 'loading' ? (
@@ -49,7 +63,7 @@ export function RelayList() {
<div className="inline-flex items-center gap-2">
<button
type="button"
onClick={() => openRelay(key)}
onClick={() => inspectRelay(key)}
className="inline-flex h-6 items-center justify-center gap-1 rounded bg-white/10 px-1.5 text-sm font-medium hover:bg-white/20"
>
<ShareIcon className="h-3 w-3" />
@@ -57,6 +71,7 @@ export function RelayList() {
</button>
<button
type="button"
onClick={() => connectRelay(key)}
className="inline-flex h-6 w-6 items-center justify-center rounded hover:bg-white/10"
>
<PlusIcon className="h-3 w-3" />

View File

@@ -1,9 +1,15 @@
import { useQuery } from '@tanstack/react-query';
import { useQuery, useQueryClient } from '@tanstack/react-query';
import { RelayForm } from '@app/relays/components/relayForm';
import { useNDK } from '@libs/ndk/provider';
import { useStorage } from '@libs/storage/provider';
import { CancelIcon } from '@shared/icons';
export function UserRelay() {
const queryClient = useQueryClient();
const { relayUrls } = useNDK();
const { db } = useStorage();
const { status, data } = useQuery(
@@ -14,6 +20,11 @@ export function UserRelay() {
{ refetchOnWindowFocus: false }
);
const removeRelay = async (relayUrl: string) => {
await db.removeRelay(relayUrl);
queryClient.invalidateQueries(['user-relay']);
};
return (
<div className="mt-3 px-3">
{status === 'loading' ? (
@@ -23,22 +34,34 @@ export function UserRelay() {
{data.map((item) => (
<div
key={item}
className="inline-flex h-10 items-center gap-2.5 rounded-lg bg-white/10 px-3"
className="group flex h-10 items-center justify-between rounded-lg bg-white/10 pl-3 pr-1.5"
>
{relayUrls.includes(item) ? (
<span className="relative flex h-3 w-3">
<span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-green-400 opacity-75"></span>
<span className="relative inline-flex h-3 w-3 rounded-full bg-green-500"></span>
</span>
) : (
<span className="relative flex h-3 w-3">
<span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-red-400 opacity-75"></span>
<span className="relative inline-flex h-3 w-3 rounded-full bg-red-500"></span>
</span>
)}
<p className="text-sm font-medium">{item}</p>
<div className="inline-flex items-center gap-2.5">
{relayUrls.includes(item) ? (
<span className="relative flex h-2 w-2">
<span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-green-400 opacity-75"></span>
<span className="relative inline-flex h-2 w-2 rounded-full bg-green-500"></span>
</span>
) : (
<span className="relative flex h-2 w-2">
<span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-red-400 opacity-75"></span>
<span className="relative inline-flex h-2 w-2 rounded-full bg-red-500"></span>
</span>
)}
<p className="max-w-[20rem] truncate text-sm font-medium leading-none">
{item}
</p>
</div>
<button
type="button"
onClick={() => removeRelay(item)}
className="hidden h-6 w-6 items-center justify-center rounded hover:bg-white/10 group-hover:inline-flex"
>
<CancelIcon className="h-4 w-4 text-white" />
</button>
</div>
))}
<RelayForm />
</div>
)}
</div>