Skip to content

feat: display listings from nostr - #38

Draft
lambdakilo wants to merge 1 commit into
mainfrom
37/nostr-events-landing-page
Draft

feat: display listings from nostr#38
lambdakilo wants to merge 1 commit into
mainfrom
37/nostr-events-landing-page

Conversation

@lambdakilo

@lambdakilo lambdakilo commented Aug 14, 2026

Copy link
Copy Markdown
Member

Summary

task as given: "display nostr events in listing view i.e. the landing page ... the code should
be as minimal as possible and as close to official documentation and best practices as possible
because its production code and not poc. i think the bounties should be fetched from the relays
... might be hard to remove the asdfasdf note i sent already to some relays."
the landing page rendered three hardcoded bounty objects. the app could already write
kind:30050 events through /bounties/new but never read them back. it now subscribes to the
relays and renders live bounties, honouring nip-09 retractions. scope kept deliberately narrow:
data source only, no search or filter ui, bountycard untouched.

Related issue

Closes #37

Changes

  • src/lib/types/bounty.ts: bounty gains id, address (30050::),
    makerpubkey, createdat. address keys the list and survives revisions; the rest drive
    nip-09 matching.
  • src/lib/bounty.ts: read side added next to the existing write side. parsebountyevent,
    upsertbounty, recorddeletion, visiblebounties, all pure so the risky logic is unit
    testable.
  • src/routes/+page.svelte: onmount subscription, sveltemap plus $derived, and
    loading / empty / relay-timeout states.
  • src/lib/bounty.test.ts: 40 new cases, including a round trip of buildbountyevent through
    parsebountyevent.

reasoning

dedupe is ours to do. ndk's subscribe() dedupes only by event id, so two relays holding
different revisions of one addressable bounty deliver two events. we collapse by
kind:pubkey:d, newest wins.

@nostr-dev-kit/ndk-svelte was rejected despite being the obvious fit. its $subscribe
splices a newer revision into the list without removing the old one, so editing a bounty
duplicates it permanently, and its skipdeleted checks a non-standard ["deleted"] tag
than nip-09. ndk.subscribe(filter, { onevent }) is what ndk's own guardrails recommend for
feeds anyway.

the parser rejects rather than coerces. relay data is untrusted, and the satcode-dem
reference parser casts the s tag with as bountystatus, so an event carrying ["s","pwned"]
would reach bountycard's status lookup and blank the page. required tags are limited
the card needs, since buildbountyevent omits spec-required gov_key_*, refund_address and
check_in_days and the parser must not reject our own writes.

kind 30050 is contested namespace. verified against live relays before merging:
{ kinds: [30050], limit: 100 } returned 157 events of which 2 were bounties, the rest being
unrelated apps (encrypted key bundles, chat rooms, device handshakes). the parser dr
155 cleanly, but the limit was the real problem, since relays return the most recent kind:30050
events and genuine bounties get pushed out of the window. adding '#s': [...bounty_st
narrows to events carrying a bounty status. measured side by side on live relays: same 2
bounties, raw events down from 101 to 20.

nip-09 is honoured client side. a second filter in the same subscription picks up ki
events tagged k=30050, matched by address or event id and only when signed by the bounty's own
author. relay deletion support is advisory, so handling it in the client makes retra
reliable.

onmount rather than $effect: never runs during ssr (so ndk is only constructed in the
browser), never tracks reads, and its return value is the teardown. unreachable rela
send eose, so an 8s timeout releases the spinner.

--after merge--
Rebased onto origin/main. #35 (Single bounty page UI) landed after this branch was cut. Two files conflicted textually; three more merged clean and broke.

Known gap: the detail page is still mock-backed, so a card for a real relay bounty lands on "Bounty not found". That's where main already is, not a regression from this branch — worth a follow-up issue.

Two stale lines in the current description: "bountycard untouched", and "40 new cases" (the file runs 41 now).

Author checklist

bun run pr runs all the commands below in one go.

  • Matches the spec
  • bun run format - code has been formatted
  • bun run lint - no prettier/eslint errors
  • bun run check - no svelte-check/TypeScript errors
  • bun run test - all tests pass
  • bun run build - build succeeds
  • I tested the relevant changes manually

Reviewer manual testing checklist

  • log in with a nip-07 extension, publish a bounty at /bounties/new, confirm it appears
    on / without a reload
  • republish with the same d tag and a changed title, confirm the card updates in place
    rather than duplicating
  • load / offline, confirm the spinner resolves to the empty state within

@lambdakilo
lambdakilo force-pushed the 37/nostr-events-landing-page branch from cfaa59e to a84e50f Compare August 15, 2026 10:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Display nostr events on listing view

1 participant