Unified invite landing for collection and document invites - #638
Unified invite landing for collection and document invites#638icarusz wants to merge 14 commits into
Conversation
Both invite entry paths (#/join-collection/... and #/share/...) now land on
one InviteLanding card: kicker, inviter line, title, display-only payload
preview, what-is-QuartoHub explainer, and a single CTA. Signed-out users see
the card (not the login wall) with a GIS "Continue with Google" CTA that
round-trips back to the invite via the pre-auth hash; signed-in users get
one-click join/open. Share routes no longer auto-connect on load: the URL is
still scrubbed on mount, but connection happens on the CTA click. After
joining, the user lands in the editor on the intended file with a one-time
dismissible welcome banner (per-target localStorage dismissal).
- routing.ts: ShareRoute gains from=/preview=; JoinCollectionRoute gains
preview=/start= (base64url JSON codec in utils/invitePreview.ts, versioned,
capped at 3 projects x 2 files, display-only). Legacy URLs parse unchanged;
malformed payloads degrade to undefined. savePreAuthHash takes an explicit
hash; clearPreAuthHash drops it once an invite is consumed.
- App.tsx: pendingShare captured at boot; connectToSharedProject hoisted with
{quiet, addToSet} opts; collection CTA subscribes then opens the start=
target (else home); invite-first silent root creation now covers share
invitees; ephemeral preview boots keep eager connect.
- JoinCollectionLanding (name/color form) deleted; identity comes from the
Google-account name upgrade with anonymous fallback.
- Sender side: buildInviteUrl embeds preview from cached peek summaries and
start=first project's first file; buildShareableUrl gains {from, preview},
wired at both call sites (ProjectsHome menu, Editor ShareDialog).
- AuthProvider SignInButtonProps gains text variant ('continue_with').
- Dev harness pages for all landing variants + welcome banner.
Tests: TDD throughout (routing/codec/component/banner suites written first,
verified red). 476 unit + 118 integration + 133 wasm green except the
pre-existing main breakage tracked in bd-qvjc6gdp (useAutomergeSync mock,
BranchBar). e2e share-link specs updated to click through the landing; suite
71/72 (BranchBar again). Verified end-to-end in a real browser against a
local hub: share link -> landing -> CTA -> editor on file -> banner, and
collection invite -> join -> editor on start target -> banner.
Design handoff bundle (spec + authoritative mocks) committed at
design_handoff_invite_landing/; plan at
claude-notes/plans/2026-09-01-unified-invite-landing.md.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…xdcxbpq)
Andrew's manual testing surfaced invite previews showing "0 files" for
populated projects. Cause: two layers dropped the cached summary when an
entry was copied into a collection. ProjectsHome's entryFor projected only
{indexDocId, syncServer, description}, and addProjectToSet discarded
entry.summary on write. The collection copy therefore had no file counts
until the project's next open (updateProjectSummaryEverywhere then healed
every copy — which is why reopening "fixed" it).
Both layers now carry the summary: entryFor includes it when the source
entry has one, and addProjectToSet persists it on create and adopts it on
re-add only when the existing entry has none (freshness stays owned by
updateProjectSummaryInSet, so a stale re-add can never clobber a live
summary).
TDD: three new schema tests written first and verified red; 75/75 green
after. Verified live against a local hub: adding a project to a brand-new
collection immediately shows "2 files" on the copy, and the collection's
invite preview payload decodes with the real fileCount without reopening.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…-fxdcxbpq) Direction change from Andrew's design review: the invite is to a collection, so joining should land on the QuartoHub home screen with that collection's section promoted to the top — not cold-open the first document's editor. - handleCollectionCta: subscribe, then navigate home; the start-target editor open is gone. ProjectsHome gains promoteCollectionId (session- scoped, set on join) and sorts that section first. - InviteLanding: startName prop and "Join and open X" CTA removed; the collection CTA reads "Join <name>" (or "Join collection" on legacy links). - buildInviteUrl no longer emits start= (the parser still tolerates it on links already in the wild — verified live against one). - Welcome banner: now scoped to the invite target — a document invite's banner shows only on that project; a collection invite's banner shows on the first project opened from that collection, reworded to "Carlos invited you." (the "suggested starting here" claim died with start=). TDD: banner-copy test updated first and verified red; 235 affected tests green after. Verified live against a local hub: join CTA lands on home with the joined collection sorted above an older one. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Andrew's rule from design review: if the user is about to go through the
Google sign-in flow for the first time, frame the invite as joining; if
they are already signed in, one click just opens the thing.
- Signed out: a "Join to collaborate on <target>" lead-in renders above
the Google button (whose label GIS locks to "Continue with Google").
The target is the file name for document invites (from the preview,
falling back to the title) and the collection name otherwise.
- Signed in: the CTA is "Open <name>" for both kinds ("Open collection"/
"Open document" on legacy links); the busy label is "Opening…". The
collection join still happens on click — only the verb changed.
TDD: CTA-matrix tests updated first; 18/18 green, CSS lint at baseline.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The header's hand-drawn stacked-bars mark is replaced with the app's actual quarto-icon.svg (the segmented circle), tinted for dark mode via --logo-filter like the other uses of the asset. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Andrew's manual testing: clicking "People & invite…" in a collection's ⋯ menu did nothing. The activation click bubbled from the MenuItem to the Menu root's closer, whose onClose (closeAllMenus) also resets membersFor — cancelling the popover in the same React batch that requested it. Marked the item keepOpen (its onSelect already closes the menu explicitly), matching the component's documented escape hatch. TDD: new ProjectsHome.integration.test.tsx drives the real menu path (actions button -> menu item -> popover role=dialog), verified red then green; full integration suite 119/119. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Captured from the dev harness pages so reviewers can assess the revised invite screens next to the design mocks without running the branch. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
Implemented screensThe four invite-card states, for review without checking out the branch. The design mocks these implement are in Collection invite — signed out ("join to collaborate" lead-in above the Google button)Collection invite — signed in (one click; lands on the projects home with this collection promoted to the top)Document invite — signed outDocument invite — signed in (opens the editor on the shared file)Two further captures (the editor welcome banner and the post-join home with the promoted collection) were removed rather than posted misleading: the banner shot was a thin bar above a blank page and did not show its inline rename, and the projects-home harness fixture has no collections, so it could not show the promotion this PR adds. Purpose-built harness pages for both are coming; until then those two behaviors are best seen by running the branch, or see the plan doc. 🤖 Generated with Claude Code |
|
I think I will spend some time (maybe early next week) harmonizing the different styles a bit. The new editor updates give it a different feel. |
The welcome-banner capture was a 40px bar above 900px of blank page (the harness renders the banner alone, and it was captured fullPage), and it did not show the inline rename its caption advertised. The projects-home capture could not show the behavior the PR describes: the harness fixture FAKE_COLLECTIONS holds only the root set, so the page renders 'Everything else' with no collection sections — nothing of the promoted-collection landing. Replacements need harness pages built for the purpose; removing them beats leaving reviewers with artifacts that imply the wrong thing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>




Summary
Redesign of QuartoHub's two invite entry paths (bd-fxdcxbpq), per the design handoff in
design_handoff_invite_landing/(spec + mocks committed on this branch):InviteLandingcard for both invite kinds: kicker, inviter line, title, display-only payload preview, what-is-QuartoHub explainer, single CTA. ReplacesJoinCollectionLanding(the name/cursor-color form is gone; identity comes from the Google account with the anonymous fallback).buildInviteUrl/buildShareableUrlembedfrom=and a display-onlypreview=(base64url JSON, capped, never doc ids) built from the cached peek summaries. Legacy links parse unchanged.Out of scope (deferred per the handoff): zero-setup cold start, seeded first-run samples, revocable/role-scoped links, sender UI redesign.
Verification
useAutomergeSyncmock missingvfsAddFile, BranchBar).Plan and running log:
claude-notes/plans/2026-09-01-unified-invite-landing.md. Screenshots of the implemented screens follow in a comment.🤖 Generated with Claude Code