From d930bf40714bab053977f6fd1e0a275e006dca28 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 15 Jan 2026 14:48:11 +0000 Subject: [PATCH] Add download button to Zapstore app renderers Add a download button for the latest release to both the feed and detail renderers for kind 32267 (Zapstore App Metadata). The feed renderer shows a compact version button, while the detail renderer shows a prominent download button in the header. Both fetch the latest release and link to its file metadata event (kind 1063) for download. --- .../nostr/kinds/ZapstoreAppDetailRenderer.tsx | 27 ++++++- .../nostr/kinds/ZapstoreAppRenderer.tsx | 78 +++++++++++++++++-- 2 files changed, 97 insertions(+), 8 deletions(-) diff --git a/src/components/nostr/kinds/ZapstoreAppDetailRenderer.tsx b/src/components/nostr/kinds/ZapstoreAppDetailRenderer.tsx index 2465308..9fddd1c 100644 --- a/src/components/nostr/kinds/ZapstoreAppDetailRenderer.tsx +++ b/src/components/nostr/kinds/ZapstoreAppDetailRenderer.tsx @@ -147,6 +147,7 @@ function PlatformItem({ platform }: { platform: Platform }) { export function ZapstoreAppDetailRenderer({ event, }: ZapstoreAppDetailRendererProps) { + const { addWindow } = useGrimoire(); const appName = getAppName(event); const summary = getAppSummary(event); const iconUrl = getAppIcon(event); @@ -186,6 +187,19 @@ export function ZapstoreAppDetailRenderer({ }); }, [releases]); + // Get the latest release for the header download button + const latestRelease = sortedReleases[0] || null; + const latestFileEventId = latestRelease + ? getReleaseFileEventId(latestRelease) + : null; + const latestVersion = latestRelease ? getReleaseVersion(latestRelease) : null; + + const handleDownloadLatest = () => { + if (latestFileEventId) { + addWindow("open", { pointer: { id: latestFileEventId } }); + } + }; + return (
{/* Header Section */} @@ -206,7 +220,18 @@ export function ZapstoreAppDetailRenderer({ {/* App Title & Summary */}
-

{appName}

+
+

{appName}

+ {latestFileEventId && ( + + )} +
{summary && (

{summary}

)} diff --git a/src/components/nostr/kinds/ZapstoreAppRenderer.tsx b/src/components/nostr/kinds/ZapstoreAppRenderer.tsx index 204bb7a..c49cfa2 100644 --- a/src/components/nostr/kinds/ZapstoreAppRenderer.tsx +++ b/src/components/nostr/kinds/ZapstoreAppRenderer.tsx @@ -6,28 +6,92 @@ import { import { getAppName, getAppSummary, + getAppIdentifier, detectPlatforms, + getReleaseVersion, + getReleaseFileEventId, } from "@/lib/zapstore-helpers"; import { PlatformIcon } from "./zapstore/PlatformIcon"; +import { use$ } from "applesauce-react/hooks"; +import eventStore from "@/services/event-store"; +import { useMemo } from "react"; +import { useGrimoire } from "@/core/state"; +import { FileDown } from "lucide-react"; /** * Renderer for Kind 32267 - App Metadata - * Clean feed view with app name, summary, and platform icons + * Clean feed view with app name, summary, platform icons, and download button */ export function ZapstoreAppRenderer({ event }: BaseEventProps) { + const { addWindow } = useGrimoire(); const appName = getAppName(event); const summary = getAppSummary(event); + const identifier = getAppIdentifier(event); const platforms = detectPlatforms(event); + // Query for releases that reference this app + const releasesFilter = useMemo(() => { + if (!identifier) { + return { kinds: [30063], ids: [] }; + } + return { + kinds: [30063], + "#a": [`32267:${event.pubkey}:${identifier}`], + }; + }, [event.pubkey, identifier]); + + const releases = use$( + () => eventStore.timeline(releasesFilter), + [releasesFilter], + ); + + // Get the latest release (by version or created_at) + const latestRelease = useMemo(() => { + if (!releases || releases.length === 0) return null; + return [...releases].sort((a, b) => { + const versionA = getReleaseVersion(a); + const versionB = getReleaseVersion(b); + if (versionA && versionB) { + return versionB.localeCompare(versionA, undefined, { numeric: true }); + } + return b.created_at - a.created_at; + })[0]; + }, [releases]); + + const latestFileEventId = latestRelease + ? getReleaseFileEventId(latestRelease) + : null; + const latestVersion = latestRelease ? getReleaseVersion(latestRelease) : null; + + const handleDownload = (e: React.MouseEvent) => { + e.stopPropagation(); + if (latestFileEventId) { + addWindow("open", { pointer: { id: latestFileEventId } }); + } + }; + return (
- - {appName} - +
+ + {appName} + + + {latestFileEventId && ( + + )} +
{summary && (