Skip to content

fix(community): ShareLink 按钮语义修正 + feed SSR 抗 CF 挑战#315

Merged
longsizhuo merged 1 commit intomainfrom
fix/feed-hero-semantics
Apr 19, 2026
Merged

fix(community): ShareLink 按钮语义修正 + feed SSR 抗 CF 挑战#315
longsizhuo merged 1 commit intomainfrom
fix/feed-hero-semantics

Conversation

@longsizhuo
Copy link
Copy Markdown
Member

背景

用户反馈:

  1. Hero 的 ShareLink 按钮主点跳 `/feed`、徽章跳 `/feed/submit`——语义错位(按钮是"投稿"动作,不该去阅读页)
  2. 生产 `/feed` 500:"This page couldn't load. A server error occurred."

改动

按钮语义修正

  • `ShareLink` 主按钮 `/feed` → `/feed/submit`,去掉右上角 "+" 徽章(冗余)
  • Join the Resistance 卡片里"访问文章"下方加同构按钮"看看我们最近在读什么" → `/feed`
  • 信息架构:Hero 左侧 Contribute + ShareLink = 两个投稿动作;Join 区两个按钮 = 两个阅读入口(文档 + 社区)

Next 16 严格模式修正

  • 本地 `/feed` 500 原因:`FeedAuthWrapper` 是 client component 但收 `getCategoryLabel` 函数 prop,Next 16 严格禁止
  • 改为 server 端预计算 `slug → 中文` map 传给 client,纯数据

生产 /feed 500 修复

  • 原因:Vercel SSR 出口被 Cloudflare Managed Challenge 403,`fetchLinks` 单次失败就抛错
  • 对齐 `fetchProfile` 的防御:UA header + 2 次重试(no-store 绕 Next Data Cache)+ cf-ray 日志
  • 全败返回 `[]` 而非抛错,页面降级展示空态不崩

i18n

  • 新增 `hero.cta.feed`
  • 移除失效的 `shareLink.submitAriaLabel`

验证

  • 本地 `/` `/feed` `/feed/submit` 都 200
  • typecheck 0 errors
  • vitest 1/1 绿

Copilot AI review requested due to automatic review settings April 19, 2026 16:48
@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 19, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
involutionhell-github-io Canceled Canceled Apr 19, 2026 5:00pm
website-preview Ready Ready Preview, Comment Apr 19, 2026 5:00pm

… SSR 抗 CF 挑战

按钮语义修正(用户反馈):
- Hero ShareLink 主按钮 /feed → /feed/submit(语义:投稿动作,与 Contribute 平级)
- 去掉 ShareLink 右上角 "+" 徽章(主按钮已经是投稿,徽章冗余)
- Join the Resistance 卡片里"访问文章"按钮下方加同构"看看我们最近在读什么"→ /feed
  (阅读入口从 Hero 主 CTA 挪到 Join 区,避免与投稿动作混淆)

Next 16 严格模式修正:
- FeedAuthWrapper 之前收 getCategoryLabel 函数 prop 会触发
  "Functions cannot be passed directly to Client Components" → /feed 500
- 改传 server 端预计算的 slug → 中文 map(纯数据),client 组件自己查表

生产 500 修复(生产症状:/feed 显示 "server error"):
- fetchLinks 之前单次失败就抛错,Cloudflare Managed Challenge 403 时直接崩
- 加重试 + UA 头 + cf-ray 日志,对齐 fetchProfile 的防御策略
- 全败时返回 [] 而非抛错,页面降级展示空态不崩

i18n:
- 新增 hero.cta.feed("看看我们最近在读什么" / "What we're reading lately")
- 移除失效的 shareLink.submitAriaLabel(徽章已删)
@longsizhuo longsizhuo force-pushed the fix/feed-hero-semantics branch from 2283663 to f670a35 Compare April 19, 2026 16:52
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adjusts the homepage/community CTA information architecture and introduces a new Community Feed feature set, while hardening /feed SSR fetching against Cloudflare challenge failures.

Changes:

  • Fixes Hero ShareLink CTA semantics (primary action goes to /feed/submit) and adds a separate “reading” entry to /feed.
  • Adds Community Feed pages/components: /feed, /feed/submit, /share, user “my shares” pages under /u/[username]/shares, and an admin moderation screen.
  • Adds backend rewrites for the new community APIs and expands i18n strings for feed/share UI.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
