Lab/Marketplace/Stash audit: useLabProfile picks newest revision (1.2.38)

Audited all three remaining surfaces. Two of them needed no changes:

MARKETPLACE / GEARSWAP — sound. useMarkListingSold preserves every tag
and flips only `status` (author-gated, right comment explaining why it
doesn't rebuild). Queries validate + dedupe, exclude showcase-only Stash
items from the sellable market, and GearSwap filters out expired/sold
listings and separates WTB from WTS. No bugs.

STASH — sound, and notably careful. The private list is one NIP-44
encrypted-to-self blob; all three mutations (add/update/remove) use
fetchLatestStash() (fetch-before-write) which falls back to non-empty
cache and THROWS rather than risk clobbering the user's gear, and is
Amber-gated (staleTime: Infinity). The public/private showcase
reconciliation in StashPage is complete: going public publishes the
kind-30402 showcase event, and going private / deleting / converting to
a wishlist item all call unpublishItem — so a public event can't linger
after an item is made private (no privacy leak). No bugs.

THE LAB — the collective join-request clobber was already fixed in
1.2.37. One robustness gap remained: useLabProfile fetched the profile
with limit:1 and trusted whatever came back. Relays can hold multiple
revisions of an addressable event, so a stale revision could win —
showing an old bio or, worse, a roster missing recently-approved
members. Now fetches a few (limit:10) and reduces to the newest
created_at, matching the pickMostRecent discipline already in useCrew.
On detail pages an author filter is always set, so newest-of-one-author
is exactly right.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
This commit is contained in:
shakespeare.diy 2026-09-08 00:09:00 +07:00
parent b6b3931ecc
commit 56e2b17990
2 changed files with 11 additions and 3 deletions

View file

@ -1,7 +1,7 @@
{
"name": "skate-spots",
"private": true,
"version": "1.2.37",
"version": "1.2.38",
"type": "module",
"homepage": "https://skate-spots.com",
"scripts": {

View file

@ -149,7 +149,10 @@ export function useLabProfile(identifier: string, pubkey?: string) {
} = {
kinds: LAB_KINDS,
'#d': [identifier],
limit: 1,
// Relays can hold multiple revisions of an addressable event; fetch a
// few and pick the newest rather than trusting whatever `limit: 1`
// returns (which may be a stale revision, dropping members/edits).
limit: 10,
};
if (pubkey) filter.authors = [pubkey];
@ -158,7 +161,12 @@ export function useLabProfile(identifier: string, pubkey?: string) {
if (events.length === 0) return null;
return parseLabProfile(events[0]);
// Newest revision wins.
const newest = events.reduce((latest, current) =>
current.created_at > latest.created_at ? current : latest,
);
return parseLabProfile(newest);
},
enabled: !!identifier,
staleTime: 30000,