fix: connection props

This commit is contained in:
2024-04-23 13:39:53 +01:00
parent eee76e64e5
commit 9ddd8fc6c2
10 changed files with 70 additions and 153 deletions

View File

@ -40,7 +40,7 @@ export default function Relay(props: RelayProps) {
<div> <div>
<b>{name}</b> <b>{name}</b>
</div> </div>
{!connection?.Ephemeral && ( {!connection?.ephemeral && (
<div className="flex g8"> <div className="flex g8">
<AsyncIcon <AsyncIcon
iconName="write" iconName="write"
@ -74,7 +74,7 @@ export default function Relay(props: RelayProps) {
iconName="gear" iconName="gear"
iconSize={16} iconSize={16}
className="button-icon-sm transparent" className="button-icon-sm transparent"
onClick={() => navigate(connection?.Id ?? "")} onClick={() => navigate(connection?.id ?? "")}
/> />
</div> </div>
)} )}

View File

@ -24,7 +24,7 @@ export async function updateRelayConnections(system: SystemInterface, relays: Re
system.ConnectToRelay(k, v); system.ConnectToRelay(k, v);
} }
for (const [k, v] of system.pool) { for (const [k, v] of system.pool) {
if (!relays[k] && !v.Ephemeral) { if (!relays[k] && !v.ephemeral) {
system.DisconnectRelay(k); system.DisconnectRelay(k);
} }
} }

View File

@ -1,98 +0,0 @@
import { unixNow } from "@snort/shared";
import { SnortContext } from "@snort/system-react";
import { useContext, useEffect, useMemo, useState } from "react";
import { FormattedMessage } from "react-intl";
import Timeline from "@/Components/Feed/Timeline";
import useHistoryState from "@/Hooks/useHistoryState";
import useLogin from "@/Hooks/useLogin";
import { debounce, getRelayName, sha256 } from "@/Utils";
interface RelayOption {
url: string;
paid: boolean;
}
export const GlobalTab = () => {
const { relays } = useLogin();
const [relay, setRelay] = useHistoryState(undefined, "global-relay");
const [allRelays, setAllRelays] = useHistoryState(undefined, "global-relay-options");
const [now] = useState(unixNow());
const system = useContext(SnortContext);
function globalRelaySelector() {
if (!allRelays || allRelays.length === 0) return null;
const paidRelays = allRelays.filter(a => a.paid);
const publicRelays = allRelays.filter(a => !a.paid);
return (
<div className="flex items-center g8 justify-end nowrap">
<h3>
<FormattedMessage
defaultMessage="Relay"
id="KHK8B9"
description="Label for reading global feed from specific relays"
/>
</h3>
<select
className="f-ellipsis"
onChange={e => setRelay(allRelays.find(a => a.url === e.target.value))}
value={relay?.url}>
{paidRelays.length > 0 && (
<optgroup label="Paid Relays">
{paidRelays.map(a => (
<option key={a.url} value={a.url}>
{getRelayName(a.url)}
</option>
))}
</optgroup>
)}
<optgroup label="Public Relays">
{publicRelays.map(a => (
<option key={a.url} value={a.url}>
{getRelayName(a.url)}
</option>
))}
</optgroup>
</select>
</div>
);
}
useEffect(() => {
return debounce(500, () => {
const ret: RelayOption[] = [];
[...system.pool].forEach(([, v]) => {
if (!v.IsClosed) {
ret.push({
url: v.Address,
paid: v.Info?.limitation?.payment_required ?? false,
});
}
});
ret.sort(a => (a.paid ? -1 : 1));
if (ret.length > 0 && !relay) {
setRelay(ret[0]);
}
setAllRelays(ret);
});
}, [relays, relay]);
const subject = useMemo(
() => ({
type: "global",
items: [],
relay: [relay?.url],
discriminator: `all-${sha256(relay?.url ?? "")}`,
}),
[relay?.url],
);
return (
<>
{globalRelaySelector()}
{relay && <Timeline subject={subject} postsOnly={false} method={"TIME_RANGE"} window={600} now={now} />}
</>
);
};

View File

@ -7,7 +7,6 @@ import { ConversationsTab } from "@/Pages/Root/ConversationsTab";
import { DefaultTab } from "@/Pages/Root/DefaultTab"; import { DefaultTab } from "@/Pages/Root/DefaultTab";
import { FollowedByFriendsTab } from "@/Pages/Root/FollowedByFriendsTab"; import { FollowedByFriendsTab } from "@/Pages/Root/FollowedByFriendsTab";
import { ForYouTab } from "@/Pages/Root/ForYouTab"; import { ForYouTab } from "@/Pages/Root/ForYouTab";
import { GlobalTab } from "@/Pages/Root/GlobalTab";
import { NotesTab } from "@/Pages/Root/NotesTab"; import { NotesTab } from "@/Pages/Root/NotesTab";
import { TagsTab } from "@/Pages/Root/TagsTab"; import { TagsTab } from "@/Pages/Root/TagsTab";
import { TopicsPage } from "@/Pages/TopicsPage"; import { TopicsPage } from "@/Pages/TopicsPage";
@ -15,7 +14,6 @@ import { TopicsPage } from "@/Pages/TopicsPage";
export type RootTabRoutePath = export type RootTabRoutePath =
| "" | ""
| "for-you" | "for-you"
| "global"
| "following" | "following"
| "followed-by-friends" | "followed-by-friends"
| "conversations" | "conversations"
@ -41,10 +39,6 @@ export const RootTabRoutes: RootTabRoute[] = [
path: "for-you", path: "for-you",
element: <ForYouTab />, element: <ForYouTab />,
}, },
{
path: "global",
element: <GlobalTab />,
},
{ {
path: "following", path: "following",
element: <NotesTab />, element: <NotesTab />,

View File

@ -30,10 +30,10 @@ export function ZapPoolPageInner() {
const relayConnections = useMemo(() => { const relayConnections = useMemo(() => {
return [...system.pool] return [...system.pool]
.map(([, a]) => { .map(([, a]) => {
if (a.Info?.pubkey && !a.Ephemeral) { if (a.info?.pubkey && !a.ephemeral) {
return { return {
address: a.Address, address: a.address,
pubkey: a.Info.pubkey, pubkey: a.info.pubkey,
}; };
} }
}) })

View File

@ -1,3 +1,4 @@
import { Connection } from "@snort/system";
import { FormattedMessage } from "react-intl"; import { FormattedMessage } from "react-intl";
import { useNavigate, useParams } from "react-router-dom"; import { useNavigate, useParams } from "react-router-dom";
@ -16,66 +17,66 @@ const RelayInfo = () => {
const login = useLogin(); const login = useLogin();
const { system } = useEventPublisher(); const { system } = useEventPublisher();
const conn = [...system.pool].find(([, a]) => a.Id === params.id)?.[1]; const conn = [...system.pool].find(([, a]) => a.id === params.id)?.[1];
const stats = useRelayState(conn?.Address ?? ""); const stats = useRelayState(conn?.address ?? "");
return ( return (
<> <>
<h3 className="pointer" onClick={() => navigate("/settings/relays")}> <h3 className="pointer" onClick={() => navigate("/settings/relays")}>
<FormattedMessage {...messages.Relays} /> <FormattedMessage {...messages.Relays} />
</h3> </h3>
<div> <div>
<h3>{stats?.Info?.name}</h3> <h3>{stats?.info?.name}</h3>
<p>{stats?.Info?.description}</p> <p>{stats?.info?.description}</p>
{stats?.Info?.pubkey && ( {stats?.info?.pubkey && (
<> <>
<h4> <h4>
<FormattedMessage {...messages.Owner} /> <FormattedMessage {...messages.Owner} />
</h4> </h4>
<ProfilePreview pubkey={parseId(stats.Info.pubkey)} /> <ProfilePreview pubkey={parseId(stats.info.pubkey)} />
</> </>
)} )}
{stats?.Info?.software && ( {stats?.info?.software && (
<div className="flex"> <div className="flex">
<h4 className="grow"> <h4 className="grow">
<FormattedMessage {...messages.Software} /> <FormattedMessage {...messages.Software} />
</h4> </h4>
<div className="flex flex-col"> <div className="flex flex-col">
{stats.Info.software.startsWith("http") ? ( {stats.info.software.startsWith("http") ? (
<a href={stats.Info.software} target="_blank" rel="noreferrer"> <a href={stats.info.software} target="_blank" rel="noreferrer">
{stats.Info.software} {stats.info.software}
</a> </a>
) : ( ) : (
<>{stats.Info.software}</> <>{stats.info.software}</>
)} )}
<small> <small>
{!stats.Info.version?.startsWith("v") && "v"} {!stats.info.version?.startsWith("v") && "v"}
{stats.Info.version} {stats.info.version}
</small> </small>
</div> </div>
</div> </div>
)} )}
{stats?.Info?.contact && ( {stats?.info?.contact && (
<div className="flex"> <div className="flex">
<h4 className="grow"> <h4 className="grow">
<FormattedMessage {...messages.Contact} /> <FormattedMessage {...messages.Contact} />
</h4> </h4>
<a <a
href={`${stats.Info.contact.startsWith("mailto:") ? "" : "mailto:"}${stats.Info.contact}`} href={`${stats.info.contact.startsWith("mailto:") ? "" : "mailto:"}${stats.info.contact}`}
target="_blank" target="_blank"
rel="noreferrer"> rel="noreferrer">
{stats.Info.contact} {stats.info.contact}
</a> </a>
</div> </div>
)} )}
{stats?.Info?.supported_nips && ( {stats?.info?.supported_nips && (
<> <>
<h4> <h4>
<FormattedMessage {...messages.Supports} /> <FormattedMessage {...messages.Supports} />
</h4> </h4>
<div className="grow"> <div className="grow">
{stats.Info?.supported_nips?.map(a => ( {stats.info?.supported_nips?.map(a => (
<a key={a} target="_blank" rel="noreferrer" href={`https://nips.be/${a}`} className="pill"> <a key={a} target="_blank" rel="noreferrer" href={`https://nips.be/${a}`} className="pill">
NIP-{a.toString().padStart(2, "0")} NIP-{a.toString().padStart(2, "0")}
</a> </a>
@ -83,30 +84,38 @@ const RelayInfo = () => {
</div> </div>
</> </>
)} )}
<h4> {conn instanceof Connection && (
<FormattedMessage defaultMessage="Active Subscriptions" id="p85Uwy" /> <>
</h4> <h4>
<div className="grow"> <FormattedMessage defaultMessage="Active Subscriptions" id="p85Uwy" />
{[...(stats?.ActiveRequests ?? [])].map(a => ( </h4>
<span className="pill" key={a}> <div className="grow">
{a} {conn.ActiveRequests.map(a => (
</span> <span className="pill" key={a}>
))} {a}
</div> </span>
<h4> ))}
<FormattedMessage defaultMessage="Pending Subscriptions" id="UDYlxu" /> </div>
</h4> </>
<div className="grow"> )}
{stats?.PendingRequests?.map(a => ( {conn instanceof Connection && (
<span className="pill" key={a.obj[1]}> <>
{a.obj[1]} <h4>
</span> <FormattedMessage defaultMessage="Pending Subscriptions" id="UDYlxu" />
))} </h4>
</div> <div className="grow">
{conn.PendingRequests.map(a => (
<span className="pill" key={a.obj[1]}>
{a.obj[1]}
</span>
))}
</div>
</>
)}
<div className="flex mt10 justify-end"> <div className="flex mt10 justify-end">
<AsyncButton <AsyncButton
onClick={async () => { onClick={async () => {
await login.state.removeRelay(unwrap(conn).Address, true); await login.state.removeRelay(unwrap(conn).address, true);
navigate("/settings/relays"); navigate("/settings/relays");
}}> }}>
<FormattedMessage {...messages.Remove} /> <FormattedMessage {...messages.Remove} />

View File

@ -78,7 +78,7 @@ const RelaySettingsPage = () => {
</h3> </h3>
<div className="flex flex-col g8"> <div className="flex flex-col g8">
{otherConnections.map(a => ( {otherConnections.map(a => (
<Relay addr={a.Address} key={a.Id} /> <Relay addr={a.address} key={a.id} />
))} ))}
</div> </div>
</div> </div>

View File

@ -104,7 +104,7 @@ export class Zapper {
if (!svc) { if (!svc) {
throw new Error(`Failed to get invoice from ${t.value}`); throw new Error(`Failed to get invoice from ${t.value}`);
} }
const relays = [...this.system.pool].filter(([, v]) => !v.Ephemeral).map(([k]) => k); const relays = [...this.system.pool].filter(([, v]) => !v.ephemeral).map(([k]) => k);
const pub = t.zap?.anon ?? false ? EventPublisher.privateKey(generateRandomKey().privateKey) : this.publisher; const pub = t.zap?.anon ?? false ? EventPublisher.privateKey(generateRandomKey().privateKey) : this.publisher;
const zap = const zap =
t.zap && svc.canZap t.zap && svc.canZap

View File

@ -31,6 +31,7 @@ export type ConnectionType = {
readonly address: string; readonly address: string;
readonly info: RelayInfo | undefined; readonly info: RelayInfo | undefined;
readonly isDown: boolean; readonly isDown: boolean;
readonly isOpen: boolean;
settings: RelaySettings; settings: RelaySettings;
ephemeral: boolean; ephemeral: boolean;

View File

@ -3,7 +3,14 @@ import { QueryLike, SystemConfig, SystemInterface } from "./system";
import { RelaySettings, SyncCommand } from "./connection"; import { RelaySettings, SyncCommand } from "./connection";
import { TaggedNostrEvent, NostrEvent, OkResponse, ReqCommand } from "./nostr"; import { TaggedNostrEvent, NostrEvent, OkResponse, ReqCommand } from "./nostr";
import { BuiltRawReqFilter, RequestBuilder } from "./request-builder"; import { BuiltRawReqFilter, RequestBuilder } from "./request-builder";
import NDK, { NDKConstructorParams, NDKEvent, NDKFilter, NDKRelay, NDKSubscription } from "@nostr-dev-kit/ndk"; import NDK, {
NDKConstructorParams,
NDKEvent,
NDKFilter,
NDKRelay,
NDKSubscription,
NDKRelayStatus,
} from "@nostr-dev-kit/ndk";
import { SystemBase } from "./system-base"; import { SystemBase } from "./system-base";
import { ConnectionPool, ConnectionType, ConnectionTypeEvents, DefaultConnectionPool } from "./connection-pool"; import { ConnectionPool, ConnectionType, ConnectionTypeEvents, DefaultConnectionPool } from "./connection-pool";
import { RelayMetadataLoader } from "./outbox"; import { RelayMetadataLoader } from "./outbox";
@ -53,7 +60,11 @@ class NDKConnection extends EventEmitter<ConnectionTypeEvents> implements Connec
} }
get isDown() { get isDown() {
return !this.relay.connectivity.isAvailable(); return this.relay.connectivity.status === NDKRelayStatus.FLAPPING;
}
get isOpen() {
return this.relay.connectivity.status === NDKRelayStatus.CONNECTED;
} }
info: RelayInfo | undefined; info: RelayInfo | undefined;