Skip to content

Allow anonymous users to read lessons (sign-in CTA for actions + shell)#10

Draft
Copilot wants to merge 6 commits into
mainfrom
copilot/allow-anonymous-read-lessons
Draft

Allow anonymous users to read lessons (sign-in CTA for actions + shell)#10
Copilot wants to merge 6 commits into
mainfrom
copilot/allow-anonymous-read-lessons

Conversation

Copy link
Copy Markdown

Copilot AI commented May 26, 2026

Lesson pages currently redirect unauthenticated visitors to /. This removes that gate so anyone can read lesson prose, while keeping every interactive action behind auth — surfaced in-context rather than as a hard redirect.

Changes

  • app/lessons/[slug]/page.tsx — drop if (!session) redirect("/"). Session is now optional; passedCheckIds short-circuits to an empty set for anonymous users. isSignedIn and callbackURL (/lessons/<slug>) are threaded into component builders.

  • app/sign-in-button.tsx — accept optional callbackURL prop (default: "/dashboard") so CTAs can return users to the lesson they were reading.

  • components/lesson/mdx-components.tsxbuildLessonComponents now accepts isSignedIn: boolean and callbackURL: string, forwarded to RunBlock and CheckCard.

  • components/lesson/RunBlock.tsx — swaps the ▶ Run button for a compact "Sign in to run" trigger when !isSignedIn. Uses useId() to assign a stable id to the container; the OAuth callbackURL includes a hash fragment (#<encoded-id>) so the browser scrolls back to the block after sign-in.

  • components/lesson/Check.tsx — swaps the Check button for "Sign in to check" when !isSignedIn. The check's own id is promoted to the HTML id attribute on the <aside>; the OAuth callbackURL includes #<check-id> so the browser scrolls back to the check card after sign-in.

  • components/lesson/SandboxSection.tsxuserId is now string | undefined; when absent, renders the unauthenticated panel and skips ensureBranchForLesson entirely (no Xata branch created for anonymous visitors).

  • components/lesson/BranchPanel.tsx — adds kind="unauthenticated" variant: a short message + <SignInButton callbackURL={lessonPath} />.

// Anonymous user sees this in the sidebar instead of the terminal
<BranchPanel kind="unauthenticated" callbackURL="/lessons/intro-to-select" />

// …and this inline instead of ▶ Run / Check buttons
<button onClick={() => signIn.social({ provider: "github", callbackURL })}>
  Sign in to run
</button>

API routes retain their existing 401 guards unchanged.

@vercel
Copy link
Copy Markdown

vercel Bot commented May 26, 2026

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

Project Deployment Actions Updated (UTC)
learn-postgres Error Error May 26, 2026 3:48pm

Copilot AI changed the title [WIP] Allow anonymous users to read lesson pages Allow anonymous users to read lessons (sign-in CTA for actions + shell) May 26, 2026
Copilot AI requested a review from exekias May 26, 2026 15:02
Copy link
Copy Markdown
Member

@exekias exekias left a comment

Choose a reason for hiding this comment

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

@copilot after clicking a "Sign in to check" / "Sign in to run" button, ideally the browser would scroll back to that button, make it possible if it's not too complex

Append a hash fragment to the OAuth callbackURL so the browser
scrolls to the triggering button after the sign-in redirect completes.

- RunBlock: useId() generates a stable per-instance id placed on the
  container div; the fragment is encodeURIComponent'd to handle the
  colon chars React inserts (:r0:, etc.).
- CheckCard: the check's own id (already used as data-check-id) is
  promoted to the HTML id attribute and appended as-is (check ids are
  plain alphanumeric slugs, no encoding needed).
Copy link
Copy Markdown
Author

Copilot AI commented May 26, 2026

@copilot after clicking a "Sign in to check" / "Sign in to run" button, ideally the browser would scroll back to that button, make it possible if it's not too complex

Done in f39dbc8. Each CTA now appends a hash fragment to the callbackURL so the browser scrolls back to the right element after the OAuth redirect completes:

  • CheckCard — the check's id (e.g. create-table-1) is set as the HTML id on the <aside> and appended as #create-table-1.
  • RunBlock — React's useId() generates a stable per-instance id placed on the container <div>; it's encodeURIComponent-encoded in the URL since React's generated IDs contain colons (:r0:%3Ar0%3A), which the browser decodes back when scrolling to the element.

Copy link
Copy Markdown
Member

@exekias exekias left a comment

Choose a reason for hiding this comment

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

Buttons don't work anymore, also the cursor doesn't change to the hand when hovering

The previous approach of embedding a hash fragment directly in the
OAuth callbackURL (/lessons/slug#%3Ar0%3A) caused better-auth to
silently reject the sign-in request, making the buttons appear to
do nothing.  The cursor was also missing because the classes lacked
cursor-pointer.

Changes:
- Add ScrollToAnchor client component that reads a stored anchor ID
  from sessionStorage on mount and scrolls to the element
- RunBlock: strip colons from useId() output (r0 instead of :r0:)
  so the HTML id is a safe URL fragment; call storeScrollAnchor()
  before signIn.social() instead of embedding the hash in the URL;
  add cursor-pointer to both Run and Sign-in-to-run buttons
- CheckCard: same pattern — storeScrollAnchor(check.id) before the
  redirect; add cursor-pointer to both Check and Sign-in-to-check buttons
- Lesson page: render <ScrollToAnchor /> so the stored anchor is
  consumed after the OAuth redirect lands back on the lesson
Copy link
Copy Markdown
Member

@exekias exekias left a comment

Choose a reason for hiding this comment

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

almost there, I want to see the hand cursor also on the shell button

Copy link
Copy Markdown
Author

Copilot AI commented May 26, 2026

almost there, I want to see the hand cursor also on the shell button

Fixed in 6a5a938 — added cursor-pointer to SignInButton and also disabled:cursor-not-allowed for the loading state.

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.

Allow anonymous users to read lessons (sign-in CTA for actions + shell)

2 participants