feat: display listings from nostr - #38
Conversation
cfaa59e to
a84e50f
Compare
|
i hope i did the subscription feature correctly in ndk instead of using ndk-svelte since i recall guildmaster saying something about that particular function |
There was a problem hiding this comment.
Pull request overview
This PR switches the landing page bounty listing from mock data to live Nostr kind:30050 events via NDK subscriptions, adding client-side parsing/deduplication and deletion handling so edited bounties collapse into a single card and retracted bounties are hidden.
Changes:
- Add a read/parse layer for kind:30050 bounty events, including dedupe-by-address and NIP-09 deletion tracking.
- Update the landing page to subscribe to relays on mount and render loading/empty states plus live bounty cards keyed by stable address.
- Extend the
Bountytype (address + optional check-in interval) and update mocks/UI + expand unit tests substantially.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src/lib/bounty.ts | Implements parsing, deduping, deletion tracking, and visible-bounty selection for relay events. |
| src/routes/+page.svelte | Replaces mock listing with an NDK subscription and adds loading/empty/timeout UI states. |
| src/lib/types/bounty.ts | Extends Bounty with stable address and makes checkInIntervalDays optional. |
| src/lib/components/BountyCard.svelte | Links bounty cards using the stable bounty address (URL-encoded). |
| src/lib/mock/bounties.ts | Updates mock bounties and lookup to use address-based routing. |
| src/routes/bounties/[id]/+page.svelte | Adjusts detail page rendering for optional check-in interval and clarifies mock-only behavior. |
| src/lib/bounty.test.ts | Adds broad unit coverage for parsing, dedupe, and deletion visibility logic. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| const { id, pubkey, created_at: createdAt } = event; | ||
| const title = event.tagValue('title')?.trim(); | ||
| const status = event.tagValue('s'); | ||
| const resolutionMode = event.tagValue('resolution_mode'); | ||
|
|
||
| if (!id || !pubkey || !createdAt || !event.dTag || !title) return null; | ||
| if (!isBountyStatus(status)) return null; |
There was a problem hiding this comment.
bounty created in 1970 is not in the spec. think before you talk you bad bot
| const { pubkey, created_at: at } = event; | ||
| if (!pubkey || !at) return; | ||
|
|
||
| const targets = [ | ||
| ...event.getMatchingTags('a'), | ||
| ...event.getMatchingTags('e') | ||
| ]; | ||
| for (const [, target] of targets) { | ||
| if (!target) continue; | ||
| const current = deletions.get(target); | ||
| if (!current || at > current.at) { | ||
| deletions.set(target, { at, by: pubkey }); | ||
| } |
There was a problem hiding this comment.
good catch finally. fixed in t300 commit
| // One subscription, one EOSE. `#s` keeps other apps' kind-30050 events out | ||
| // of the limit; the deletion filter uses the `k` tag NDKEvent.delete() | ||
| // writes, so it only matches retracted bounties. | ||
| const sub = ndk().subscribe( | ||
| [ | ||
| { kinds: [BOUNTY_KIND], '#s': [...BOUNTY_STATUSES], limit: 100 }, | ||
| { kinds: [NDKKind.EventDeletion], '#k': [String(BOUNTY_KIND)] } | ||
| ], |
There was a problem hiding this comment.
good note but this is intended design to avoid firehose effect
Summary
prompt for opus 4.8: 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 bounties. the app could already write kind:30050 events
at /bounties/new but never read them back. it now subscribes to the relays and renders live
bounties, dropping anything its author has retracted, which has to happen client side because
relay deletion support is advisory. scope is the data source only, no search or filter ui.
rebased onto origin/main, which brought in #35 (single bounty page ui). #35 changed both the card
and the bounty type, so this branch adopts those rather than keeping its own.
@nostr-dev-kit/ndk-svelte was rejected despite being the obvious fit: its subscription helper
leaves the old copy in place when an edit arrives, so editing a bounty duplicates it permanently,
and its deletion handling checks a non-standard tag rather than nip-09. worth knowing about ndk
proper too: its guardrails throw rather than warn, and their fetchevents check does not exempt
kind 30050, so enabling them outside production would start failing bounty queries.
the detail page is still mock-backed, so a card for a real relay bounty lands on "bounty not
found". that is where main already is rather than a regression from this branch, and it wants a
follow-up issue.
Related issue
Closes #37
Changes made
relay returns into one card, since subscribe() dedupes only by event id and an addressable event
gets a new id on every edit. the parser rejects rather than coerces, because one event with a
bogus status tag would otherwise blank the page, and it requires only the tags the ui cannot
render without, since our own publisher omits several the spec asks for. all pure functions, so
the parts most likely to be wrong are unit tested.
query is narrowed to events carrying a bounty status, because kind 30050 is a shared namespace
and unrelated apps' traffic would otherwise push real bounties out of the query window: same 2
bounties either way, raw events down from 101 to 20.
the author and timestamp retraction matching needs.
link and the rebase took it silently, so every edit would have broken that bounty's own link.
keeps working with the widened type.
Author checklist
bun run prruns all the commands below in one go.bun run format- code has been formattedbun run lint- no prettier/eslint errorsbun run check- no svelte-check/TypeScript errorsbun run test- all tests passbun run build- build succeedsReviewer manual testing checklist
without a reload