Skip to content

Commit 4b884b8

Browse files
sktbrdclaude
andauthored
feat(newhome): Gnars TV set + new homepage experiment at /newhome (#239)
* feat(newhome): Gnars TV set + new homepage experiment at /newhome Adds a hidden /newhome route with the redesigned homepage, fronted by a photorealistic CSS CRT that plays real droposal video from IPFS. Gnars TV (src/components/tv/GnarsTVSet.tsx) - Multi-shell chassis: brushed-metal grain, light sweep, ambient occlusion, seam groove and phillips screws; neon GNARS TV badge; CRT tube with scanlines, convex glass, vignette and a static burst on tune. - Channels come from the droposal service, newest first, with a hardcoded guide as the offline fallback. MINT collects the tuned edition in place via useMintDroposal (edition address recovered from the execution receipt); SHOT captures the live frame to PNG; SAVE pulls the raw IPFS media; SHARE deep-links ?ch=<prop>. - One IPFS stream at a time: only the tuned video preloads and plays, the rest pause and drop back to preload="none". - Sculpted keycaps with a dished top face and 5px CSS :active travel. - Below md the guide is hidden and tuning moves to arrows on the glass; the frame's padding collapses so the picture keeps the width. Homepage sections: stake roster, poidh bounty trading cards (skate + surf only, two-stage click with a flip to the brief), NogglesRails globe with a linked detail panel, swap, and a bespoke governance panel. Fixes found along the way - api/tv/feed dropped a droposal's bannerImage whenever it had an animationUrl, so every video droposal reached the UI with no poster. - DaoHeader swapped to the desktop nav at md, overflowing the viewport between 768px and ~880px on every page. - REWARD_SPLIT moved out of a "use client" module: read from a server component it arrived as a client-reference proxy and rendered "undefined%". - poidh keywords grouped by discipline with word-boundary matching, so the homepage can show skate and surf without parkour and 420 bounties. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(newhome): don't let a transient subgraph error fail the build listProposals and getAllFeedEvents ran unguarded inside Suspense boundaries. During static generation an unhandled throw there fails the whole export — one Goldsky 5xx would take the production deploy down for two panels that read fine with no rows. Caught in a local prod build, where /pt-br/newhome failed on a proposals(first: 8) error that did not reproduce on the next run. Both now degrade to empty and self-heal on the next 5-minute revalidate. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent db090bc commit 4b884b8

33 files changed

Lines changed: 4153 additions & 128 deletions

messages/en/newhome.json

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
{
2+
"hero": {
3+
"onAir": "On air · Gnars TV",
4+
"title": "Open action sports brand.",
5+
"leadPrefix": "Gnars is a headless brand run by the people who skate it.",
6+
"ctaStake": "Back a rider",
7+
"ctaBounties": "See open bounties",
8+
"stats": {
9+
"totalGnars": "Total Gnars",
10+
"members": "Members",
11+
"treasury": "Treasury",
12+
"rails": "NogglesRails"
13+
}
14+
},
15+
"ticker": {
16+
"treasury": "TREASURY",
17+
"auction": "AUCTION",
18+
"bounties": "OPEN BOUNTIES",
19+
"rails": "RAILS",
20+
"members": "MEMBERS",
21+
"railsValue": "{count} built",
22+
"countries": "{count} countries",
23+
"cc0": "CC0 FOREVER"
24+
},
25+
"tv": {
26+
"channelGuide": "Channel Guide",
27+
"all": "ALL →",
28+
"tune": "TUNE",
29+
"fullscreen": "Fullscreen",
30+
"exitFullscreen": "Exit",
31+
"prevChannel": "Previous channel",
32+
"nextChannel": "Next channel",
33+
"playPause": "Play or pause channel",
34+
"tuning": "TUNING…",
35+
"channel": "CH {number}",
36+
"loaded": "LOADED",
37+
"channels": "{count} channels",
38+
"railsCount": "{count} rails",
39+
"empty": "No channels on air right now.",
40+
"bountiesOpen": "{count} skate & surf bounties",
41+
"mute": "Mute",
42+
"unmute": "Unmute"
43+
},
44+
"stake": {
45+
"eyebrow": "Stake or Die",
46+
"title": "Pick your rider. Your deposit stays yours.",
47+
"body": "Deposit into a rider's sponsorship vault. Only the yield is shared — you keep 50%, the rider gets 25%, the Gnars treasury gets 25%. Withdraw the principal whenever you want.",
48+
"split": {
49+
"you": "You keep",
50+
"rider": "The rider",
51+
"treasury": "Treasury"
52+
},
53+
"whyEyebrow": "Why staking exists",
54+
"whyBody": "Sponsorships used to mean one brand writing one check. Now anyone can co-sponsor a rider — deposit, keep your principal, and let the yield do the giving."
55+
},
56+
"bounties": {
57+
"eyebrow": "Bounties",
58+
"title": "Challenges",
59+
"body": "Gnars Bounties are challenges from the community: pay athletes in ETH to land the tricks, runs, and stunts the community wants to see filmed.",
60+
"stats": {
61+
"pool": "Open pool",
62+
"open": "Open bounties",
63+
"usd": "USD value"
64+
},
65+
"viewAll": "View all bounties →",
66+
"flowPrefix": "The flow:",
67+
"flow": "create or select a bounty → fund it → execute and submit work → get verified → paid onchain",
68+
"create": "Create Bounty",
69+
"viewPoidh": "View on Poidh",
70+
"card": {
71+
"class": "CLASS",
72+
"status": "STATUS",
73+
"posted": "POSTED",
74+
"chain": "CHAIN",
75+
"usd": "USD",
76+
"type": "Poidh Bounty"
77+
},
78+
"rarity": {
79+
"legendary": "Legendary",
80+
"epic": "Epic",
81+
"rare": "Rare",
82+
"uncommon": "Uncommon",
83+
"common": "Common"
84+
},
85+
"empty": "No open bounties right now.",
86+
"whyEyebrow": "Why bounties exist",
87+
"whyBody": "No committee decides what's worth filming. Anyone can post a challenge and fund it — the same open pattern that put a rail in 9 countries.",
88+
"discipline": {
89+
"skate": "Skate",
90+
"surf": "Surf",
91+
"parkour": "Parkour",
92+
"weed": "420"
93+
},
94+
"back": {
95+
"reward": "Reward",
96+
"issuer": "Issuer",
97+
"deadline": "Deadline",
98+
"claims": "Claims",
99+
"noDeadline": "Open-ended",
100+
"noDescription": "No brief written for this bounty.",
101+
"view": "View bounty →",
102+
"flipBack": "Back",
103+
"flipHint": "Tap to flip",
104+
"flipTo": "Flip {title} to read the brief"
105+
}
106+
},
107+
"rails": {
108+
"eyebrow": "Community · NogglesRails",
109+
"title": "{rails} rails. {countries} countries. One big crew",
110+
"body": "A global network of community skate infrastructure — funded by proposals, installed by locals, free forever. Spin the globe or pick a spot to see the proposal that built it.",
111+
"hint": "Drag to spin · click a spot",
112+
"stats": {
113+
"installations": "Installations",
114+
"countries": "Countries",
115+
"continents": "Continents"
116+
},
117+
"readProposal": "Read the proposal →",
118+
"whyEyebrow": "Why the map matters",
119+
"whyBody": "Every pin is a proposal that passed and a crew that showed up. CC0 means anyone can build the next one — no license, no permission needed."
120+
},
121+
"swap": {
122+
"eyebrow": "Money · Swap",
123+
"title": "Need some ETH?",
124+
"body": "With the Support Gnars treasury box ticked, 0.5% of every trade routes straight to the Gnars Collective Treasury — already behind 15 skatable sculptures around the world, ambassador sponsorships, and infrastructure for a network of shredders from across the planet.",
125+
"loop": {
126+
"swap": "of every swap to the treasury",
127+
"yield": "of stake yield to the treasury",
128+
"auction": "of auction proceeds to proposals"
129+
},
130+
"whyEyebrow": "Why it all loops back",
131+
"whyBody": "Auctions, swaps and yield all feed one treasury. Every rider votes on where it goes next — that's the whole DAO, in one card."
132+
},
133+
"gov": {
134+
"eyebrow": "Governance",
135+
"title": "Join the crew",
136+
"recentProposals": "Recent proposals",
137+
"liveFeed": "Live feed",
138+
"viewAll": "View all →",
139+
"liveAuction": "Live auction",
140+
"currentBid": "Current bid",
141+
"leaderboard": "Leaderboard · {count} bids",
142+
"auctionFootnote": "New Gnars are auctioned continuously onchain. Anyone can bid; the winner mints the Gnar and funds flow to the DAO treasury to back proposals.",
143+
"for": "For {count}",
144+
"against": "Against {count}",
145+
"abstain": "Abstain {count}",
146+
"timeLeftDh": "{days}d {hours}h left",
147+
"timeLeftH": "{hours}h left",
148+
"timeAgoD": "{days}d ago",
149+
"timeAgoH": "{hours}h ago",
150+
"feedCreated": "created",
151+
"feedVoted": "voted",
152+
"feedOn": "on",
153+
"feedEmpty": "Nothing onchain in the last 30 days.",
154+
"awaitingSettlement": "Awaiting settlement"
155+
}
156+
}

messages/en/tv.json

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,5 +174,48 @@
174174
"ethTotal": "{amount} ETH total",
175175
"contentCoin": "Content Coin",
176176
"gnarsPaired": "Gnars Paired"
177+
},
178+
"set": {
179+
"onAirLabel": "ON AIR · GNARS TV",
180+
"onAir": "ON AIR",
181+
"power": "POWER",
182+
"channelGuide": "CHANNEL GUIDE",
183+
"all": "ALL",
184+
"ch": "CH",
185+
"tuneTo": "Tune to channel {no}: {title}",
186+
"actions": "ACTIONS",
187+
"mint": "MINT",
188+
"shot": "SHOT",
189+
"save": "SAVE",
190+
"share": "SHARE",
191+
"mintAria": "Mint droposal #{prop}",
192+
"shotAria": "Screenshot the current frame",
193+
"saveAria": "Download this video",
194+
"shareAria": "Share this channel",
195+
"playPause": "PLAY / PAUSE",
196+
"play": "PLAY",
197+
"pause": "PAUSE",
198+
"mute": "MUTE",
199+
"unmute": "UNMUTE",
200+
"fullscreen": "FULLSCREEN",
201+
"exitFullscreen": "EXIT FULLSCREEN",
202+
"broadcastSystem": "DIGITAL BROADCAST SYSTEM",
203+
"modelYear": "2001 MODEL",
204+
"keyboardHint": "← → CHANNEL · SPACE PLAY · M MUTE · F FULLSCREEN",
205+
"flashMint": "mint #{prop}",
206+
"flashFrameSaved": "frame saved",
207+
"flashShotBlocked": "shot blocked",
208+
"flashDownloading": "downloading",
209+
"flashShared": "shared",
210+
"flashLinkCopied": "link copied",
211+
"flashCopyBlocked": "copy blocked",
212+
"shareTitle": "Gnars TV — {title}",
213+
"shareText": "Watching {title} (#{prop}) on Gnars TV",
214+
"minting": "",
215+
"flashMinting": "minting #{prop}",
216+
"flashMinted": "minted",
217+
"flashMintFailed": "mint failed",
218+
"prevChannel": "Previous channel",
219+
"nextChannel": "Next channel"
177220
}
178221
}

messages/pt-br/newhome.json

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
{
2+
"hero": {
3+
"onAir": "No ar · Gnars TV",
4+
"title": "Esportes de ação, dos próprios riders.",
5+
"leadPrefix": "Gnars é uma marca headless tocada por quem anda de skate.",
6+
"ctaStake": "Apoie um rider",
7+
"ctaBounties": "Ver bounties abertas",
8+
"stats": {
9+
"totalGnars": "Total de Gnars",
10+
"members": "Membros",
11+
"treasury": "Tesouro",
12+
"rails": "NogglesRails"
13+
}
14+
},
15+
"ticker": {
16+
"treasury": "TESOURO",
17+
"auction": "LEILÃO",
18+
"bounties": "BOUNTIES ABERTAS",
19+
"rails": "RAILS",
20+
"members": "MEMBROS",
21+
"railsValue": "{count} construídos",
22+
"countries": "{count} países",
23+
"cc0": "CC0 PARA SEMPRE"
24+
},
25+
"tv": {
26+
"channelGuide": "Guia de canais",
27+
"all": "TUDO →",
28+
"tune": "SINTONIZAR",
29+
"fullscreen": "Tela cheia",
30+
"exitFullscreen": "Sair",
31+
"prevChannel": "Canal anterior",
32+
"nextChannel": "Próximo canal",
33+
"playPause": "Reproduzir ou pausar canal",
34+
"tuning": "SINTONIZANDO…",
35+
"channel": "CH {number}",
36+
"loaded": "CARREGADO",
37+
"channels": "{count} canais",
38+
"railsCount": "{count} rails",
39+
"empty": "Nenhum canal no ar no momento.",
40+
"bountiesOpen": "{count} bounties de skate e surf",
41+
"mute": "Silenciar",
42+
"unmute": "Ativar som"
43+
},
44+
"stake": {
45+
"eyebrow": "Stake or Die",
46+
"title": "Escolha seu rider. Seu depósito continua seu.",
47+
"body": "Deposite no cofre de patrocínio de um rider. Só o rendimento é compartilhado — você fica com 50%, o rider recebe 25%, o tesouro Gnars recebe 25%. Saque o principal quando quiser.",
48+
"split": {
49+
"you": "Você fica",
50+
"rider": "O rider",
51+
"treasury": "Tesouro"
52+
},
53+
"whyEyebrow": "Por que o stake existe",
54+
"whyBody": "Patrocínio antes era uma marca assinando um cheque. Agora qualquer pessoa pode copatrocinar um rider — deposite, mantenha seu principal, e deixe o rendimento fazer a doação."
55+
},
56+
"bounties": {
57+
"eyebrow": "Bounties",
58+
"title": "Desafios",
59+
"body": "As Gnars Bounties são desafios da comunidade: pague atletas em ETH para mandar as manobras, linhas e stunts que a comunidade quer ver filmados.",
60+
"stats": {
61+
"pool": "Pool aberto",
62+
"open": "Bounties abertas",
63+
"usd": "Valor em USD"
64+
},
65+
"viewAll": "Ver todas as bounties →",
66+
"flowPrefix": "O fluxo:",
67+
"flow": "crie ou escolha uma bounty → financie → execute e envie o trabalho → seja verificado → receba onchain",
68+
"create": "Criar bounty",
69+
"viewPoidh": "Ver no Poidh",
70+
"card": {
71+
"class": "CLASSE",
72+
"status": "STATUS",
73+
"posted": "POSTADA",
74+
"chain": "REDE",
75+
"usd": "USD",
76+
"type": "Bounty Poidh"
77+
},
78+
"rarity": {
79+
"legendary": "Lendária",
80+
"epic": "Épica",
81+
"rare": "Rara",
82+
"uncommon": "Incomum",
83+
"common": "Comum"
84+
},
85+
"empty": "Nenhuma bounty aberta no momento.",
86+
"whyEyebrow": "Por que as bounties existem",
87+
"whyBody": "Nenhum comitê decide o que vale filmar. Qualquer pessoa pode postar um desafio e financiá-lo — o mesmo padrão aberto que colocou um rail em 9 países.",
88+
"discipline": {
89+
"skate": "Skate",
90+
"surf": "Surf",
91+
"parkour": "Parkour",
92+
"weed": "420"
93+
},
94+
"back": {
95+
"reward": "Recompensa",
96+
"issuer": "Criador",
97+
"deadline": "Prazo",
98+
"claims": "Entregas",
99+
"noDeadline": "Sem prazo",
100+
"noDescription": "Sem descrição para esta bounty.",
101+
"view": "Ver bounty →",
102+
"flipBack": "Voltar",
103+
"flipHint": "Toque pra virar",
104+
"flipTo": "Virar {title} para ler o briefing"
105+
}
106+
},
107+
"rails": {
108+
"eyebrow": "Comunidade · NogglesRails",
109+
"title": "{rails} rails. {countries} países. Uma galera só",
110+
"body": "Uma rede global de infraestrutura de skate comunitária — financiada por propostas, instalada pelos locais, livre para sempre. Gire o globo ou escolha um ponto para ver a proposta que o construiu.",
111+
"hint": "Arraste para girar · clique num ponto",
112+
"stats": {
113+
"installations": "Instalações",
114+
"countries": "Países",
115+
"continents": "Continentes"
116+
},
117+
"readProposal": "Ler a proposta →",
118+
"whyEyebrow": "Por que o mapa importa",
119+
"whyBody": "Cada pin é uma proposta que passou e uma galera que apareceu. CC0 significa que qualquer pessoa pode construir a próxima — sem licença, sem pedir permissão."
120+
},
121+
"swap": {
122+
"eyebrow": "Dinheiro · Swap",
123+
"title": "Precisa de ETH?",
124+
"body": "Com a caixa Apoiar o tesouro Gnars marcada, 0,5% de cada troca vai direto para o Gnars Collective Treasury — já responsável por 15 esculturas skatáveis pelo mundo, patrocínios de embaixadores e infraestrutura para uma rede de shredders do planeta inteiro.",
125+
"loop": {
126+
"swap": "de cada swap para o tesouro",
127+
"yield": "do rendimento de stake para o tesouro",
128+
"auction": "da receita de leilões para propostas"
129+
},
130+
"whyEyebrow": "Por que tudo volta pro começo",
131+
"whyBody": "Leilões, swaps e rendimento alimentam um só tesouro. Cada rider vota para onde ele vai — é o DAO inteiro, num card só."
132+
},
133+
"gov": {
134+
"eyebrow": "Governança",
135+
"title": "Um tesouro, um voto cada.",
136+
"recentProposals": "Propostas recentes",
137+
"liveFeed": "Feed ao vivo",
138+
"viewAll": "Ver todas →",
139+
"liveAuction": "Leilão ao vivo",
140+
"currentBid": "Lance atual",
141+
"leaderboard": "Ranking · {count} lances",
142+
"auctionFootnote": "Novos Gnars são leiloados continuamente onchain. Qualquer um pode dar lance; o vencedor minta o Gnar e o valor vai para o tesouro da DAO financiar propostas.",
143+
"for": "A favor {count}",
144+
"against": "Contra {count}",
145+
"abstain": "Abstenção {count}",
146+
"timeLeftDh": "faltam {days}d {hours}h",
147+
"timeLeftH": "faltam {hours}h",
148+
"timeAgoD": "há {days}d",
149+
"timeAgoH": "há {hours}h",
150+
"feedCreated": "criou",
151+
"feedVoted": "votou",
152+
"feedOn": "em",
153+
"feedEmpty": "Nada onchain nos últimos 30 dias.",
154+
"awaitingSettlement": "Aguardando settle"
155+
}
156+
}

0 commit comments

Comments
 (0)