12 changed files with 186 additions and 43 deletions
@ -0,0 +1,36 @@ |
|||||||
|
import { describe, expect, it } from 'vitest' |
||||||
|
import { |
||||||
|
filterViewerBlockedRelaysForFetch, |
||||||
|
parseBlockedRelayUrlsFromEvent, |
||||||
|
setViewerBlockedRelayUrls |
||||||
|
} from './viewer-blocked-relays' |
||||||
|
|
||||||
|
describe('viewer-blocked-relays', () => { |
||||||
|
it('parseBlockedRelayUrlsFromEvent dedupes relay tags', () => { |
||||||
|
setViewerBlockedRelayUrls([]) |
||||||
|
const urls = parseBlockedRelayUrlsFromEvent({ |
||||||
|
kind: 10006, |
||||||
|
tags: [ |
||||||
|
['relay', 'wss://freelay.sovbit.host/'], |
||||||
|
['relay', 'wss://freelay.sovbit.host'] |
||||||
|
], |
||||||
|
content: '', |
||||||
|
created_at: 1, |
||||||
|
id: 'x', |
||||||
|
pubkey: 'p', |
||||||
|
sig: 's' |
||||||
|
}) |
||||||
|
expect(urls).toEqual(['wss://freelay.sovbit.host/']) |
||||||
|
}) |
||||||
|
|
||||||
|
it('filterViewerBlockedRelaysForFetch matches hostname across schemes', () => { |
||||||
|
setViewerBlockedRelayUrls(['wss://freelay.sovbit.host/']) |
||||||
|
expect( |
||||||
|
filterViewerBlockedRelaysForFetch([ |
||||||
|
'wss://freelay.sovbit.host/', |
||||||
|
'wss://relay.example.com/', |
||||||
|
'https://freelay.sovbit.host/' |
||||||
|
]) |
||||||
|
).toEqual(['wss://relay.example.com/']) |
||||||
|
}) |
||||||
|
}) |
||||||
@ -0,0 +1,32 @@ |
|||||||
|
import type { Event } from 'nostr-tools' |
||||||
|
import { isRelayBlockedByUser } from '@/lib/relay-blocked' |
||||||
|
import { normalizeAnyRelayUrl } from '@/lib/url' |
||||||
|
|
||||||
|
let viewerBlockedRelayUrls: readonly string[] = [] |
||||||
|
|
||||||
|
/** Kind 10006 `relay` tags → normalized URLs (deduped). */ |
||||||
|
export function parseBlockedRelayUrlsFromEvent(event: Event | null | undefined): string[] { |
||||||
|
const out: string[] = [] |
||||||
|
if (!event) return out |
||||||
|
event.tags.forEach(([tagName, tagValue]) => { |
||||||
|
if (tagName !== 'relay' || !tagValue) return |
||||||
|
const n = normalizeAnyRelayUrl(tagValue) |
||||||
|
if (n && !out.includes(n)) out.push(n) |
||||||
|
}) |
||||||
|
return out |
||||||
|
} |
||||||
|
|
||||||
|
/** Updated from IDB hydration and {@link FavoriteRelaysProvider} when the block list changes. */ |
||||||
|
export function setViewerBlockedRelayUrls(urls: readonly string[]): void { |
||||||
|
viewerBlockedRelayUrls = urls.length ? [...urls] : [] |
||||||
|
} |
||||||
|
|
||||||
|
export function getViewerBlockedRelayUrls(): readonly string[] { |
||||||
|
return viewerBlockedRelayUrls |
||||||
|
} |
||||||
|
|
||||||
|
/** Drop user-blocked relays (hostname-aware) before any REQ / query / WebSocket connect. */ |
||||||
|
export function filterViewerBlockedRelaysForFetch(urls: readonly string[]): string[] { |
||||||
|
if (!viewerBlockedRelayUrls.length) return [...urls] |
||||||
|
return urls.filter((u) => !isRelayBlockedByUser(u, viewerBlockedRelayUrls)) |
||||||
|
} |
||||||
Loading…
Reference in new issue