Uh oh!
There was an error while loading. Please reload this page.
feat(home): featurear la ronda de votación que está abierta - #75
Conversation
El hero de votación del home estaba clavado a `getHackathon("gaming")`, así
que quedó invisible en cuanto esa ronda cerró (VotingHero esconde los
resultados en el home 7 días después del cierre) y nunca mostró la ronda
siguiente. Hoy la votación abierta es `ai-agents` y el home no la anunciaba.
Ahora `getFeaturedVotingRound()` resuelve la ronda desde los relays: escanea
las 3 hackatones más recientes que ya arrancaron, prefiere la que está
abierta y si no hay ninguna cae a la cerrada más reciente (que el hero sigue
mostrando durante su semana de anuncio). Registra el cacheTag de votación de
todas las hackatones, porque los tags de un `"use cache"` anidado no burbujean
en Next 16 y tagear sólo las candidatas dejaría fuera la apertura de una
hackatón nueva.
El hero además baja al dashboard de usuario logueado: HomeGate reemplaza el
home por HomeDashboard cuando hay sesión, así que los que realmente tienen
votos para gastar no veían el estado de la votación. La copia del dashboard
va con `initialPeriod={null}` a propósito — sólo renderiza en el cliente y
useVotingLive relee el período al montar, no hace falta mandar el snapshot
dos veces. El prop `inline` saca el padding que despeja el header fijo.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughThe home page now resolves the featured voting round from cached relay data. Authenticated users see the round in an inline dashboard hero. The hero supports adjusted spacing, while unauthenticated and marketing-home rendering remain unchanged. ChangesFeatured voting home
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant Home
participant HomeGate
participant HomeDashboard
participant HomeVotingHero
participant getFeaturedVotingRound
Home->>HomeVotingHero: render inline hero
HomeVotingHero->>getFeaturedVotingRound: resolve featured round
getFeaturedVotingRound-->>HomeVotingHero: return round or null
Home->>HomeGate: pass votingHero
HomeGate->>HomeDashboard: forward votingHero when authenticated
HomeDashboard-->>Home: render voting hero above dashboard content
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@app/page.tsx`:
- Around line 16-33: Distinguish “no voting period” from “period fetch pending”
for the inline path. Update useVotingLive to expose a first-fetch/pending state
when initialPeriod is null, and update VotingHero’s early-return logic to render
a lightweight inline placeholder while that fetch is pending, retaining null
only when no period exists and preserving the existing home behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 29f944a9-ef1c-44ee-b9b5-a745995bb222
📒 Files selected for processing (5)
app/page.tsxcomponents/home/HomeDashboard.tsxcomponents/home/HomeGate.tsxcomponents/voting/VotingHero.tsxlib/votingCache.ts
| async function HomeVotingHero({ inline = false }: { inline?: boolean }) { | ||
| "use cache"; | ||
| const gaming = getHackathon("gaming"); | ||
| if (!gaming) return null; | ||
| cacheTag(nostrVotingTag(gaming.id)); | ||
| const period = await getCachedVotingPeriod(gaming.id); | ||
| if (!period) return null; | ||
| cacheLife("nostr"); | ||
| const featured = await getFeaturedVotingRound(); | ||
| if (!featured) return null; | ||
| return ( | ||
| <VotingHero | ||
| hackathonId={gaming.id} | ||
| hackathonName={gaming.name} | ||
| initialPeriod={period} | ||
| hackathonId={featured.hackathon.id} | ||
| hackathonName={featured.hackathon.name} | ||
| // The dashboard copy only ever renders on the client (HomeGate swaps it in | ||
| // once auth resolves) and `useVotingLive` re-reads the period on mount — | ||
| // shipping the SSR snapshot twice would be dead payload. | ||
| initialPeriod={inline ? null : featured.period} | ||
| variant="home" | ||
| inline={inline} | ||
| /> | ||
| ); | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift
Inline hero renders nothing until the client-side period fetch resolves, even when a round is open.
Line 28 sets initialPeriod={null} for the inline (dashboard) case. In VotingHero (components/voting/VotingHero.tsx, lines 103-110), if (!period) return null; runs before any state update from useVotingLive's mount-time fetch (lib/useVotingLive.ts, the /api/hackathons/${hackathonId}/voting effect). Until that fetch resolves, the component returns null, so the dashboard hero is invisible for every authenticated user on every load with an open round, not just the first paint.
useVotingLive's loading flag does not cover this case: it reduces to !ready while period is null (since votingOpenNow is false), so ready can turn true before the period fetch completes, leaving a window where nothing is rendered and no skeleton exists to reserve space (a layout shift on every load).
Before this PR, variant="home" always shipped the real period, so !period meant "no round exists" — a stable, correct case. This PR overloads the same null state to also mean "round exists, not fetched yet," which VotingHero's early return and useVotingLive's loading computation don't distinguish.
Consider exposing a distinct "period pending" state from useVotingLive (e.g., initialPeriod === null && !firstFetchDone) and having VotingHero render a lightweight placeholder instead of null while pending, only for the inline case.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@app/page.tsx` around lines 16 - 33, Distinguish “no voting period” from
“period fetch pending” for the inline path. Update useVotingLive to expose a
first-fetch/pending state when initialPeriod is null, and update VotingHero’s
early-return logic to render a lightweight inline placeholder while that fetch
is pending, retaining null only when no period exists and preserving the
existing home behavior.
Uh oh!
There was an error while loading. Please reload this page.
Problema
El hero de votación del home estaba clavado a
getHackathon("gaming")(app/page.tsx). Esa ronda cerró el 2026-06-30 yVotingHeroesconde los resultados en el home 7 días después del cierre, así que el home no muestra nada — ni siquiera la votación deai-agents, que está abierta ahora mismo:Segundo problema, más silencioso:
HomeGatereemplaza el home porHomeDashboardcuando hay sesión, y el dashboard no tenía ranura para el hero. Es decir, los únicos que tienen votos para gastar eran justo los que no veían el estado de la votación.Cambio
lib/votingCache.ts— nuevogetFeaturedVotingRound(): escanea las 3 hackatones más recientes que ya arrancaron (una ronda no existe antes de la apertura), prefiere la que está abierta y si no hay ninguna cae a la cerrada más reciente. Registra elcacheTagde votación de todas las hackatones: los tags de un"use cache"anidado no burbujean en Next 16, y tagear sólo las candidatas dejaría a la apertura de una hackatón nueva sin revalidar el home.app/page.tsx— el hero se resuelve desde los relays en vez de estar pineado a un id, concacheLife("nostr").components/home/HomeGate.tsx+HomeDashboard.tsx— el hero baja al dashboard, debajo de la banda de saludo. La copia del dashboard va coninitialPeriod={null}a propósito: sólo renderiza en el cliente yuseVotingLiverelee el período al montar, así no se manda el snapshot dos veces.components/voting/VotingHero.tsx— propinlineque saca el padding que despeja el header fijo cuando el hero va dentro del dashboard.Verificación
Resolución corrida contra los relays reales con la pubkey publisher de producción →
→ el home mostraría: AI AGENTS (open).Render verificado en un entorno local aislado (relay local + claves descartables, dos rondas sembradas:
zapscerrada hace 3 días +ai-agents):tsc --noEmitlimpio ypnpm buildOK (/queda estático, revalidate 5m / expire 1w).🤖 Generated with Claude Code
Summary by CodeRabbit