feat: use close relays
This commit is contained in:
parent
55c938961f
commit
588c3756fd
@ -1,5 +1,4 @@
|
||||
.relay {
|
||||
background-color: var(--gray-secondary);
|
||||
border-radius: 5px;
|
||||
display: grid;
|
||||
grid-template-columns: min-content auto;
|
||||
|
@ -41,7 +41,7 @@ export default function Relay(props: RelayProps) {
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="relay">
|
||||
<div className="relay bg-dark">
|
||||
<div className={classNames("flex items-center", state?.connected ? "bg-success" : "bg-error")}>
|
||||
<RelayFavicon url={props.addr} />
|
||||
</div>
|
||||
|
14
packages/app/src/External/SnortApi.ts
vendored
14
packages/app/src/External/SnortApi.ts
vendored
@ -68,6 +68,16 @@ export interface TranslationResponse {
|
||||
}>;
|
||||
}
|
||||
|
||||
export interface RelayDistance {
|
||||
url: string;
|
||||
distance: number;
|
||||
users: number;
|
||||
country?: string;
|
||||
city?: string;
|
||||
is_paid?: boolean;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export default class SnortApi {
|
||||
#url: string;
|
||||
#publisher?: EventPublisher;
|
||||
@ -121,6 +131,10 @@ export default class SnortApi {
|
||||
return this.#getJson<TranslationResponse | object>("api/v1/translate", "POST", tx);
|
||||
}
|
||||
|
||||
closeRelays(lat: number, lon: number, count = 5) {
|
||||
return this.#getJson<Array<RelayDistance>>(`api/v1/relays?count=${count}`, "POST", { lat, lon });
|
||||
}
|
||||
|
||||
async #getJsonAuthd<T>(
|
||||
path: string,
|
||||
method?: "GET" | string,
|
||||
|
@ -15,10 +15,11 @@ import * as utils from "@noble/curves/abstract/utils";
|
||||
import { DefaultRelays, SnortPubKey } from "Const";
|
||||
import { LoginStore, UserPreferences, LoginSession, LoginSessionType, SnortAppData, Newest } from "Login";
|
||||
import { generateBip39Entropy, entropyToPrivateKey } from "nip6";
|
||||
import { bech32ToHex, dedupeById, sanitizeRelayUrl, unwrap } from "SnortUtils";
|
||||
import { bech32ToHex, dedupeById, getCountry, sanitizeRelayUrl, unwrap } from "SnortUtils";
|
||||
import { SubscriptionEvent } from "Subscription";
|
||||
import { Chats, FollowsFeed, GiftsCache, Notifications } from "Cache";
|
||||
import { Nip7OsSigner } from "./Nip7OsSigner";
|
||||
import SnortApi from "External/SnortApi";
|
||||
|
||||
export function setRelays(state: LoginSession, relays: Record<string, RelaySettings>, createdAt: number) {
|
||||
if (SINGLE_RELAY) {
|
||||
@ -94,6 +95,21 @@ export async function generateNewLogin(
|
||||
const privateKey = entropyToPrivateKey(ent);
|
||||
const newRelays = Object.fromEntries(DefaultRelays.entries());
|
||||
|
||||
// Use current timezone info to determine approx location
|
||||
// use closest 5 relays
|
||||
const country = getCountry();
|
||||
const api = new SnortApi();
|
||||
const closeRelays = await api.closeRelays(country.lat, country.lon, 10);
|
||||
for (const cr of closeRelays.sort((a, b) => (a.distance > b.distance ? 1 : -1)).filter(a => !a.is_paid)) {
|
||||
const rr = sanitizeRelayUrl(cr.url);
|
||||
if (rr) {
|
||||
newRelays[rr] = { read: true, write: true };
|
||||
if (Object.keys(newRelays).length >= 5) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// connect to new relays
|
||||
await Promise.all(Object.entries(newRelays).map(([k, v]) => system.ConnectToRelay(k, v)));
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { useMemo, useState } from "react";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
import { unixNowMs } from "@snort/shared";
|
||||
import { unixNowMs, unwrap } from "@snort/shared";
|
||||
import { EventPublisher, FullRelaySettings, RelaySettings, SystemInterface } from "@snort/system";
|
||||
|
||||
import Relay from "Element/Relay/Relay";
|
||||
@ -8,9 +8,11 @@ import useEventPublisher from "Hooks/useEventPublisher";
|
||||
import useLogin from "Hooks/useLogin";
|
||||
import { setRelays } from "Login";
|
||||
import AsyncButton from "Element/AsyncButton";
|
||||
import SnortApi, { RelayDistance } from "External/SnortApi";
|
||||
import { getCountry, getRelayName, sanitizeRelayUrl } from "SnortUtils";
|
||||
import { formatShort } from "Number";
|
||||
|
||||
import messages from "./messages";
|
||||
|
||||
const Blasters = ["wss://nostr.mutinywallet.com"];
|
||||
|
||||
export async function saveRelays(
|
||||
@ -88,6 +90,7 @@ const RelaySettingsPage = () => {
|
||||
<FormattedMessage {...messages.Save} />
|
||||
</AsyncButton>
|
||||
{addRelay()}
|
||||
<CloseRelays />
|
||||
<h3>
|
||||
<FormattedMessage defaultMessage="Other Connections" />
|
||||
</h3>
|
||||
@ -101,3 +104,89 @@ const RelaySettingsPage = () => {
|
||||
};
|
||||
|
||||
export default RelaySettingsPage;
|
||||
|
||||
export function CloseRelays() {
|
||||
const [relays, setRecommendedRelays] = useState<Array<RelayDistance>>();
|
||||
const country = getCountry();
|
||||
const [location, setLocation] = useState<{ lat: number; lon: number }>(country);
|
||||
const login = useLogin();
|
||||
const relayUrls = Object.keys(login.relays.item);
|
||||
|
||||
async function loadRelays() {
|
||||
const api = new SnortApi();
|
||||
setRecommendedRelays(await api.closeRelays(location.lat, location.lon, 10));
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
loadRelays().catch(console.error);
|
||||
}, [location]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<h3>
|
||||
<FormattedMessage defaultMessage="Recommended Relays" />
|
||||
</h3>
|
||||
{"geolocation" in navigator && (
|
||||
<AsyncButton
|
||||
onClick={async () => {
|
||||
try {
|
||||
const pos = await new Promise<GeolocationPosition>((resolve, reject) => {
|
||||
navigator.geolocation.getCurrentPosition(resolve, reject);
|
||||
});
|
||||
setLocation({
|
||||
lat: pos.coords.latitude,
|
||||
lon: pos.coords.longitude,
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}}>
|
||||
<FormattedMessage defaultMessage="Use Exact Location" />
|
||||
</AsyncButton>
|
||||
)}
|
||||
{relays
|
||||
?.filter(a => !relayUrls.includes(unwrap(sanitizeRelayUrl(a.url))) && !a.is_paid)
|
||||
.sort((a, b) => (a.distance > b.distance ? 1 : -1))
|
||||
.map(a => (
|
||||
<div className="bg-dark p br flex flex-col g8">
|
||||
<div className="flex justify-between items-center">
|
||||
<div className="bold">{getRelayName(a.url)}</div>
|
||||
<AsyncButton
|
||||
onClick={async () => {
|
||||
setRelays(
|
||||
login,
|
||||
{
|
||||
...login.relays.item,
|
||||
[a.url]: { read: true, write: true },
|
||||
},
|
||||
unixNowMs(),
|
||||
);
|
||||
}}>
|
||||
<FormattedMessage defaultMessage="Add" />
|
||||
</AsyncButton>
|
||||
</div>
|
||||
<div className="flex flex-col g8">
|
||||
<span>{a.description}</span>
|
||||
<small>
|
||||
<FormattedMessage
|
||||
defaultMessage="{n} km - {location}"
|
||||
values={{
|
||||
n: (a.distance / 1000).toFixed(0),
|
||||
location: a.city ? `${a.city}, ${a.country}` : a.country,
|
||||
}}
|
||||
/>
|
||||
</small>
|
||||
<small>
|
||||
<FormattedMessage
|
||||
defaultMessage="{n} users"
|
||||
values={{
|
||||
n: formatShort(a.users),
|
||||
}}
|
||||
/>
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -517,12 +517,15 @@ export function getDisplayNameOrPlaceHolder(user: UserMetadata | undefined, pubk
|
||||
export function getCountry() {
|
||||
const tz = Intl.DateTimeFormat().resolvedOptions();
|
||||
const info = (TZ as Record<string, Array<string> | undefined>)[tz.timeZone];
|
||||
const [, lat, lon] = info?.[1].split(/[-+]/) ?? ["", "00", "000"];
|
||||
const pos = info?.[1];
|
||||
const sep = Number(pos?.slice(1).search(/[-+]/)) + 1;
|
||||
const [lat, lon] = [pos?.slice(0, sep) ?? "00", pos?.slice(sep) ?? "000"];
|
||||
return {
|
||||
zone: tz.timeZone,
|
||||
country: info?.[0],
|
||||
lat: Number(lat) / Math.pow(10, lat.length - 2),
|
||||
lon: Number(lon) / Math.pow(10, lon.length - 3),
|
||||
lat: Number(lat) / Math.pow(10, lat.length - 3),
|
||||
lon: Number(lon) / Math.pow(10, lon.length - 4),
|
||||
info,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -66,6 +66,9 @@
|
||||
"0BUTMv": {
|
||||
"defaultMessage": "Search..."
|
||||
},
|
||||
"0HFX0T": {
|
||||
"defaultMessage": "Use Exact Location"
|
||||
},
|
||||
"0jOEtS": {
|
||||
"defaultMessage": "Invalid LNURL"
|
||||
},
|
||||
@ -81,6 +84,9 @@
|
||||
"0yO7wF": {
|
||||
"defaultMessage": "{n} secs"
|
||||
},
|
||||
"1H4Keq": {
|
||||
"defaultMessage": "{n} users"
|
||||
},
|
||||
"1Mo59U": {
|
||||
"defaultMessage": "Are you sure you want to remove this note from bookmarks?"
|
||||
},
|
||||
@ -834,6 +840,9 @@
|
||||
"UrKTqQ": {
|
||||
"defaultMessage": "You have an active iris.to account"
|
||||
},
|
||||
"VL900k": {
|
||||
"defaultMessage": "Recommended Relays"
|
||||
},
|
||||
"VN0+Fz": {
|
||||
"defaultMessage": "Balance: {amount} sats"
|
||||
},
|
||||
@ -1158,6 +1167,9 @@
|
||||
"jMzO1S": {
|
||||
"defaultMessage": "Internal error: {msg}"
|
||||
},
|
||||
"jTrbGf": {
|
||||
"defaultMessage": "{n} km - {location}"
|
||||
},
|
||||
"jfV8Wr": {
|
||||
"defaultMessage": "Back",
|
||||
"description": "Navigate back button on threads view"
|
||||
|
@ -21,11 +21,13 @@
|
||||
"08zn6O": "Export Keys",
|
||||
"0Azlrb": "Manage",
|
||||
"0BUTMv": "Search...",
|
||||
"0HFX0T": "Use Exact Location",
|
||||
"0jOEtS": "Invalid LNURL",
|
||||
"0mch2Y": "name has disallowed characters",
|
||||
"0siT4z": "Politics",
|
||||
"0uoY11": "Show Status",
|
||||
"0yO7wF": "{n} secs",
|
||||
"1H4Keq": "{n} users",
|
||||
"1Mo59U": "Are you sure you want to remove this note from bookmarks?",
|
||||
"1R43+L": "Enter Nostr Wallet Connect config",
|
||||
"1c4YST": "Connected to: {node} 🎉",
|
||||
@ -274,6 +276,7 @@
|
||||
"Ub+AGc": "Sign In",
|
||||
"Up5U7K": "Block",
|
||||
"UrKTqQ": "You have an active iris.to account",
|
||||
"VL900k": "Recommended Relays",
|
||||
"VN0+Fz": "Balance: {amount} sats",
|
||||
"VOjC1i": "Pick which upload service you want to upload attachments to",
|
||||
"VR5eHw": "Public key (npub/nprofile)",
|
||||
@ -381,6 +384,7 @@
|
||||
"jAmfGl": "Your {site_name} subscription is expired",
|
||||
"jHa/ko": "Clean up your feed",
|
||||
"jMzO1S": "Internal error: {msg}",
|
||||
"jTrbGf": "{n} km - {location}",
|
||||
"jfV8Wr": "Back",
|
||||
"jvo0vs": "Save",
|
||||
"jzgQ2z": "{n} Reactions",
|
||||
|
Loading…
x
Reference in New Issue
Block a user