mirror of
https://github.com/purrgrammer/grimoire.git
synced 2026-04-11 16:07:15 +02:00
refactor: Simplify Zapstore app renderers with platform icons
Improve Zapstore app rendering for cleaner, more intuitive display: Changes: - Add detectPlatforms() helper to normalize architecture tags (e.g., "android-arm64-v8a" → "android") - Replace verbose platform badges with clean platform icons (Android, iOS, Web, macOS, Windows, Linux) - Remove screenshots from feed view (keep in detail view only) - Remove repository links and license badges from feed view - Update detail view to show "Available On" with icon+label platform items Feed view now shows: - App icon - App name - Summary (2 lines max) - Platform icons (just icons, no text) Detail view now shows: - App icon, name, summary - Publisher, Package ID, License, Repository (metadata grid) - Available On (platforms with icons and labels) - Screenshots gallery (unchanged) All tests pass (744 total), build succeeds.
This commit is contained in:
@@ -4,21 +4,79 @@ import {
|
||||
getAppSummary,
|
||||
getAppIcon,
|
||||
getAppImages,
|
||||
getAppPlatforms,
|
||||
detectPlatforms,
|
||||
getAppRepository,
|
||||
getAppLicense,
|
||||
getAppIdentifier,
|
||||
} from "@/lib/zapstore-helpers";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import type { Platform } from "@/lib/zapstore-helpers";
|
||||
import { UserName } from "../UserName";
|
||||
import { ExternalLink } from "@/components/ExternalLink";
|
||||
import { MediaEmbed } from "../MediaEmbed";
|
||||
import { Package } from "lucide-react";
|
||||
import {
|
||||
Package,
|
||||
Globe,
|
||||
Smartphone,
|
||||
TabletSmartphone,
|
||||
Monitor,
|
||||
Laptop,
|
||||
} from "lucide-react";
|
||||
|
||||
interface ZapstoreAppDetailRendererProps {
|
||||
event: NostrEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Platform icon and label component
|
||||
*/
|
||||
function PlatformItem({ platform }: { platform: Platform }) {
|
||||
const iconClass = "size-5";
|
||||
|
||||
const getPlatformName = () => {
|
||||
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-2 px-3 py-2 bg-muted/30 rounded-lg">
|
||||
{getIcon()}
|
||||
<span className="text-sm font-medium">{getPlatformName()}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detail renderer for Kind 32267 - Zapstore App Metadata
|
||||
* Shows comprehensive app information including screenshots
|
||||
@@ -31,7 +89,7 @@ export function ZapstoreAppDetailRenderer({
|
||||
const summary = getAppSummary(event);
|
||||
const iconUrl = getAppIcon(event);
|
||||
const images = getAppImages(event);
|
||||
const platforms = getAppPlatforms(event);
|
||||
const platforms = detectPlatforms(event);
|
||||
const repository = getAppRepository(event);
|
||||
const license = getAppLicense(event);
|
||||
const identifier = getAppIdentifier(event);
|
||||
@@ -103,18 +161,10 @@ export function ZapstoreAppDetailRenderer({
|
||||
{/* Platforms Section */}
|
||||
{platforms.length > 0 && (
|
||||
<div className="flex flex-col gap-3">
|
||||
<h2 className="text-xl font-semibold">
|
||||
Platforms ({platforms.length})
|
||||
</h2>
|
||||
<h2 className="text-xl font-semibold">Available On</h2>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{platforms.map((platform) => (
|
||||
<Badge
|
||||
key={platform}
|
||||
variant="secondary"
|
||||
className="text-sm px-3 py-1"
|
||||
>
|
||||
{platform}
|
||||
</Badge>
|
||||
<PlatformItem key={platform} platform={platform} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -7,25 +7,50 @@ import {
|
||||
getAppName,
|
||||
getAppSummary,
|
||||
getAppIcon,
|
||||
getAppPlatforms,
|
||||
getAppRepository,
|
||||
getAppLicense,
|
||||
detectPlatforms,
|
||||
} from "@/lib/zapstore-helpers";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { ExternalLink } from "@/components/ExternalLink";
|
||||
import { Package } from "lucide-react";
|
||||
import {
|
||||
Package,
|
||||
Globe,
|
||||
Smartphone,
|
||||
TabletSmartphone,
|
||||
Monitor,
|
||||
Laptop,
|
||||
} from "lucide-react";
|
||||
import type { Platform } from "@/lib/zapstore-helpers";
|
||||
|
||||
/**
|
||||
* Platform icon component
|
||||
*/
|
||||
function PlatformIcon({ platform }: { platform: Platform }) {
|
||||
const iconClass = "size-4 text-muted-foreground";
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renderer for Kind 32267 - Zapstore App Metadata
|
||||
* Displays app name, icon, summary, and platforms in feed
|
||||
* Displays app name, icon, summary, and platform icons in feed
|
||||
*/
|
||||
export function ZapstoreAppRenderer({ event }: BaseEventProps) {
|
||||
const appName = getAppName(event);
|
||||
const summary = getAppSummary(event);
|
||||
const iconUrl = getAppIcon(event);
|
||||
const platforms = getAppPlatforms(event);
|
||||
const repository = getAppRepository(event);
|
||||
const license = getAppLicense(event);
|
||||
const platforms = detectPlatforms(event);
|
||||
|
||||
return (
|
||||
<BaseEventContainer event={event}>
|
||||
@@ -61,41 +86,13 @@ export function ZapstoreAppRenderer({ event }: BaseEventProps) {
|
||||
</p>
|
||||
)}
|
||||
|
||||
{/* Platforms & License */}
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
{platforms.length > 0 && (
|
||||
<>
|
||||
{platforms.slice(0, 4).map((platform) => (
|
||||
<Badge
|
||||
key={platform}
|
||||
variant="secondary"
|
||||
className="text-[10px] px-2 py-0.5"
|
||||
>
|
||||
{platform}
|
||||
</Badge>
|
||||
))}
|
||||
{platforms.length > 4 && (
|
||||
<Badge variant="outline" className="text-[10px] px-2 py-0">
|
||||
+{platforms.length - 4} more
|
||||
</Badge>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{license && (
|
||||
<Badge variant="outline" className="text-[10px] px-2 py-0.5">
|
||||
{license}
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Repository Link */}
|
||||
{repository && (
|
||||
<ExternalLink
|
||||
href={repository}
|
||||
className="text-xs truncate max-w-full"
|
||||
>
|
||||
{repository}
|
||||
</ExternalLink>
|
||||
{/* Platform Icons */}
|
||||
{platforms.length > 0 && (
|
||||
<div className="flex items-center gap-2">
|
||||
{platforms.map((platform) => (
|
||||
<PlatformIcon key={platform} platform={platform} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -101,6 +101,53 @@ export function getAppPlatforms(event: NostrEvent): string[] {
|
||||
return getTagValues(event, "f");
|
||||
}
|
||||
|
||||
/**
|
||||
* Platform names for display
|
||||
*/
|
||||
export type Platform =
|
||||
| "android"
|
||||
| "ios"
|
||||
| "web"
|
||||
| "linux"
|
||||
| "windows"
|
||||
| "macos";
|
||||
|
||||
/**
|
||||
* Detect unique platforms from f tags
|
||||
* Normalizes architecture-specific tags (e.g., "android-arm64-v8a" → "android")
|
||||
*/
|
||||
export function detectPlatforms(event: NostrEvent): Platform[] {
|
||||
if (event.kind !== 32267 && event.kind !== 1063) return [];
|
||||
|
||||
const fTags = getTagValues(event, "f");
|
||||
const platformSet = new Set<Platform>();
|
||||
|
||||
for (const tag of fTags) {
|
||||
const lower = tag.toLowerCase();
|
||||
|
||||
if (lower.startsWith("android")) {
|
||||
platformSet.add("android");
|
||||
} else if (lower.startsWith("ios") || lower.includes("iphone")) {
|
||||
platformSet.add("ios");
|
||||
} else if (lower === "web" || lower.includes("web")) {
|
||||
platformSet.add("web");
|
||||
} else if (lower.includes("linux")) {
|
||||
platformSet.add("linux");
|
||||
} else if (lower.includes("windows") || lower.includes("win")) {
|
||||
platformSet.add("windows");
|
||||
} else if (
|
||||
lower.includes("macos") ||
|
||||
lower.includes("mac") ||
|
||||
lower.includes("darwin")
|
||||
) {
|
||||
platformSet.add("macos");
|
||||
}
|
||||
}
|
||||
|
||||
// Sort for consistent order
|
||||
return Array.from(platformSet).sort();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get release artifact references from kind 32267 a tags (usually kind 30063)
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user