feat(relay): add wss://relay.zapstore.dev for zapstore app kinds

Include the Zapstore relay when loading kind 32267 (app metadata)
events, since they're primarily hosted on relay.zapstore.dev for now.

Changes:
- Wrap addressLoader in loaders.ts to inject zapstore relay for
  kind 32267 pointers — covers all code paths (zaps, generic viewers,
  any a-tag referencing an app)
- Add zapstore relay to ZapstoreAppRenderer/DetailRenderer relay lists
  (for fetching releases related to an app)
- Add zapstore relay to ZapstoreAppSetRenderer/DetailRenderer when
  resolving app references from app collections (kind 30267)

https://claude.ai/code/session_016CY1NFzK3AzhvB4e9Vc4Qq
This commit is contained in:
Claude
2026-02-12 21:33:30 +00:00
parent 7167d64d9c
commit 6021e82f78
5 changed files with 69 additions and 12 deletions

View File

@@ -169,12 +169,16 @@ export function ZapstoreAppDetailRenderer({
const identifier = getAppIdentifier(event);
// Build relay list for fetching releases:
// 1. Seen relays (where we received this app event)
// 2. Publisher's outbox relays (NIP-65)
// 3. Aggregator relays (fallback)
// 1. Zapstore relay (primary source for app events)
// 2. Seen relays (where we received this app event)
// 3. Publisher's outbox relays (NIP-65)
// 4. Aggregator relays (fallback)
const relays = useMemo(() => {
const relaySet = new Set<string>();
// Add zapstore relay (primary source for app metadata)
relaySet.add("wss://relay.zapstore.dev/");
// Add seen relays from the app event
const seenRelays = getSeenRelays(event);
if (seenRelays) {

View File

@@ -32,12 +32,16 @@ export function ZapstoreAppRenderer({ event }: BaseEventProps) {
const platforms = detectPlatforms(event);
// Build relay list for fetching releases:
// 1. Seen relays (where we received this app event)
// 2. Publisher's outbox relays (NIP-65)
// 3. Aggregator relays (fallback)
// 1. Zapstore relay (primary source for app events)
// 2. Seen relays (where we received this app event)
// 3. Publisher's outbox relays (NIP-65)
// 4. Aggregator relays (fallback)
const relays = useMemo(() => {
const relaySet = new Set<string>();
// Add zapstore relay (primary source for app metadata)
relaySet.add("wss://relay.zapstore.dev/");
// Add seen relays from the app event
const seenRelays = getSeenRelays(event);
if (seenRelays) {

View File

@@ -10,8 +10,11 @@ import {
} from "@/lib/zapstore-helpers";
import { useNostrEvent } from "@/hooks/useNostrEvent";
import { useGrimoire } from "@/core/state";
import { useMemo } from "react";
import { UserName } from "../UserName";
import { Package } from "lucide-react";
const ZAPSTORE_RELAY = "wss://relay.zapstore.dev/";
import { PlatformIcon } from "./zapstore/PlatformIcon";
interface ZapstoreAppSetDetailRendererProps {
@@ -23,11 +26,20 @@ interface ZapstoreAppSetDetailRendererProps {
*/
function AppCard({
address,
relayHint,
}: {
address: { kind: number; pubkey: string; identifier: string };
relayHint?: string;
}) {
const { addWindow } = useGrimoire();
const appEvent = useNostrEvent(address);
const pointer = useMemo(
() => ({
...address,
relays: [ZAPSTORE_RELAY, ...(relayHint ? [relayHint] : [])],
}),
[address, relayHint],
);
const appEvent = useNostrEvent(pointer);
if (!appEvent) {
return (
@@ -139,7 +151,11 @@ export function ZapstoreAppSetDetailRenderer({
) : (
<div className="flex flex-col gap-3">
{apps.map((ref, idx) => (
<AppCard key={idx} address={ref.address} />
<AppCard
key={idx}
address={ref.address}
relayHint={ref.relayHint}
/>
))}
</div>
)}

View File

@@ -10,15 +10,27 @@ import {
} from "@/lib/zapstore-helpers";
import { useNostrEvent } from "@/hooks/useNostrEvent";
import { useGrimoire } from "@/core/state";
import { useMemo } from "react";
import { Package } from "lucide-react";
const ZAPSTORE_RELAY = "wss://relay.zapstore.dev/";
function AppItem({
address,
relayHint,
}: {
address: { kind: number; pubkey: string; identifier: string };
relayHint?: string;
}) {
const { addWindow } = useGrimoire();
const appEvent = useNostrEvent(address);
const pointer = useMemo(
() => ({
...address,
relays: [ZAPSTORE_RELAY, ...(relayHint ? [relayHint] : [])],
}),
[address, relayHint],
);
const appEvent = useNostrEvent(pointer);
const appName = appEvent
? getAppName(appEvent)
: address?.identifier || "Unknown App";
@@ -65,7 +77,11 @@ export function ZapstoreAppSetRenderer({ event }: BaseEventProps) {
{apps.length > 0 && (
<div className="flex flex-col gap-0.5">
{apps.map((ref, idx) => (
<AppItem key={idx} address={ref.address} />
<AppItem
key={idx}
address={ref.address}
relayHint={ref.relayHint}
/>
))}
</div>
)}

View File

@@ -4,7 +4,7 @@ import {
createTimelineLoader,
createEventLoaderForStore,
} from "applesauce-loaders/loaders";
import type { EventPointer } from "nostr-tools/nip19";
import type { EventPointer, AddressPointer } from "nostr-tools/nip19";
import { Observable } from "rxjs";
import {
getSeenRelays,
@@ -198,12 +198,29 @@ export function eventLoader(
return baseEventLoader(enhancedPointer);
}
// Kind-specific relay overrides for addressable events
// TODO: Remove once these events are widely replicated
const KIND_ADDRESS_RELAYS: Record<number, string[]> = {
32267: ["wss://relay.zapstore.dev/"], // Zapstore app metadata
};
// Address loader for replaceable events (profiles, relay lists, etc.)
export const addressLoader = createAddressLoader(pool, {
const baseAddressLoader = createAddressLoader(pool, {
eventStore,
extraRelays: AGGREGATOR_RELAYS,
});
export function addressLoader(pointer: AddressPointer): Observable<NostrEvent> {
const kindRelays = KIND_ADDRESS_RELAYS[pointer.kind];
if (kindRelays) {
return baseAddressLoader({
...pointer,
relays: [...kindRelays, ...(pointer.relays || [])],
});
}
return baseAddressLoader(pointer);
}
// Profile loader with batching - combines multiple profile requests within 200ms
export const profileLoader = createAddressLoader(pool, {
eventStore,