next.config.mjs Adds rewrites for community feed and admin community API proxying to backend.
messages/zh.json Adds feed/share i18n strings and Hero CTA label for /feed.
messages/en.json Adds English equivalents for feed/share i18n strings and Hero CTA label for /feed.
app/u/[username]/shares/page.tsx New client page to show the signed-in user’s submitted links (“mine”) with status badges.
app/u/[username]/shares/layout.tsx Adds a server layout to host Header/Footer above a client page.
app/u/[username]/page.tsx Adds “My shares” entry point and profile-side embedded shares section.
app/u/[username]/SharesOnProfile.tsx New embedded “My shares” module on the profile page (owner-only).
app/u/[username]/SharesLinkIfOwner.tsx New owner-only navigation link to /u/{identifier}/shares.
app/share/page.tsx New minimal “quick share” page with optional URL/text prefilling and login redirect.
app/feed/types.ts Introduces typed DTOs for community shared links and categories.
app/feed/submit/page.tsx New feed submission page that posts links to backend with auth.
app/feed/submit/layout.tsx Server layout to keep Header/Footer as server components while page is client.
app/feed/page.tsx New SSR feed listing page with CF-challenge-aware retries and server-precomputed category labels.
app/feed/components/ReportButton.tsx New client report dialog/button to POST link reports.
app/feed/components/LinkCard.tsx New link card UI used across feed and “my shares” views.
app/feed/components/FeedAuthWrapper.tsx Client bridge to inject login status and category labels into LinkCard list rendering.
app/feed/components/CategoryTabs.tsx Client category tabs that drive filtering via ?category=<slug>.
app/components/ShareLink.tsx Updates Hero ShareLink CTA to point to /feed/submit and removes redundant badge behavior.
app/components/Hero.tsx Updates Hero CTA layout to separate “contribute/share” vs “read” actions and adds /feed CTA label.
app/components/Contribute.tsx Small layout tweak so outer container controls margin and positioning consistently with the new CTA row.
app/admin/community/page.tsx New admin UI to approve/reject pending/flagged community links.
app/admin/community/lib.ts Adds admin API client helper for community moderation endpoints.
app/admin/community/layout.tsx Adds route segment layout placeholder for future admin community nav/sidebars.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread app/components/Hero.tsx
Comment on lines +101 to +119
className="block w-full"
data-umami-event="navigation_click"
data-umami-event-region="hero_cta"
data-umami-event-label="Access Articles"
>
<button className="w-full py-3 border border-[var(--background)] font-sans text-xs uppercase tracking-widest hover:bg-[var(--background)] hover:text-[var(--foreground)] transition-all cursor-pointer">
{t("cta.access")}
</button>
</Link>
<Link
href="/feed"
className="block w-full"
data-umami-event="navigation_click"
data-umami-event-region="hero_cta"
data-umami-event-label="Community Feed"
>
<button className="w-full py-3 border border-[var(--background)] font-sans text-xs uppercase tracking-widest hover:bg-[var(--background)] hover:text-[var(--foreground)] transition-all cursor-pointer">
{t("cta.feed")}
</button>
Copy link

Copilot AI Apr 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These Link blocks wrap a native <button> inside an anchor, which is invalid HTML (nested interactive elements) and can cause accessibility / keyboard issues. Prefer rendering the link itself as the styled control (or use the shared <Button asChild> pattern used elsewhere, e.g. app/not-found.tsx:31) to avoid nested interactive elements.

Suggested change
className="block w-full"
data-umami-event="navigation_click"
data-umami-event-region="hero_cta"
data-umami-event-label="Access Articles"
>
<button className="w-full py-3 border border-[var(--background)] font-sans text-xs uppercase tracking-widest hover:bg-[var(--background)] hover:text-[var(--foreground)] transition-all cursor-pointer">
{t("cta.access")}
</button>
</Link>
<Link
href="/feed"
className="block w-full"
data-umami-event="navigation_click"
data-umami-event-region="hero_cta"
data-umami-event-label="Community Feed"
>
<button className="w-full py-3 border border-[var(--background)] font-sans text-xs uppercase tracking-widest hover:bg-[var(--background)] hover:text-[var(--foreground)] transition-all cursor-pointer">
{t("cta.feed")}
</button>
className="block w-full py-3 border border-[var(--background)] font-sans text-xs uppercase tracking-widest text-center hover:bg-[var(--background)] hover:text-[var(--foreground)] transition-all cursor-pointer"
data-umami-event="navigation_click"
data-umami-event-region="hero_cta"
data-umami-event-label="Access Articles"
>
{t("cta.access")}
</Link>
<Link
href="/feed"
className="block w-full py-3 border border-[var(--background)] font-sans text-xs uppercase tracking-widest text-center hover:bg-[var(--background)] hover:text-[var(--foreground)] transition-all cursor-pointer"
data-umami-event="navigation_click"
data-umami-event-region="hero_cta"
data-umami-event-label="Community Feed"
>
{t("cta.feed")}

Copilot uses AI. Check for mistakes.
@longsizhuo longsizhuo merged commit cd14662 into main Apr 19, 2026
7 checks passed
longsizhuo pushed a commit that referenced this pull request Apr 19, 2026
Copilot CR 指出的两条:

PR #315 Hero.tsx:119 —— <a> 包 <button> 是嵌套交互元素(HTML 无效 + a11y 问题)
  修法:把 <Link> 直接渲染成按钮样式,不再嵌套 <button>

PR #316 LinkCard.tsx:52 / admin/community/page.tsx:143 —— OG 封面 URL
  直接进 <img src> 没过白名单。
  修法:用 lib/url-safety.ts 的 sanitizeMediaUrl 兜底,拦 javascript:/data: 协议
  (后端 UrlNormalizer 是第一道防线,前端 sanitize 是 defense-in-depth)
longsizhuo added a commit that referenced this pull request Apr 19, 2026
Copilot CR 指出的两条:

PR #315 Hero.tsx:119 —— <a> 包 <button> 是嵌套交互元素(HTML 无效 + a11y 问题)
  修法:把 <Link> 直接渲染成按钮样式,不再嵌套 <button>

PR #316 LinkCard.tsx:52 / admin/community/page.tsx:143 —— OG 封面 URL
  直接进 <img src> 没过白名单。
  修法:用 lib/url-safety.ts 的 sanitizeMediaUrl 兜底,拦 javascript:/data: 协议
  (后端 UrlNormalizer 是第一道防线,前端 sanitize 是 defense-in-depth)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants