38 lines
3.4 KiB
TypeScript
38 lines
3.4 KiB
TypeScript
import { ArrowUpRight, BookOpen, Shield, Sparkles, Swords } from 'lucide-react';
|
|
import { rarity, stats, type Article } from '../shared/protocol.ts';
|
|
import type { TestnetBlock } from '../shared/testnet.ts';
|
|
import { STARTER_ARTICLES } from '../shared/catalog.ts';
|
|
import { useEnrichment } from './useEnrichment.ts';
|
|
import { CardInsights, EnrichmentScores, MintStatsScores } from './CardInsights.tsx';
|
|
|
|
// hash is the RandomX proof hash for game stats; block.tokenId is NFT identity.
|
|
export type CardData = { article: Article; hash: string; block?: TestnetBlock };
|
|
export const samples: CardData[] = STARTER_ARTICLES.map((article, i) => ({ article,
|
|
hash: ['000eca7498a26d1908613726da6e869d7c9c936f4a461cb439ef24672b9a0008',
|
|
'000b1a387a067a7d698677965ca1ca7b0eb5e590811431bdb9aad62268180001',
|
|
'0008a4fa5893e9ddbd679cbcc7f0938706aa70aab8c8af42a4bc67a42520001',
|
|
'0009a4fa5893e9ddbd679cbcc7f0938706aa70aab8c8af42a4bc67a42520020'][i] }));
|
|
export function imageCredit(article: Article): string {
|
|
if (!article.image) return article.url;
|
|
const parts = new URL(article.image).pathname.split('/');
|
|
return `https://commons.wikimedia.org/wiki/File:${parts.includes('thumb') ? parts.at(-2)! : parts.at(-1)!}`;
|
|
}
|
|
export function Card({ card, onClick }: { card: CardData; onClick?: () => void }) {
|
|
const { article, hash } = card;
|
|
const tier = rarity(hash);
|
|
const s = stats(hash);
|
|
const {data:enrichment,error}=useEnrichment(article.pageId);
|
|
return <><button className={`wiki-card ${tier.toLowerCase()}`} onClick={onClick} tabIndex={onClick ? 0 : -1} aria-label={`View ${article.title} ${tier} card`}>
|
|
<div className="card-top"><span><Sparkles size={11} /> {tier}</span><span>W / {String(article.pageId).slice(-4)}</span></div>
|
|
<div className={`card-art art-${article.category.toLowerCase()}`}>
|
|
{article.image ? <img src={article.image} alt={article.title} loading="lazy" referrerPolicy="no-referrer" onError={e => { e.currentTarget.style.display = 'none'; }} /> : <img className="card-placeholder-logo" src="/original-logo.svg" alt="Wikideck" />}
|
|
<span className="card-category">{article.category}</span>
|
|
</div>
|
|
<div className="card-content"><div className="card-title"><h3>{article.title}</h3><ArrowUpRight size={16} /></div><p>{article.extract}</p>
|
|
<div className="card-stats" aria-label="Immutable proof-derived game stats"><span title="Power · proof-derived"><Swords size={12} />{s.power}</span><span title="Defense · proof-derived"><Shield size={12} />{s.defense}</span><span title="Knowledge · proof-derived"><BookOpen size={12} />{s.knowledge}</span></div>
|
|
<div className="card-enrichment">{enrichment?<><div className="card-readership"><span>CURRENT WIKIPEDIA</span><span>{enrichment.pageviews.status==='available'?`${new Intl.NumberFormat('en',{notation:'compact',maximumFractionDigits:1}).format(enrichment.pageviews.months.at(-1)!.views)} views / month`:'Views unavailable'}</span></div><EnrichmentScores data={enrichment}/></>:<span className="card-enrichment-status">{error?'Current metadata unavailable':'Loading article insights…'}</span>}{card.block&&<MintStatsScores snapshot={card.block.statsSnapshot} compactView/>}</div>
|
|
</div>
|
|
<div className="card-bottom"><span>WIKIPEDIA · #{article.pageId}</span><span>{card.block ? `BLOCK ${card.block.height}` : 'PREVIEW'}</span></div>
|
|
</button>{!onClick&&<CardInsights pageId={article.pageId} snapshot={card.block?.statsSnapshot}/>}</>;
|
|
}
|