feat(share): surface a copy-link control on tag and source pages - #6357
Draft
tsahimatsliah wants to merge 2 commits into
Draft
feat(share): surface a copy-link control on tag and source pages#6357tsahimatsliah wants to merge 2 commits into
tsahimatsliah wants to merge 2 commits into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
tsahimatsliah
force-pushed
the
claude/share-tags-sources
branch
from
July 27, 2026 12:02
e168f1e to
88b557e
Compare
tsahimatsliah
changed the base branch from
claude/website-sharing-visibility-be6b32
to
claude/share-split-copy-button
July 27, 2026 12:03
tsahimatsliah
force-pushed
the
claude/share-tags-sources
branch
from
July 27, 2026 12:14
88b557e to
b9571d5
Compare
Share moves out of the buried "..." options menu into the header action row on both entity pages, as PR #6369's SplitShareButton: the label copies the link and the chevron drops the standard social list. - New `EntityShareAction` wraps `ShareActions` with the entity's link/text/cid and logs `ShareTag`/`ShareSource` with the provider and origin. `display` defaults to the split control; `icon` keeps the icon-only trigger. - Block leaves the row for the "..." menu, so the row is one Follow button plus identical secondary controls. Blocked is the exception: Unblock stays in the row, in the Follow slot that is empty then, and leaves the menu. - The notification bell takes ButtonVariant.Float so the bell, the copy control and the "..." button read as one treatment. - `CustomFeedOptionsMenu` gains `hideShare` so the in-menu Share entry is dropped where the visible control renders, never both. - `SourceActions` gains `showShare`; only /sources/[source] passes it, so the post page highlights widget keeps its existing row. No feature flag: this ships to everyone. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
tsahimatsliah
force-pushed
the
claude/share-tags-sources
branch
from
July 27, 2026 13:43
b9571d5 to
dfd3cc2
Compare
The copy path tags its link with the referral campaign through `useShareOrCopyLink`, but the social targets went out untagged: `SocialShareList` called `getShortUrl(link)` with no cid. The post share modal was unaffected — it pre-tags the link before handing it down — so this only hit `ShareActions` consumers, which is the control this PR surfaces. `SocialShareList` now takes an optional `cid` and `ShareActions` passes its own. Two link builders also interpolated their text raw, so any `&`, `#`, `?` or `%` in a title silently truncated or corrupted the shared text: - `getRedditShareLink` — `&title=` now encoded - `getTelegramShareLink` — `&text=` now encoded And `getWhatsappShareLink` shared a bare URL while every other target carried the description; it now sends the text with the link. Facebook and LinkedIn are left alone — both ignore text/quote params. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of the sharing visibility initiative. Now stacked on #6369 (
claude/share-split-copy-button) — notmain— because both PRs share the copy-link control. Review #6343 → #6349 → #6369 → this one, or read this diff alone: it only touches the two entity headers.What changed
Share moves out of the buried
CustomFeedOptionsMenu"…" menu into the header action row on both entity pages, as #6369'sSplitShareButton: the left half copies the link (the glyph cross-fades to a green check), the chevron drops the standard social list.TagTopicPageheader (the real tag surface;PageInfoHeadernamed in the brief is aclasseddiv used only by the source page).SourceActions, rendered insidePageInfoHeaderon/sources/[source].The row
Follow·Copy link ⌄·⋮Following·Copy link ⌄·bell·⋮Unblock·Copy link ⌄·⋮Block leaves the row for the menu, so the row is one filled Follow button plus secondary controls that all look and behave the same. Blocked is the deliberate exception: Unblock is the only way back out of a state a user may not have meant to enter, so it stays visible — in the Follow slot, which is empty while blocked — and leaves the menu, so it never appears twice.
The notification bell takes
ButtonVariant.Float, matching the copy control and the "…" button. Its state still reads from the icon.No feature flag
This ships to everyone.
share_tags_sourcesanduseShareTagsSourcesare gone — nothing else read them.sharing_visibilitystays in the codebase; it still gates the initiative's other surfaces, but these two rows no longer consult it.That means no runtime off-switch for these rows: turning them off after merge takes a revert. Flagged deliberately, per the product call to show it to everyone.
Structure
components/share/EntityShareAction.tsx— wraps feat(share): flat end-of-conversation band + split copy-link button #6369'sShareActionswith the entity's link/text/cid and the per-surface log event.displaydefaults tosplit;iconkeeps the original icon-only trigger (behind a vertical rule) for any surface that wants it.CustomFeedOptionsMenugainshideShare, so the in-menu Share entry is dropped exactly where the visible control renders — never both. Defaultfalse, so every other consumer is unchanged.SourceActionsNotifygains an optionalvariantoverride; without it the state-derived variant is unchanged.SourceActionsgainsshowShare. Only/sources/[source]passes it, soPostUsersHighlightson the post page keeps its existing row: Block as a button, Share inside the menu.sharePropsobject feeding the visible control and the existingCustomFeedOptionsMenushareProps, so both hand out the same link.The individual social share links
The copy path tags its link with the referral campaign through
useShareOrCopyLink, but the social targets went out untagged —SocialShareListcalledgetShortUrl(link)with nocid. The post share modal was unaffected because it pre-tags the link before handing it down, so this only hitShareActionsconsumers, i.e. the control this PR surfaces.SocialShareListnow takes an optionalcidandShareActionspasses its own, so a share to X carries the sameuserid/cida copied link does.Two link builders also interpolated their text raw, so any
&,#,?or%in a title silently truncated or corrupted the shared text — and post titles carry those constantly:getRedditShareLink&title=${text}encodeURIComponent(text)getTelegramShareLink&text=${text}encodeURIComponent(text)getWhatsappShareLinktext\nlink, matching the other targetsFacebook and LinkedIn are left alone: both ignore the text/quote params they used to accept.
Note that
getShortUrlreturns the raw link for logged-out visitors, so their shares stay unattributed on every path, copy included. That is existing behaviour, unchanged here.Logging
ShareProvider+ per-surfaceOriginon the existing entity events —LogEvent.ShareTag/LogEvent.ShareSourcewithextra: {provider, origin}.OG meta status, per route
Checked against
next-seo's actualbuildTagsbehaviour rather than assuming./tags/[tag]flags.description, else a generated sentence)_app'sDefaultSeo/sources/[source]source.description, else the site defaultA shared tag/source link already unfurls naming the specific entity — no code change needed.
__tests__/TagSourceSeo.spec.tslocks that behaviour (including the nullable fallbacks) so it can't silently regress now that these links are promoted.Explicitly not faked: per-entity
og:image. There is no OG-image renderer for tags or sources —og.daily.devonly serves/api/posts/:id. Needs a backend/OG-service change, out of scope here.Storybook
Components/Share/EntityShareAction— one All States page mapping every surface, state and opt-in combination, a before/after comparison against the old row, the three copy-control displays, and an interactive playground. Plus per-state stories for isolated review.Verification
NODE_ENV=test pnpm --filter shared test— 298 suites / 2009 tests ✅NODE_ENV=test pnpm --filter webapp test— 45 suites / 302 tests ✅ (cross-package check per AGENTS.md;SourcePage.tsxandTagPage.tsxreworked to reach Block through the menu)pnpm --filter shared lint✅ ·pnpm --filter webapp lint✅node ./scripts/typecheck-strict-changed.js✅ — clean. The 3 pre-existing violations in the files this PR touches are fixed rather than skip-listed:feeds?.edges?.length > 0→feeds?.edges?.length, andsource.idhoisted to asourceIdfallback (source.id ?? source.handle) for the follow/unfollow entity id, sinceSource['id']is optional on the model.buildnot run, per instructions.On the share links specifically: a regression test builds Reddit/Telegram/WhatsApp links from
Rust & Go: what #1 teams ask? 100% realand reads the params back throughURL, and the attribution test seeds the query cache under the tagged url's key so it only passes if thecidactually reaches the social path.Tests assert: the label copies straight to the clipboard with the "✅ Copied link to clipboard" toast; the chevron opens the list without copying; mobile single tap →
navigator.share; the entity log event carries provider + origin; the in-menu Share entry disappears only where the visible control shows; Block moves to the menu and Unblock returns to the row; and the embedded consumer keeps its original row.Open questions
ShareTag/ShareSource) vs the brief'sSharePost. See above.globalThis?.location?.href, inherited verbatim from the existing menu wiring so both controls agree. It carries whatever query string the visitor arrived with. A canonical${webappUrl}tags/${tag}would be cleaner for a promoted control — easy follow-up.additionalOptionsappends). Say the word if it should sit first.🤖 Generated with Claude Code