Browse Source

bug-fixes

imwald
Silberengel 1 month ago
parent
commit
9c3023ae49
  1. 14
      src/components/Relay/index.tsx
  2. 33
      src/constants.ts
  3. 9
      src/lib/favorites-feed-relays.ts
  4. 23
      src/lib/relay-url-priority.ts
  5. 5
      src/lib/rss-web-feed.ts
  6. 16
      src/pages/secondary/FollowingListPage/index.tsx
  7. 27
      src/pages/secondary/RelaySettingsPage/index.tsx
  8. 9
      src/services/note-stats.service.ts

14
src/components/Relay/index.tsx

@ -52,6 +52,20 @@ const Relay = forwardRef< @@ -52,6 +52,20 @@ const Relay = forwardRef<
}
}, [normalizedUrl])
/**
* Session strikes skip a relay for reads until cleared. Refresh in the titlebar already clears; without this,
* opening the panel on a striked relay subscribed too late or showed an empty feed while the banner confused users.
* Runs after child effects so the NoteList ref is ready for {@link refresh}.
*/
useEffect(() => {
if (!normalizedUrl) return
if (!client.clearSessionRelayStrikeForUrl(normalizedUrl)) return
setStrikeCount(0)
if (typeof noteListRef !== 'function') {
noteListRef.current?.refresh()
}
}, [normalizedUrl])
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedInput(searchInput)

33
src/constants.ts

@ -230,12 +230,24 @@ export const BOOKSTR_RELAY_URLS = [ @@ -230,12 +230,24 @@ export const BOOKSTR_RELAY_URLS = [
/**
* Block-list order (applied in sequence when building relay lists):
* 1. READ_ONLY never publish
* 2. SOCIAL_KIND_BLOCKED skip for REQ/publish that target {@link SOCIAL_KIND_BLOCKED_KINDS}
* 1. READ_ONLY never publish (search mirrors, index relays, NIP-42 read-only aggregators)
* 2. SOCIAL_KIND_BLOCKED skip for REQ/publish that touch {@link SOCIAL_KIND_BLOCKED_KINDS} (see list below)
* 3. E_TAG_FILTER_BLOCKED skip for reply/quote/stats fetches (#e, #a, #q filters)
*/
/** Relays that must never be used for publishing (read-only aggregators, etc.). */
export const READ_ONLY_RELAY_URLS = ['wss://aggr.nostr.land']
/**
* Relays that must never receive publishes: search engines, index mirrors, and similar endpoints that only ingest
* or aggregate for read. Distinct from {@link SOCIAL_KIND_BLOCKED_RELAY_URLS} (kind-coverage limits, not write policy).
*/
export const READ_ONLY_RELAY_URLS = [
'wss://aggr.nostr.land',
'wss://relay.nostr.watch',
'wss://relaypag.es',
'wss://relay.noswhere.com',
'wss://search.nos.today',
'wss://trending.nostr.wine',
'wss://sendit.nosflare.com',
'wss://relay.nip46.com'
]
/**
* Relays that reject or poorly serve social kinds (short notes, discussions, URL comments).
@ -244,7 +256,6 @@ export const READ_ONLY_RELAY_URLS = ['wss://aggr.nostr.land'] @@ -244,7 +256,6 @@ export const READ_ONLY_RELAY_URLS = ['wss://aggr.nostr.land']
*/
export const SOCIAL_KIND_BLOCKED_RELAY_URLS = [
'wss://thecitadel.nostr1.com',
'wss://hist.nostr.land',
'wss://profiles.nostr1.com',
'wss://purplepag.es',
'wss://relay.nsec.app',
@ -252,12 +263,7 @@ export const SOCIAL_KIND_BLOCKED_RELAY_URLS = [ @@ -252,12 +263,7 @@ export const SOCIAL_KIND_BLOCKED_RELAY_URLS = [
'wss://spatia-arcana.com',
'wss://relay.wikifreedia.xyz',
'wss://relay.gifbuddy.lol',
'wss://relay.noswhere.com',
'wss://aggr.nostr.land',
'wss://search.nos.today',
'wss://trending.nostr.wine',
'wss://sendit.nosflare.com',
'wss://relay.nip46.com'
'wss://hist.nostr.land',
]
/** Relays that reject #e (and similar) tag filters; skip for reply/quote/stats fetches. */
@ -439,8 +445,9 @@ export const THREAD_BACKLINK_STREAM_KINDS_WITHOUT_HIGHLIGHT: readonly number[] = @@ -439,8 +445,9 @@ export const THREAD_BACKLINK_STREAM_KINDS_WITHOUT_HIGHLIGHT: readonly number[] =
THREAD_BACKLINK_STREAM_KINDS.filter((k) => k !== kinds.Highlights)
/**
* Kinds aligned with {@link SOCIAL_KIND_BLOCKED_RELAY_URLS}: omit those relays when querying or publishing
* these kinds (or when `kinds` is omitted on a filter see {@link relayFilterIncludesSocialKindBlockedKind}).
* When a filter touches these kinds (or omits `kinds`), omit {@link SOCIAL_KIND_BLOCKED_RELAY_URLS} from the relay
* stack those relays do not carry this note/comment surface (kinds **1** / **1111** / **11** per relay policy).
* @see {@link relayFilterIncludesSocialKindBlockedKind}
*/
export const SOCIAL_KIND_BLOCKED_KINDS: readonly number[] = [
kinds.ShortTextNote,

9
src/lib/favorites-feed-relays.ts

@ -14,6 +14,7 @@ import { @@ -14,6 +14,7 @@ import {
mergeRelayPriorityLayers,
relayUrlsLocalsFirst
} from '@/lib/relay-url-priority'
import { normalizeAnyRelayUrl } from '@/lib/url'
const blockedSet = (blockedRelays: string[]) =>
new Set(blockedRelays.map((b) => normalizeUrl(b) || b))
@ -179,6 +180,11 @@ export function augmentSubRequestsWithFavoritesFastReadAndInbox( @@ -179,6 +180,11 @@ export function augmentSubRequestsWithFavoritesFastReadAndInbox(
options?: ReadRelayPriorityOptions
): TFeedSubRequest[] {
const max = options?.maxRelays ?? MAX_REQ_RELAY_URLS
const userReadSocialExempt = new Set<string>()
for (const u of userInboxReadRelays) {
const n = normalizeAnyRelayUrl(u) || u.trim()
if (n) userReadSocialExempt.add(n)
}
return requests.map((r) => {
const useSubUrls = options?.mergeSubrequestRelayUrls !== false
const foldIntoAuthor = options?.mergeSubrequestRelaysIntoAuthorTier === true
@ -221,7 +227,8 @@ export function augmentSubRequestsWithFavoritesFastReadAndInbox( @@ -221,7 +227,8 @@ export function augmentSubRequestsWithFavoritesFastReadAndInbox(
return {
...r,
urls: mergeRelayPriorityLayers(layers, blockedRelays, max, {
applySocialKindBlockedFilter: applySocial
applySocialKindBlockedFilter: applySocial,
exemptNormUrlsFromSocialKindBlock: userReadSocialExempt
})
}
})

23
src/lib/relay-url-priority.ts

@ -53,6 +53,12 @@ function socialKindBlockedNormSet(): Set<string> { @@ -53,6 +53,12 @@ function socialKindBlockedNormSet(): Set<string> {
export type MergeRelayPriorityLayersOptions = {
/** When true, drop {@link SOCIAL_KIND_BLOCKED_RELAY_URLS} before applying the max cap. */
applySocialKindBlockedFilter?: boolean
/**
* Normalized relay URLs that stay in the stack even when {@link applySocialKindBlockedFilter} is on e.g. the
* users NIP-65 read list so an explicit inbox still appears under Seen on. ({@link READ_ONLY_RELAY_URLS} such as
* aggr are a separate concern: no publishes, but they are not in {@link SOCIAL_KIND_BLOCKED_RELAY_URLS}.)
*/
exemptNormUrlsFromSocialKindBlock?: Set<string>
}
/**
@ -68,13 +74,20 @@ export function mergeRelayPriorityLayers( @@ -68,13 +74,20 @@ export function mergeRelayPriorityLayers(
const socialBlocked = mergeOpts?.applySocialKindBlockedFilter
? socialKindBlockedNormSet()
: new Set<string>()
const socialExempt = mergeOpts?.exemptNormUrlsFromSocialKindBlock
const seen = new Set<string>()
const out: string[] = []
for (const layer of layers) {
for (const u of layer) {
// Must not use {@link normalizeUrl}: it turns http(s) index relays into ws(s), which then hit the WS pool.
const n = normalizeAnyRelayUrl(u) || u.trim()
if (!n || blocked.has(n) || socialBlocked.has(n) || seen.has(n)) continue
if (!n || blocked.has(n) || seen.has(n)) continue
if (
socialBlocked.has(n) &&
!(socialExempt?.has(n) ?? false)
) {
continue
}
seen.add(n)
out.push(n)
if (out.length >= max) return out
@ -131,6 +144,11 @@ export function buildPrioritizedReadRelayUrls(opts: { @@ -131,6 +144,11 @@ export function buildPrioritizedReadRelayUrls(opts: {
}): string[] {
const max = opts.maxRelays ?? MAX_REQ_RELAY_URLS
const applySocial = opts.applySocialKindBlockedFilter !== false
const exemptFromSocial = new Set<string>()
for (const u of opts.userReadRelays ?? []) {
const n = normalizeAnyRelayUrl(u) || u.trim()
if (n) exemptFromSocial.add(n)
}
const layers = buildReadRelayPriorityLayers({
userReadRelays: opts.userReadRelays,
userWriteRelays: opts.userWriteRelays,
@ -138,7 +156,8 @@ export function buildPrioritizedReadRelayUrls(opts: { @@ -138,7 +156,8 @@ export function buildPrioritizedReadRelayUrls(opts: {
favoriteRelays: opts.favoriteRelays
})
return mergeRelayPriorityLayers(layers, opts.blockedRelays, max, {
applySocialKindBlockedFilter: applySocial
applySocialKindBlockedFilter: applySocial,
exemptNormUrlsFromSocialKindBlock: exemptFromSocial
})
}

5
src/lib/rss-web-feed.ts

@ -176,8 +176,9 @@ export function isRssWebUnifiedClutterUrl(url: string): boolean { @@ -176,8 +176,9 @@ export function isRssWebUnifiedClutterUrl(url: string): boolean {
}
/**
* Split filters: kind 1/1111 in `social` strip aggregator relays from the whole REQ; reactions and
* `#r` queries stay in `nonSocial` so aggr and similar still answer.
* Split filters: `social` uses kinds that match {@link relayFilterIncludesSocialKindBlockedKind} and therefore omit
* {@link SOCIAL_KIND_BLOCKED_RELAY_URLS}; `nonSocial` keeps reactions / `#r` on batches that do not apply that strip.
* Read-only index relays ({@link READ_ONLY_RELAY_URLS}) are unrelated to the social-kind block list.
*/
export function buildRssArticleUrlThreadInteractionFilterGroups(
canonicalArticleUrl: string,

16
src/pages/secondary/FollowingListPage/index.tsx

@ -20,10 +20,22 @@ const FollowingListPage = forwardRef(({ id, index, hideTitlebar = false }: { id? @@ -20,10 +20,22 @@ const FollowingListPage = forwardRef(({ id, index, hideTitlebar = false }: { id?
const { registerPrimaryPanelRefresh } = usePrimaryNoteView()
const [listRefreshNonce, setListRefreshNonce] = useState(0)
const { profile } = useFetchProfile(id)
const { followings } = useFetchFollowings(profile?.pubkey, listRefreshNonce)
const { followings, followListEvent } = useFetchFollowings(profile?.pubkey, listRefreshNonce)
const [jsonOpen, setJsonOpen] = useState(false)
const [followJsonPayload, setFollowJsonPayload] = useState<unknown>(null)
const bumpList = useCallback(() => setListRefreshNonce((n) => n + 1), [])
const openFollowingListJson = useCallback(() => {
setFollowJsonPayload({
pubkey: profile?.pubkey ?? null,
contactsKind3Event: followListEvent ?? null,
derivedFollowingPubkeys: followings,
note: 'Following pubkeys are derived from `p` tags on the kind 3 contacts event when present.'
})
setJsonOpen(true)
}, [profile?.pubkey, followListEvent, followings])
useEffect(() => {
if (!hideTitlebar) {
registerPrimaryPanelRefresh(null)
@ -56,7 +68,7 @@ const FollowingListPage = forwardRef(({ id, index, hideTitlebar = false }: { id? @@ -56,7 +68,7 @@ const FollowingListPage = forwardRef(({ id, index, hideTitlebar = false }: { id?
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem onClick={() => setJsonOpen(true)}>
<DropdownMenuItem onClick={() => openFollowingListJson()}>
<Code className="size-4 mr-2" />
{t('View JSON')}
</DropdownMenuItem>

27
src/pages/secondary/RelaySettingsPage/index.tsx

@ -25,9 +25,36 @@ import { useTranslation } from 'react-i18next' @@ -25,9 +25,36 @@ import { useTranslation } from 'react-i18next'
const RelaySettingsPage = forwardRef(({ index, hideTitlebar = false }: { index?: number; hideTitlebar?: boolean }, ref) => {
const { t } = useTranslation()
const { registerPrimaryPanelRefresh } = usePrimaryNoteView()
const { account, relayList } = useNostr()
const [contentKey, setContentKey] = useState(0)
const bump = useCallback(() => setContentKey((k) => k + 1), [])
const [tabValue, setTabValue] = useState('favorite-relays')
const [jsonOpen, setJsonOpen] = useState(false)
const [jsonPayload, setJsonPayload] = useState<unknown>(null)
const openRelayListJson = useCallback(async () => {
const pk = account?.pubkey
if (!pk) {
setJsonPayload({ error: 'Not logged in' })
setJsonOpen(true)
return
}
const [k10002, k10432, k10243] = await Promise.all([
indexedDb.getReplaceableEvent(pk, kinds.RelayList).catch(() => null),
indexedDb.getReplaceableEvent(pk, ExtendedKind.CACHE_RELAYS).catch(() => null),
indexedDb.getReplaceableEvent(pk, ExtendedKind.HTTP_RELAY_LIST).catch(() => null)
])
setJsonPayload({
pubkey: pk,
mergedRelayList: relayList,
kind10002_mailbox_fromIndexedDb: k10002 ?? null,
kind10432_cacheRelays_fromIndexedDb: k10432 ?? null,
kind10243_httpRelayList_fromIndexedDb: k10243 ?? null,
note:
'Merged list is from the client cache service. IndexedDB values are your locally stored replaceable lists.'
})
setJsonOpen(true)
}, [account?.pubkey, relayList])
useEffect(() => {
switch (window.location.hash) {

9
src/services/note-stats.service.ts

@ -257,7 +257,7 @@ class NoteStatsService { @@ -257,7 +257,7 @@ class NoteStatsService {
seen.add(n)
}
// 1. Broad search index / aggregator relays
// 1. Search / discovery relay set (includes read-only index mirrors; see READ_ONLY_RELAY_URLS in constants)
SEARCHABLE_RELAY_URLS.forEach(add)
// 2. Default fast read set (includes e.g. theforest — not in SEARCHABLE)
@ -294,9 +294,10 @@ class NoteStatsService { @@ -294,9 +294,10 @@ class NoteStatsService {
}
/**
* Split REQ batches so social kinds (1 / 11 / 1111) do not strip aggregator relays from the
* same subscription as reactions and zaps ({@link relayFilterIncludesSocialKindBlockedKind}).
* RSS URL threads also need `#r` + kind 7 for NIP-73 page-targeted likes.
* Split REQ batches: filters that include social kinds (1 / 11 / 1111) trigger
* {@link relayFilterIncludesSocialKindBlockedKind} and drop {@link SOCIAL_KIND_BLOCKED_RELAY_URLS}; keep reactions,
* zaps, and `#r` queries in separate batches so read-only index relays ({@link READ_ONLY_RELAY_URLS}) still answer
* where appropriate. RSS URL threads also need `#r` + kind 7 for NIP-73 page-targeted likes.
*/
private buildFilterGroups(
event: Event,

Loading…
Cancel
Save