mirror of
https://github.com/purrgrammer/grimoire.git
synced 2026-04-10 23:47:12 +02:00
refactor: Add human-friendly names and simplify Zapstore renderers
- kinds.ts: Add kind 32267 (App), update 30063 to "App Release", update 30267 to "App Collection" - Extract PlatformIcon to shared component (zapstore/PlatformIcon.tsx) - Update all renderer comments to use human-friendly terminology - Remove unnecessary comments throughout Zapstore renderers - Simplify code without changing functionality
This commit is contained in:
@@ -78,9 +78,8 @@ function PlatformItem({ platform }: { platform: Platform }) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Detail renderer for Kind 32267 - Zapstore App Metadata
|
||||
* Shows comprehensive app information including screenshots
|
||||
* Note: Zapstore helpers wrap getTagValue which caches internally
|
||||
* Detail renderer for Kind 32267 - App
|
||||
* Shows comprehensive app information including screenshots and platforms
|
||||
*/
|
||||
export function ZapstoreAppDetailRenderer({
|
||||
event,
|
||||
|
||||
@@ -8,71 +8,11 @@ import {
|
||||
getAppSummary,
|
||||
detectPlatforms,
|
||||
} from "@/lib/zapstore-helpers";
|
||||
import {
|
||||
Globe,
|
||||
Smartphone,
|
||||
TabletSmartphone,
|
||||
Monitor,
|
||||
Laptop,
|
||||
} from "lucide-react";
|
||||
import type { Platform } from "@/lib/zapstore-helpers";
|
||||
import { PlatformIcon } from "./zapstore/PlatformIcon";
|
||||
|
||||
/**
|
||||
* Platform icon component with label
|
||||
*/
|
||||
function PlatformIcon({ platform }: { platform: Platform }) {
|
||||
const iconClass = "size-4 text-muted-foreground";
|
||||
|
||||
const getPlatformLabel = () => {
|
||||
switch (platform) {
|
||||
case "android":
|
||||
return "Android";
|
||||
case "ios":
|
||||
return "iOS";
|
||||
case "web":
|
||||
return "Web";
|
||||
case "macos":
|
||||
return "macOS";
|
||||
case "windows":
|
||||
return "Windows";
|
||||
case "linux":
|
||||
return "Linux";
|
||||
default:
|
||||
return platform;
|
||||
}
|
||||
};
|
||||
|
||||
const getIcon = () => {
|
||||
switch (platform) {
|
||||
case "android":
|
||||
return <TabletSmartphone className={iconClass} />;
|
||||
case "ios":
|
||||
return <Smartphone className={iconClass} />;
|
||||
case "web":
|
||||
return <Globe className={iconClass} />;
|
||||
case "macos":
|
||||
return <Laptop className={iconClass} />;
|
||||
case "windows":
|
||||
case "linux":
|
||||
return <Monitor className={iconClass} />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-1.5">
|
||||
{getIcon()}
|
||||
<span className="text-xs text-muted-foreground">
|
||||
{getPlatformLabel()}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renderer for Kind 32267 - Zapstore App Metadata
|
||||
* Displays app name, icon, summary, and platform icons in feed
|
||||
* Renderer for Kind 32267 - App Metadata
|
||||
* Clean feed view with app name, summary, and platform icons
|
||||
*/
|
||||
export function ZapstoreAppRenderer({ event }: BaseEventProps) {
|
||||
const appName = getAppName(event);
|
||||
@@ -82,7 +22,6 @@ export function ZapstoreAppRenderer({ event }: BaseEventProps) {
|
||||
return (
|
||||
<BaseEventContainer event={event}>
|
||||
<div className="flex flex-col gap-2">
|
||||
{/* App Name */}
|
||||
<ClickableEventTitle
|
||||
event={event}
|
||||
className="text-base font-semibold text-foreground"
|
||||
@@ -90,14 +29,12 @@ export function ZapstoreAppRenderer({ event }: BaseEventProps) {
|
||||
{appName}
|
||||
</ClickableEventTitle>
|
||||
|
||||
{/* Summary */}
|
||||
{summary && (
|
||||
<p className="text-sm text-muted-foreground line-clamp-2">
|
||||
{summary}
|
||||
</p>
|
||||
)}
|
||||
|
||||
{/* Platform Icons */}
|
||||
{platforms.length > 0 && (
|
||||
<div className="flex items-center gap-2">
|
||||
{platforms.map((platform) => (
|
||||
|
||||
@@ -8,78 +8,18 @@ import {
|
||||
detectPlatforms,
|
||||
getCurationSetIdentifier,
|
||||
} from "@/lib/zapstore-helpers";
|
||||
import type { Platform } from "@/lib/zapstore-helpers";
|
||||
import { useNostrEvent } from "@/hooks/useNostrEvent";
|
||||
import { useGrimoire } from "@/core/state";
|
||||
import { UserName } from "../UserName";
|
||||
import {
|
||||
Package,
|
||||
Globe,
|
||||
Smartphone,
|
||||
TabletSmartphone,
|
||||
Monitor,
|
||||
Laptop,
|
||||
} from "lucide-react";
|
||||
import { Package } from "lucide-react";
|
||||
import { PlatformIcon } from "./zapstore/PlatformIcon";
|
||||
|
||||
interface ZapstoreAppSetDetailRendererProps {
|
||||
event: NostrEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Platform icon component with label
|
||||
*/
|
||||
function PlatformIcon({ platform }: { platform: Platform }) {
|
||||
const iconClass = "size-4 text-muted-foreground";
|
||||
|
||||
const getPlatformLabel = () => {
|
||||
switch (platform) {
|
||||
case "android":
|
||||
return "Android";
|
||||
case "ios":
|
||||
return "iOS";
|
||||
case "web":
|
||||
return "Web";
|
||||
case "macos":
|
||||
return "macOS";
|
||||
case "windows":
|
||||
return "Windows";
|
||||
case "linux":
|
||||
return "Linux";
|
||||
default:
|
||||
return platform;
|
||||
}
|
||||
};
|
||||
|
||||
const getIcon = () => {
|
||||
switch (platform) {
|
||||
case "android":
|
||||
return <TabletSmartphone className={iconClass} />;
|
||||
case "ios":
|
||||
return <Smartphone className={iconClass} />;
|
||||
case "web":
|
||||
return <Globe className={iconClass} />;
|
||||
case "macos":
|
||||
return <Laptop className={iconClass} />;
|
||||
case "windows":
|
||||
case "linux":
|
||||
return <Monitor className={iconClass} />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-1.5">
|
||||
{getIcon()}
|
||||
<span className="text-xs text-muted-foreground">
|
||||
{getPlatformLabel()}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Expanded app card showing full app details
|
||||
* App card showing app details with icon, summary, and platforms
|
||||
*/
|
||||
function AppCard({
|
||||
address,
|
||||
@@ -113,7 +53,6 @@ function AppCard({
|
||||
|
||||
return (
|
||||
<div className="p-4 bg-muted/20 rounded-lg border border-border flex gap-4 hover:bg-muted/30 transition-colors">
|
||||
{/* App Icon */}
|
||||
{iconUrl ? (
|
||||
<img
|
||||
src={iconUrl}
|
||||
@@ -127,9 +66,7 @@ function AppCard({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* App Info */}
|
||||
<div className="flex-1 flex flex-col gap-2 min-w-0">
|
||||
{/* App Name */}
|
||||
<button
|
||||
onClick={handleClick}
|
||||
className="text-lg font-semibold hover:underline cursor-crosshair text-left"
|
||||
@@ -137,14 +74,12 @@ function AppCard({
|
||||
{appName}
|
||||
</button>
|
||||
|
||||
{/* Summary */}
|
||||
{summary && (
|
||||
<p className="text-sm text-muted-foreground line-clamp-2">
|
||||
{summary}
|
||||
</p>
|
||||
)}
|
||||
|
||||
{/* Platform Icons */}
|
||||
{platforms.length > 0 && (
|
||||
<div className="flex items-center gap-2">
|
||||
{platforms.map((platform) => (
|
||||
@@ -158,8 +93,8 @@ function AppCard({
|
||||
}
|
||||
|
||||
/**
|
||||
* Detail renderer for Kind 30267 - Zapstore App Curation Set
|
||||
* Shows comprehensive view of all apps in the collection
|
||||
* Detail renderer for Kind 30267 - App Collection
|
||||
* Displays all apps in the collection with comprehensive metadata
|
||||
*/
|
||||
export function ZapstoreAppSetDetailRenderer({
|
||||
event,
|
||||
@@ -170,19 +105,15 @@ export function ZapstoreAppSetDetailRenderer({
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-6 p-6">
|
||||
{/* Header Section */}
|
||||
<div className="flex flex-col gap-3">
|
||||
<h1 className="text-3xl font-bold">{setName}</h1>
|
||||
|
||||
{/* Metadata */}
|
||||
<div className="grid grid-cols-2 gap-4 text-sm">
|
||||
{/* Curator */}
|
||||
<div className="flex flex-col gap-1">
|
||||
<h3 className="text-muted-foreground">Curated by</h3>
|
||||
<UserName pubkey={event.pubkey} />
|
||||
</div>
|
||||
|
||||
{/* Identifier */}
|
||||
{identifier && (
|
||||
<div className="flex flex-col gap-1">
|
||||
<h3 className="text-muted-foreground">Collection ID</h3>
|
||||
@@ -193,13 +124,11 @@ export function ZapstoreAppSetDetailRenderer({
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* App Count */}
|
||||
<p className="text-muted-foreground">
|
||||
{apps.length} {apps.length === 1 ? "app" : "apps"} in this collection
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Apps Section */}
|
||||
<div className="flex flex-col gap-3">
|
||||
<h2 className="text-xl font-semibold">Apps</h2>
|
||||
|
||||
|
||||
@@ -12,9 +12,6 @@ import { useNostrEvent } from "@/hooks/useNostrEvent";
|
||||
import { useGrimoire } from "@/core/state";
|
||||
import { Package } from "lucide-react";
|
||||
|
||||
/**
|
||||
* Individual app item - fetches and displays app info
|
||||
*/
|
||||
function AppItem({
|
||||
address,
|
||||
}: {
|
||||
@@ -44,8 +41,8 @@ function AppItem({
|
||||
}
|
||||
|
||||
/**
|
||||
* Renderer for Kind 30267 - Zapstore App Curation Set
|
||||
* Displays collection name and list of all apps with compact layout
|
||||
* Renderer for Kind 30267 - App Collection
|
||||
* Compact feed view listing all apps similar to relay lists
|
||||
*/
|
||||
export function ZapstoreAppSetRenderer({ event }: BaseEventProps) {
|
||||
const setName = getCurationSetName(event);
|
||||
@@ -54,7 +51,6 @@ export function ZapstoreAppSetRenderer({ event }: BaseEventProps) {
|
||||
return (
|
||||
<BaseEventContainer event={event}>
|
||||
<div className="flex flex-col gap-2">
|
||||
{/* Collection Name */}
|
||||
<ClickableEventTitle
|
||||
event={event}
|
||||
className="text-base font-semibold text-foreground"
|
||||
@@ -62,12 +58,10 @@ export function ZapstoreAppSetRenderer({ event }: BaseEventProps) {
|
||||
{setName}
|
||||
</ClickableEventTitle>
|
||||
|
||||
{/* App Count */}
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{apps.length} {apps.length === 1 ? "app" : "apps"}
|
||||
</p>
|
||||
|
||||
{/* App List - Show all apps with compact spacing like relay lists */}
|
||||
{apps.length > 0 && (
|
||||
<div className="flex flex-col gap-0.5">
|
||||
{apps.map((ref, idx) => (
|
||||
|
||||
@@ -23,8 +23,8 @@ interface ZapstoreReleaseDetailRendererProps {
|
||||
}
|
||||
|
||||
/**
|
||||
* Detail renderer for Kind 30063 - Zapstore Release
|
||||
* Shows comprehensive release information including file metadata
|
||||
* Detail renderer for Kind 30063 - App Release
|
||||
* Shows release information with embedded file metadata
|
||||
*/
|
||||
export function ZapstoreReleaseDetailRenderer({
|
||||
event,
|
||||
@@ -35,12 +35,10 @@ export function ZapstoreReleaseDetailRenderer({
|
||||
const fileEventId = getReleaseFileEventId(event);
|
||||
const appPointer = getReleaseAppPointer(event);
|
||||
|
||||
// Fetch related events
|
||||
const appEvent = useNostrEvent(appPointer || undefined);
|
||||
// Load file event with release event as context for better relay selection
|
||||
const fileEvent = useNostrEvent(
|
||||
fileEventId ? { id: fileEventId } : undefined,
|
||||
event, // Pass release event as context to use author's relays
|
||||
event,
|
||||
);
|
||||
|
||||
const appName = appEvent ? getAppName(appEvent) : appPointer?.identifier;
|
||||
@@ -54,9 +52,7 @@ export function ZapstoreReleaseDetailRenderer({
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-6 p-6 max-w-4xl mx-auto">
|
||||
{/* Header Section */}
|
||||
<div className="flex gap-4">
|
||||
{/* App Icon or Package Icon */}
|
||||
{appIcon ? (
|
||||
<img
|
||||
src={appIcon}
|
||||
@@ -70,7 +66,6 @@ export function ZapstoreReleaseDetailRenderer({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Release Title */}
|
||||
<div className="flex flex-col gap-2 flex-1 min-w-0">
|
||||
<div className="flex items-baseline gap-2 flex-wrap">
|
||||
<h1 className="text-3xl font-bold">{appName || "Release"}</h1>
|
||||
@@ -81,7 +76,6 @@ export function ZapstoreReleaseDetailRenderer({
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* App Link */}
|
||||
{appName && appPointer && (
|
||||
<button
|
||||
onClick={handleAppClick}
|
||||
@@ -94,15 +88,12 @@ export function ZapstoreReleaseDetailRenderer({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Metadata Grid */}
|
||||
<div className="grid grid-cols-2 gap-4 text-sm">
|
||||
{/* Publisher */}
|
||||
<div className="flex flex-col gap-1">
|
||||
<h3 className="text-muted-foreground">Publisher</h3>
|
||||
<UserName pubkey={event.pubkey} />
|
||||
</div>
|
||||
|
||||
{/* Release Identifier */}
|
||||
{identifier && (
|
||||
<div className="flex flex-col gap-1">
|
||||
<h3 className="text-muted-foreground">Release ID</h3>
|
||||
@@ -113,7 +104,6 @@ export function ZapstoreReleaseDetailRenderer({
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* File Metadata Section */}
|
||||
{fileEvent && (
|
||||
<div className="flex flex-col gap-3">
|
||||
<h2 className="text-xl font-semibold flex items-center gap-2">
|
||||
@@ -126,7 +116,6 @@ export function ZapstoreReleaseDetailRenderer({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Loading/Missing States */}
|
||||
{fileEventId && !fileEvent && (
|
||||
<div className="flex items-center gap-2 p-4 bg-muted/20 rounded-lg text-muted-foreground">
|
||||
<FileDown className="size-5" />
|
||||
|
||||
@@ -15,8 +15,8 @@ import { Badge } from "@/components/ui/badge";
|
||||
import { Package, FileDown } from "lucide-react";
|
||||
|
||||
/**
|
||||
* Renderer for Kind 30063 - Zapstore Release
|
||||
* Displays release version and links to app and file metadata
|
||||
* Renderer for Kind 30063 - App Release
|
||||
* Displays release version with links to app and download file
|
||||
*/
|
||||
export function ZapstoreReleaseRenderer({ event }: BaseEventProps) {
|
||||
const { addWindow } = useGrimoire();
|
||||
@@ -24,7 +24,6 @@ export function ZapstoreReleaseRenderer({ event }: BaseEventProps) {
|
||||
const fileEventId = getReleaseFileEventId(event);
|
||||
const appPointer = getReleaseAppPointer(event);
|
||||
|
||||
// Fetch app metadata to show app name
|
||||
const appEvent = useNostrEvent(appPointer || undefined);
|
||||
const appName = appEvent ? getAppName(appEvent) : appPointer?.identifier;
|
||||
|
||||
@@ -43,7 +42,6 @@ export function ZapstoreReleaseRenderer({ event }: BaseEventProps) {
|
||||
return (
|
||||
<BaseEventContainer event={event}>
|
||||
<div className="flex flex-col gap-2">
|
||||
{/* Title */}
|
||||
<ClickableEventTitle
|
||||
event={event}
|
||||
className="text-base font-semibold text-foreground"
|
||||
@@ -56,9 +54,7 @@ export function ZapstoreReleaseRenderer({ event }: BaseEventProps) {
|
||||
)}
|
||||
</ClickableEventTitle>
|
||||
|
||||
{/* Links */}
|
||||
<div className="flex items-center gap-3 flex-wrap text-sm">
|
||||
{/* App Link - show app name with icon */}
|
||||
{appName && (
|
||||
<button
|
||||
onClick={handleAppClick}
|
||||
@@ -69,7 +65,6 @@ export function ZapstoreReleaseRenderer({ event }: BaseEventProps) {
|
||||
</button>
|
||||
)}
|
||||
|
||||
{/* File Link */}
|
||||
{fileEventId && (
|
||||
<button
|
||||
onClick={handleFileClick}
|
||||
|
||||
71
src/components/nostr/kinds/zapstore/PlatformIcon.tsx
Normal file
71
src/components/nostr/kinds/zapstore/PlatformIcon.tsx
Normal file
@@ -0,0 +1,71 @@
|
||||
import type { Platform } from "@/lib/zapstore-helpers";
|
||||
import {
|
||||
Globe,
|
||||
Smartphone,
|
||||
TabletSmartphone,
|
||||
Monitor,
|
||||
Laptop,
|
||||
} from "lucide-react";
|
||||
|
||||
interface PlatformIconProps {
|
||||
platform: Platform;
|
||||
showLabel?: boolean;
|
||||
size?: "sm" | "md";
|
||||
}
|
||||
|
||||
export function PlatformIcon({
|
||||
platform,
|
||||
showLabel = true,
|
||||
size = "sm",
|
||||
}: PlatformIconProps) {
|
||||
const iconClass = size === "sm" ? "size-3" : "size-4";
|
||||
|
||||
const getPlatformLabel = () => {
|
||||
switch (platform) {
|
||||
case "android":
|
||||
return "Android";
|
||||
case "ios":
|
||||
return "iOS";
|
||||
case "web":
|
||||
return "Web";
|
||||
case "macos":
|
||||
return "macOS";
|
||||
case "windows":
|
||||
return "Windows";
|
||||
case "linux":
|
||||
return "Linux";
|
||||
default:
|
||||
return platform;
|
||||
}
|
||||
};
|
||||
|
||||
const getIcon = () => {
|
||||
const className = `${iconClass} text-muted-foreground`;
|
||||
switch (platform) {
|
||||
case "android":
|
||||
return <TabletSmartphone className={className} />;
|
||||
case "ios":
|
||||
return <Smartphone className={className} />;
|
||||
case "web":
|
||||
return <Globe className={className} />;
|
||||
case "macos":
|
||||
return <Laptop className={className} />;
|
||||
case "windows":
|
||||
case "linux":
|
||||
return <Monitor className={className} />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-1.5">
|
||||
{getIcon()}
|
||||
{showLabel && (
|
||||
<span className="text-xs text-muted-foreground">
|
||||
{getPlatformLabel()}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1172,8 +1172,8 @@ export const EVENT_KINDS: Record<number | string, EventKind> = {
|
||||
// },
|
||||
30063: {
|
||||
kind: 30063,
|
||||
name: "Release Artifact Set",
|
||||
description: "Release artifact sets",
|
||||
name: "App Release",
|
||||
description: "Application release with version and files",
|
||||
nip: "51",
|
||||
icon: Package,
|
||||
},
|
||||
@@ -1193,8 +1193,8 @@ export const EVENT_KINDS: Record<number | string, EventKind> = {
|
||||
},
|
||||
30267: {
|
||||
kind: 30267,
|
||||
name: "App Curation",
|
||||
description: "App curation sets",
|
||||
name: "App Collection",
|
||||
description: "Curated collection of applications",
|
||||
nip: "51",
|
||||
icon: BookHeart,
|
||||
},
|
||||
@@ -1345,13 +1345,13 @@ export const EVENT_KINDS: Record<number | string, EventKind> = {
|
||||
nip: "89",
|
||||
icon: Package,
|
||||
},
|
||||
// 32267: {
|
||||
// kind: 32267,
|
||||
// name: "Software App",
|
||||
// description: "Software Application",
|
||||
// nip: "",
|
||||
// icon: AppWindow,
|
||||
// },
|
||||
32267: {
|
||||
kind: 32267,
|
||||
name: "App",
|
||||
description: "Application metadata with platforms and screenshots",
|
||||
nip: "",
|
||||
icon: Package,
|
||||
},
|
||||
34235: {
|
||||
kind: 34235,
|
||||
name: "Video",
|
||||
|
||||
Reference in New Issue
Block a user