feat(entry): durable on-post internal links + visible breadcrumb - #1050
Conversation
Add server-rendered "More from author" and "More in {community/tag}"
blocks to the post page, plus a visible breadcrumb that mirrors the
existing BreadcrumbList JSON-LD.
The two related-post blocks are async server components fed by existing
SDK queries (getAccountPostsQueryOptions, getPostsRankedQueryOptions).
Their links render in the initial SSR HTML and are wrapped in Suspense
so the bounded feed fetches stream as a later chunk instead of gating
the post body. Links use the canonical bare /@author/permlink form and
exclude the current post, the author's own posts (in the tag block),
duplicates, NSFW, and mod-muted posts. The breadcrumb shares a single
source array with the BreadcrumbList JSON-LD so the two never drift.
Greptile SummaryThis PR adds two SSR-rendered "related posts" blocks ("More from author" and "More in {community/tag}") plus a visible breadcrumb nav to the entry page. It refactors the old hardcoded
Confidence Score: 5/5Safe to merge — the new async server components degrade gracefully on any fetch failure and the shared breadcrumb helper fixes a pre-existing raw hive-id display bug. The fetchQuery wrapper (withSsrTimeout) resolves to undefined on both timeouts and errors, so neither related-posts component can throw and disrupt the post body. The EntryRenderBoundary (SentryErrorBoundary) covers the whole render tree as a backstop. The breadcrumb refactor is well-tested (14 cases) and the structured data change is a strict improvement — omitting a raw '#hive-125125' crumb from JSON-LD rather than adding a misleading one. No files require special attention. Important Files Changed
Sequence Diagram%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant Browser
participant Next as Next.js SSR
participant Hive as Hive API
Browser->>Next: "GET /@author/permlink"
Next->>Hive: prefetchQuery(entry + account)
Hive-->>Next: entry data
Next->>Next: buildEntryBreadcrumbs(entry)
Next->>Next: buildArticleJsonLd + buildBreadcrumbJsonLd
Next-->>Browser: Stream: post body + breadcrumb nav + JSON-LD
Note over Next,Browser: Suspense boundaries stream later chunks
Next->>Hive: fetchQuery(getAccountPostsQueryOptions)
Hive-->>Next: author posts (or undefined on timeout/error)
Next->>Next: filter: skip current post, NSFW, muted
Next-->>Browser: Stream chunk: More from author cards
Next->>Hive: fetchQuery(getPostsRankedQueryOptions)
Hive-->>Next: tag/community posts (or undefined on timeout/error)
Next->>Next: filter: skip author posts, NSFW, muted, duplicates
Next-->>Browser: Stream chunk: More in section cards
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant Browser
participant Next as Next.js SSR
participant Hive as Hive API
Browser->>Next: "GET /@author/permlink"
Next->>Hive: prefetchQuery(entry + account)
Hive-->>Next: entry data
Next->>Next: buildEntryBreadcrumbs(entry)
Next->>Next: buildArticleJsonLd + buildBreadcrumbJsonLd
Next-->>Browser: Stream: post body + breadcrumb nav + JSON-LD
Note over Next,Browser: Suspense boundaries stream later chunks
Next->>Hive: fetchQuery(getAccountPostsQueryOptions)
Hive-->>Next: author posts (or undefined on timeout/error)
Next->>Next: filter: skip current post, NSFW, muted
Next-->>Browser: Stream chunk: More from author cards
Next->>Hive: fetchQuery(getPostsRankedQueryOptions)
Hive-->>Next: tag/community posts (or undefined on timeout/error)
Next->>Next: filter: skip author posts, NSFW, muted, duplicates
Next-->>Browser: Stream chunk: More in section cards
Reviews (2): Last reviewed commit: "fix(entry): guard breadcrumb hive-id lea..." | Re-trigger Greptile |
|
Warning Review limit reached
Next review available in: 27 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (5)
📝 WalkthroughWalkthroughAdds server-rendered "More from author" and "More in tag" related-post sections to the entry page, alongside a new breadcrumb component. Introduces source-resolution and linkability helpers, a shared card/list UI, i18n keys, Suspense-wrapped streaming in the page, and tests covering all new logic. ChangesEntry Related Posts & Breadcrumb
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 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: 2
🤖 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
`@apps/web/src/app/`(dynamicPages)/entry/[category]/[author]/[permlink]/page.tsx:
- Around line 158-162: The breadcrumb fallback in the page component is leaking
raw hive community ids when `entry.community_title` is missing. Update the
breadcrumb construction in the entry page so the `name`/section crumb does not
fall back to `#${entry.category}` for hive-id-shaped categories; instead mirror
the hive-id guard used in `buildArticleJsonLd` and `resolveRelatedSource` by
omitting or replacing that crumb when there is no community title. Ensure the
BreadcrumbList JSON-LD and visible breadcrumb stay aligned.
In `@apps/web/src/specs/features/entry/entry-related.spec.tsx`:
- Around line 14-21: The spec is using `any` in the `next/link` and `next/image`
mock components, and the `stats` override is also escaping type safety. Replace
those `any` annotations with explicit prop types for the mocked `default`
exports so the mock signatures stay aligned with the real components, and type
the `stats` override with `Parameters<typeof isLinkableRelated>[0]` to preserve
strict checking and catch prop-shape drift.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: d69353d7-ba9e-4782-b947-e8f1537a417d
📒 Files selected for processing (9)
apps/web/src/app/(dynamicPages)/entry/[category]/[author]/[permlink]/_components/entry-page-breadcrumb.tsxapps/web/src/app/(dynamicPages)/entry/[category]/[author]/[permlink]/_components/entry-page-more-from-author.tsxapps/web/src/app/(dynamicPages)/entry/[category]/[author]/[permlink]/_components/entry-page-more-in-tag.tsxapps/web/src/app/(dynamicPages)/entry/[category]/[author]/[permlink]/_components/entry-related-card.tsxapps/web/src/app/(dynamicPages)/entry/[category]/[author]/[permlink]/_components/entry-related-list.tsxapps/web/src/app/(dynamicPages)/entry/[category]/[author]/[permlink]/_components/entry-related-source.tsapps/web/src/app/(dynamicPages)/entry/[category]/[author]/[permlink]/page.tsxapps/web/src/features/i18n/locales/en-US.jsonapps/web/src/specs/features/entry/entry-related.spec.tsx
| default: ({ href, children, ...rest }: any) => ( | ||
| <a href={typeof href === "string" ? href : href?.pathname} {...rest}> | ||
| {children} | ||
| </a> | ||
| ) | ||
| })); | ||
| vi.mock("next/image", () => ({ | ||
| default: ({ src, alt }: any) => <img src={typeof src === "string" ? src : src?.src} alt={alt} /> |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Checking strict-mode declaration in tsconfig files:"
fd -a 'tsconfig*.json' . -x sh -c 'echo "== {} =="; rg -n "\"strict\"\\s*:\\s*true" "{}" || true'
echo
echo "Checking for remaining any-escapes in this spec:"
rg -n '\b:\s*any\b|\bas any\b' apps/web/src/specs/features/entry/entry-related.spec.tsxRepository: ecency/vision-next
Length of output: 876
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect the spec file around the flagged lines and the related helper types.
sed -n '1,140p' apps/web/src/specs/features/entry/entry-related.spec.tsx
echo
echo "Related type/source definitions:"
rg -n "function isLinkableRelated|type .*Related|interface .*Related|stats" \
apps/web/src/specs/features/entry/entry-related-source.ts \
apps/web/src/specs/features/entry/entry-page-breadcrumb.tsx \
apps/web/src/specs/features/entry/entry-related-list.tsxRepository: ecency/vision-next
Length of output: 5441
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Map the helper file to inspect the parameter type shape directly.
ast-grep outline apps/web/src/specs/features/entry/entry-related-source.ts --view expandedRepository: ecency/vision-next
Length of output: 275
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Locate the imported helper files and inspect the relevant type shapes.
fd -a 'entry-related-source.ts' apps/web
fd -a 'entry-page-breadcrumb.tsx' apps/web
fd -a 'entry-related-list.tsx' apps/web
echo
echo "Outline of the source helper:"
ast-grep outline 'apps/web/src/app/(dynamicPages)/entry/[category]/[author]/[permlink]/_components/entry-related-source.ts' --view expanded
echo
echo "Relevant type/usage snippets:"
rg -n "isLinkableRelated|stats|json_metadata|type .*Entry|interface .*Entry" \
'apps/web/src/app/(dynamicPages)/entry/[category]/[author]/[permlink]/_components/entry-related-source.ts' \
'apps/web/src/app/(dynamicPages)/entry/[category]/[author]/[permlink]/_components/entry-related-list.tsx' \
'apps/web/src/app/(dynamicPages)/entry/[category]/[author]/[permlink]/_components/entry-page-breadcrumb.tsx' \
apps/web/src/specs/test-utils* \
apps/web/src/specs/features/entry/entry-related.spec.tsxRepository: ecency/vision-next
Length of output: 3549
🏁 Script executed:
#!/bin/bash
set -euo pipefail
sed -n '1,80p' apps/web/src/app/(dynamicPages)/entry/[category]/[author]/[permlink]/_components/entry-related-source.ts
echo
sed -n '160,310p' apps/web/src/specs/test-utils.tsxRepository: ecency/vision-next
Length of output: 209
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect the spec file line ranges around the mocks and the typed fixture.
sed -n '1,130p' apps/web/src/specs/features/entry/entry-related.spec.tsxRepository: ecency/vision-next
Length of output: 4659
🏁 Script executed:
#!/bin/bash
set -euo pipefail
sed -n '1,120p' apps/web/src/app/(dynamicPages)/entry/[category]/[author]/[permlink]/_components/entry-related-source.ts
echo
sed -n '160,290p' apps/web/src/specs/test-utils.tsx
echo
sed -n '1,120p' apps/web/src/specs/features/entry/entry-related.spec.tsxRepository: ecency/vision-next
Length of output: 209
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Read the precise type signatures used by the spec and helper.
sed -n '1,80p' 'apps/web/src/app/(dynamicPages)/entry/[category]/[author]/[permlink]/_components/entry-related-source.ts'
echo
sed -n '160,310p' apps/web/src/specs/test-utils.tsx
echo
sed -n '1,120p' apps/web/src/specs/features/entry/entry-related.spec.tsxRepository: ecency/vision-next
Length of output: 10507
Replace the any escapes in this spec.
Use explicit prop types for the next/link and next/image mocks, and type the stats override with Parameters<typeof isLinkableRelated>[0] instead of any so strict mode keeps catching prop-shape drift.
🤖 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 `@apps/web/src/specs/features/entry/entry-related.spec.tsx` around lines 14 -
21, The spec is using `any` in the `next/link` and `next/image` mock components,
and the `stats` override is also escaping type safety. Replace those `any`
annotations with explicit prop types for the mocked `default` exports so the
mock signatures stay aligned with the real components, and type the `stats`
override with `Parameters<typeof isLinkableRelated>[0]` to preserve strict
checking and catch prop-shape drift.
Source: Coding guidelines
…elated cards Address review feedback: - Extract breadcrumb building into buildEntryBreadcrumbs and omit the section crumb when the category is a raw hive-id without a community title, so the visible breadcrumb and BreadcrumbList JSON-LD never surface a machine id. - Add a sizes hint to the related/similar post card images so the optimizer serves a card-sized (~33vw) rendition in the 3-column grid instead of a 100vw one (smaller image downloads).
Adds two server-rendered related-post blocks to the post page ("More from author" and "More in {community/tag}") plus a visible breadcrumb.
getAccountPostsQueryOptions,getPostsRankedQueryOptions); links render in the initial SSR HTML.Suspenseso the bounded feed fetches stream as a later chunk instead of gating the post body./@author/permlinkhrefs; excludes the current post, the author's own posts (in the tag block), duplicates, NSFW, and mod-muted posts.BreadcrumbListJSON-LD so the two never drift.Summary by CodeRabbit
New Features
Bug Fixes