mirror of
https://github.com/hoornet/vega.git
synced 2026-05-06 12:19:11 -07:00
Fix relay dedup: normalize URLs to prevent trailing-slash duplicates
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { getNDK, getStoredRelayUrls, addRelay, removeRelay, publishRelayList, fetchRelayRecommendations } from "../../lib/nostr";
|
||||
import { getNDK, getStoredRelayUrls, addRelay, removeRelay, publishRelayList, fetchRelayRecommendations, normalizeRelayUrl } from "../../lib/nostr";
|
||||
import { useRelayHealthStore } from "../../stores/relayHealth";
|
||||
import { useUserStore } from "../../stores/user";
|
||||
import type { RelayHealthResult } from "../../lib/nostr/relayHealth";
|
||||
@@ -154,7 +154,7 @@ export function RelaysView() {
|
||||
const { loggedIn } = useUserStore();
|
||||
const ndk = getNDK();
|
||||
const poolRelays = Array.from(ndk.pool?.relays?.values() ?? []);
|
||||
const poolConnectedUrls = new Set(poolRelays.filter((r) => r.connected).map((r) => r.url));
|
||||
const poolConnectedUrls = new Set(poolRelays.filter((r) => r.connected).map((r) => normalizeRelayUrl(r.url)));
|
||||
|
||||
const [input, setInput] = useState("");
|
||||
const [addError, setAddError] = useState<string | null>(null);
|
||||
@@ -180,7 +180,7 @@ export function RelaysView() {
|
||||
setAddError("URL must start with ws:// or wss://");
|
||||
return;
|
||||
}
|
||||
if (getStoredRelayUrls().includes(url)) {
|
||||
if (getStoredRelayUrls().includes(normalizeRelayUrl(url))) {
|
||||
setAddError("Already in list");
|
||||
return;
|
||||
}
|
||||
@@ -218,8 +218,8 @@ export function RelaysView() {
|
||||
};
|
||||
|
||||
// Merge: show health results first, then any pool relays not yet checked
|
||||
const checkedUrls = new Set(results.map((r) => r.url));
|
||||
const uncheckedPoolRelays = poolRelays.filter((r) => !checkedUrls.has(r.url));
|
||||
const checkedUrls = new Set(results.map((r) => normalizeRelayUrl(r.url)));
|
||||
const uncheckedPoolRelays = poolRelays.filter((r) => !checkedUrls.has(normalizeRelayUrl(r.url)));
|
||||
|
||||
// Sort: online first, then slow, then offline
|
||||
const sortedResults = [...results].sort((a, b) => {
|
||||
|
||||
@@ -51,16 +51,30 @@ export const OUTBOX_RELAYS = [
|
||||
"wss://relay.nostr.band/",
|
||||
];
|
||||
|
||||
/** Normalize relay URL: lowercase host, strip trailing slash, deduplicate. */
|
||||
export function normalizeRelayUrl(url: string): string {
|
||||
return url.replace(/\/+$/, "");
|
||||
}
|
||||
|
||||
export function getStoredRelayUrls(): string[] {
|
||||
try {
|
||||
const stored = localStorage.getItem(RELAY_STORAGE_KEY);
|
||||
if (stored) return JSON.parse(stored);
|
||||
if (stored) {
|
||||
// Deduplicate on load (handles legacy duplicates from trailing-slash mismatch)
|
||||
const urls: string[] = JSON.parse(stored);
|
||||
const seen = new Set<string>();
|
||||
return urls.map(normalizeRelayUrl).filter((u) => {
|
||||
if (seen.has(u)) return false;
|
||||
seen.add(u);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
} catch { /* ignore */ }
|
||||
return FALLBACK_RELAYS;
|
||||
}
|
||||
|
||||
export function saveRelayUrls(urls: string[]) {
|
||||
localStorage.setItem(RELAY_STORAGE_KEY, JSON.stringify(urls));
|
||||
localStorage.setItem(RELAY_STORAGE_KEY, JSON.stringify(urls.map(normalizeRelayUrl)));
|
||||
}
|
||||
|
||||
let ndk: NDK | null = null;
|
||||
@@ -119,13 +133,15 @@ export async function resetNDK(): Promise<void> {
|
||||
}
|
||||
|
||||
export function addRelay(url: string): void {
|
||||
const normalized = normalizeRelayUrl(url);
|
||||
const instance = getNDK();
|
||||
const urls = getStoredRelayUrls();
|
||||
if (!urls.includes(url)) {
|
||||
saveRelayUrls([...urls, url]);
|
||||
if (!urls.includes(normalized)) {
|
||||
saveRelayUrls([...urls, normalized]);
|
||||
}
|
||||
if (!instance.pool?.relays.has(url)) {
|
||||
const relay = new NDKRelay(url, undefined, instance);
|
||||
// Check both with and without trailing slash since NDK may use either
|
||||
if (!instance.pool?.relays.has(normalized) && !instance.pool?.relays.has(normalized + "/")) {
|
||||
const relay = new NDKRelay(normalized, undefined, instance);
|
||||
instance.pool?.addRelay(relay, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export { getNDK, getNDKUptimeMs, connectToRelays, ensureConnected, resetNDK, getStoredRelayUrls, addRelay, removeRelay, fetchWithTimeout, withTimeout, FEED_TIMEOUT, THREAD_TIMEOUT, SINGLE_TIMEOUT } from "./core";
|
||||
export { getNDK, getNDKUptimeMs, connectToRelays, ensureConnected, resetNDK, getStoredRelayUrls, normalizeRelayUrl, addRelay, removeRelay, fetchWithTimeout, withTimeout, FEED_TIMEOUT, THREAD_TIMEOUT, SINGLE_TIMEOUT } from "./core";
|
||||
export { fetchGlobalFeed, fetchFollowFeed, fetchUserNotes, fetchUserNotesNIP65, fetchNoteById, fetchReplies, publishNote, publishReply, publishRepost, publishQuote, fetchHashtagFeed, fetchThreadEvents, fetchAncestors } from "./notes";
|
||||
export { publishProfile, publishContactList, fetchProfile, fetchFollowSuggestions, fetchMentions, fetchFollowers, fetchNewFollowers } from "./social";
|
||||
export { publishArticle, fetchArticle, fetchAuthorArticles, fetchArticleFeed, searchArticles, fetchByAddr } from "./articles";
|
||||
|
||||
Reference in New Issue
Block a user