feat(share): floating share bar for text selected in a post#6352
Draft
tsahimatsliah wants to merge 2 commits into
Draft
feat(share): floating share bar for text selected in a post#6352tsahimatsliah wants to merge 2 commits into
tsahimatsliah wants to merge 2 commits into
Conversation
Selecting text inside a post body raises a portaled floating bar anchored to the selection with three actions: copy link to the post (native share sheet on mobile), copy the selection itself, and generate a shareable quote image. - `useTextSelectionShare` detects selection end inside a container, exposes the text plus a viewport rect, follows it on scroll/resize and clears on collapse - `SelectionShareBar` portals the bar to the root, clamps to the visual viewport (pinch-zoom/Android), flips below the selection when there is no room above, dismisses on click-away and Escape, and respects reduced motion - `QuoteImageCard` + `/image-generator/quote/[id]` follow the existing screenshot-service pattern (`#screenshot_wrapper`, ISR) and carry `og:image` / `twitter:card=summary_large_image` - Wired into all three post bodies: classic `PostContent`, `SquadPostContent`, and the redesign `PostFocusCard` (post page and post modal) Gated by the new `share_text_selection` flag AND the `sharing_visibility` master gate. Flag-off mounts none of the hooks, so no listeners are attached. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Opening the raw /image-generator/quote page landed users on a bare 1200x630 bitmap template with no download, share or back affordance. The route, QuoteImageCard and the URL builder stay for the follow-up that previews and shares a real PNG once the screenshot service serves the route. Co-Authored-By: Claude Opus 4.8 <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.
Stacks on PR 1 (#6343) — base branch is
claude/website-sharing-visibility-be6b32, notmain.What changed
Selecting text inside a post body raises a portaled floating bar anchored to the selection, with three actions:
useShareOrCopyLink, so the selection rides along as the share text)useCopyText(clipboard + toast)/image-generator/quote/[id]?text=…in a new tabNew files
packages/shared/src/hooks/useTextSelectionShare.tsenabled: falseattaches nothing.packages/shared/src/components/post/SelectionShareBar.tsxpackages/shared/src/components/post/QuoteImageCard.tsxpackages/webapp/pages/image-generator/quote/[id].tsxid="screenshot_wrapper", followingimage-generator/devcards/[userId].tsxandbadges/[badgeId].tsx.Wiring
usePostRedesignswaps the classic bodies forPostFocusCardon both the post page and the post modal, so all three render paths get the bar:PostContent(classic article/video)SquadPostContent(freeform / welcome / share / YouTube)PostFocusCard(redesign)Each change is a
refon an element that already exists plus one<SelectionShareBar>— which portals, so it adds no DOM in place.PostFocusCardand the classic bodies are a ternary swap, never nested, so there is no double-render.Behaviour details
position: fixed, centred over the selection, flipping below it when there is no room above (< 64px from the top).visualViewportwidth andoffsetLeft, updated on the visual viewport'sscroll/resize— pinch-zoom pans the visual viewport without moving the layout viewport afixedelement sits in, which is the known Android overlay trap.animate-composer-inutility, withmotion-reduce:animate-none.<button type="button">with anaria-labeland a tooltip; the bar isrole="toolbar".Flag
share_text_selection, appended at the end offeatureManagement.ts, defaultfalse. Gated on that AND the masteruseSharingVisibility()gate, both viauseConditionalFeaturewithshouldEvaluate. Flag-off renders nothing and mounts none of the inner hooks, so no selection or viewport listeners are attached at all.Logging
LogEvent.SharePostviausePostLogEvent, withextra: { provider, origin: Origin.TextSelection }. Additions to existing enums:Origin.TextSelection(log.ts, appended in the sharing-visibility section)ShareProvider.CopyText,ShareProvider.QuoteImage(share.ts) — the existing providers are all link targets, and neither of these is oneBackend dependency⚠️
The screenshot service must be taught the
/image-generator/quote/:idroute before "Generate quote image" produces an actual PNG. The frontend renders thescreenshot_wrapperpage regardless, and today the button opens that page in a new tab — so the feature degrades to "here is your quote card" until the service picks up the route. The quote text is passed as a?text=query param (capped at 280 chars) so the ISR'd shell is reused across every quote of the same post.og:imagecurrently points at the existinghttps://og.daily.dev/api/posts/:idendpoint; once the screenshot service serves quote images it can be repointed.Storybook
Components/Share/SelectionShareBar— Desktop, Mobile, FlippedBelow, FlagOff. Storybook aliases@growthbook/growthbookto a mock that coerces falsy flag defaults to the truthy string'control', so flag-off is simulated by holdingFeaturesReadyContextas not-ready (same approach as PR 6).Components/Share/QuoteImageCard— Default, LongQuote, SourceOnly.Verification
node ./scripts/typecheck-strict-changed.jspnpm --filter shared lintpnpm --filter webapp lintNODE_ENV=test pnpm --filter shared testpnpm --filter webapp testNew tests:
SelectionShareBar.spec.tsx— copy link writes to clipboard + toast; copy text writes the selection + toast; the mobile path callsnavigator.share; generate-image opens the quote URL with the encoded selection; the bar dismisses on click-away; flag-off renders nothing and never callsuseTextSelectionShare(proving no listeners), including the case where only the per-surface flag is on.useTextSelectionShare.spec.ts— reports text + rect on selection end, ignores selections outside the container, clears on collapse, inert when disabled.QuoteImageGeneratorPage.spec.tsx—getStaticPropsattribution +twitter:card=summary_large_image+ og image, 404 on a missing post, and the page renders the#screenshot_wrapperelement.Deliberately left out
ShareActionsand would be too heavy for a selection popover.animate-composer-in.🤖 Generated with Claude Code
Preview domain
https://claude-share-text-selection.preview.app.daily.dev