fix(frontend): mobile navbar navigation + SSR hydration mismatch
Root cause of 'navbar mobile tak bisa pindah halaman': Next <Link> client-side navigation is dead app-wide. A React hydration mismatch (#418: 'server rendered text didn't match the client') is thrown by the SSR-seeded live feeds — relative times (formatRelativeTime(e.edited_at) / m.created_at) computed with Date.now() render slightly differently on server vs client, which breaks the Next client router (router.push is a no-op). The desktop NavRail worked only because it uses plain <a href> (hard navigation bypasses the broken router). Fixes: - mobile-nav.tsx: use plain <a href> (NOT Next <Link>), identical to the working sidebar NavRail, so mobile nav always navigates regardless of router state ('ikuti cara kerja sidebar'). - Add suppressHydrationWarning to the SSR-seeded relative-time spans so server/client drift no longer throws #418 (EditHistory, LiveModerationFeed, messages/results + detail rows, recordings, TermGlossary, ChannelCultureGlossary, CategoryDrilldown). Verified on non-prod :4024 @375px: Voice/Media/Search all navigate, no #418 in console. Plan: .hermes/plans/mobile-nav-hydration-fix.md
This commit is contained in:
@@ -360,7 +360,10 @@ export function MessagesView({
|
||||
<span className="font-mono text-[10px] font-semibold text-signal">
|
||||
{(r.score * 100).toFixed(0)}% RELEVANCE
|
||||
</span>
|
||||
<span className="ml-auto font-mono text-[10px] text-ink-muted">
|
||||
<span
|
||||
className="ml-auto font-mono text-[10px] text-ink-muted"
|
||||
suppressHydrationWarning
|
||||
>
|
||||
{formatRelativeTime(r.created_at)}
|
||||
</span>
|
||||
</div>
|
||||
@@ -576,7 +579,10 @@ function MessageDetail({
|
||||
{m.server_nick && m.server_nick !== m.username && (
|
||||
<div className="text-[11px] text-ink-muted">@{m.username}</div>
|
||||
)}
|
||||
<div className="mono text-[0.65rem] text-ink-faint">
|
||||
<div
|
||||
className="mono text-[0.65rem] text-ink-faint"
|
||||
suppressHydrationWarning
|
||||
>
|
||||
{getMessageChannelLabel(m)} · {formatRelativeTime(m.created_at)}
|
||||
</div>
|
||||
</div>
|
||||
@@ -752,7 +758,10 @@ function MessageRow({
|
||||
<span className="font-mono text-[10px] text-ink-muted">
|
||||
{getMessageChannelLabel(m)}
|
||||
</span>
|
||||
<span className="ml-auto font-mono text-[10px] text-ink-muted">
|
||||
<span
|
||||
className="ml-auto font-mono text-[10px] text-ink-muted"
|
||||
suppressHydrationWarning
|
||||
>
|
||||
{formatRelativeTime(m.created_at)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -215,7 +215,9 @@ export function RecordingsView({
|
||||
{r.channel_name ?? "voice"}
|
||||
</span>
|
||||
<span>·</span>
|
||||
<span>{formatRelativeTime(r.created_at)}</span>
|
||||
<span suppressHydrationWarning>
|
||||
{formatRelativeTime(r.created_at)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{isPlaying && <NowPlayingChip />}
|
||||
|
||||
Reference in New Issue
Block a user