Skip to content

Add copyable shareable links for individual stories#1541

Draft
maebeale wants to merge 1 commit into
mainfrom
maebeale/cambridge-v2
Draft

Add copyable shareable links for individual stories#1541
maebeale wants to merge 1 commit into
mainfrom
maebeale/cambridge-v2

Conversation

@maebeale

@maebeale maebeale commented Jun 5, 2026

Copy link
Copy Markdown
Collaborator

Closes #1523

What is the goal of this PR and why is this important?

  • The story sharing surface previously showed only social share icons and a story count — there was no way to grab the direct URL for an individual story to paste into an email, message, or social post.
  • Stakeholders requested copy-paste shareable links per story so stories can be circulated easily.

How did you approach the change?

  • Added a reusable stories/_share_links partial that renders:
    • the story's direct public URL in a read-only field with a Copy link button
    • email + Facebook / X / LinkedIn / Pinterest share targets
  • Backed the copy button with a small copy-link Stimulus controller (clipboard write with a graceful execCommand fallback and a transient "Copied!" confirmation).
  • Wired the partial into the public story share page (story_shares/show), which previously had no share affordance at all.
  • Replaced the duplicated inline social markup on the story show page with the shared partial, so both surfaces stay in sync and now use the canonical public story_share_url.
  • Updated AGENTS.md (Stimulus controller list + count).

UI Testing Checklist

  • On a public story share page, the direct URL is shown and Copy link copies it to the clipboard (button shows "Copied!").
  • Email / Facebook / X / LinkedIn / Pinterest share links open with the correct pre-filled URL and title.
  • Story show page renders the same share section.

Anything else to add?

  • Added a request spec asserting the share page renders the direct story URL and copy affordance.
  • Note: this workspace was missing frontend deps; ran npm ci so Vite-built pages render in the test env.

The story sharing surface exposed only social share icons and a count,
with no way to grab the direct URL for a story to paste into email or
posts. Stakeholders need a copy-paste link per story (issue #1523).

Adds a reusable share section (direct URL + copy button, plus email and
social targets) to the public story share page and the story show page,
backed by a small copy-link Stimulus controller. The duplicated inline
social markup on the story show page is replaced by the shared partial.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 5, 2026 00:30
await navigator.clipboard.writeText(url)
} catch {
// Fallback for browsers without the async clipboard API
this.inputTarget.select()

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fallback path for browsers/contexts where the async Clipboard API is unavailable (e.g. non-HTTPS or older Safari). execCommand("copy") is deprecated but still the most reliable cross-browser fallback.

@@ -1,5 +1,4 @@
<% story_url = story_url(@story) %>
<% story_title = ERB::Util.url_encode(@story.title) %>
<% share_url = story_share_url(@story) %>

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Switched the copyable link from story_url (admin /stories/:id, auth-gated) to the canonical public story_share_url, so a copied link is actually shareable with non-logged-in recipients.

@@ -0,0 +1,50 @@
<%# Direct shareable link + social/email share targets for a single story.
Locals: story (decorated or plain), url (canonical public share URL string). %>

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Partial takes url as a local rather than deriving it, so each caller passes the appropriate canonical URL for its context while keeping the markup shared.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds a per-story sharing UI that exposes the canonical public story URL (with a copy-to-clipboard affordance) and standard social/email share targets, and reuses that UI across both the story show and public story share pages.

Changes:

  • Introduces a reusable stories/_share_links partial rendering a direct URL field + copy button and social/email share links.
  • Adds a copy-link Stimulus controller and registers it for clipboard copy + “Copied!” confirmation.
  • Adds a request spec assertion that the public story share page includes the direct share URL and “Copy link” text.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
spec/requests/story_share_spec.rb Adds request coverage asserting the share page renders the direct URL + copy affordance.
app/views/story_shares/show.html.erb Renders the new share links partial on the public story share page.
app/views/stories/show.html.erb Replaces duplicated inline social share markup with the shared partial (using story_share_url).
app/views/stories/_share_links.html.erb New partial providing copyable URL UI and social/email share links.
app/frontend/javascript/controllers/index.js Registers the new copy-link Stimulus controller.
app/frontend/javascript/controllers/copy_link_controller.js Implements clipboard copy + transient confirmation label behavior.
AGENTS.md Updates documented Stimulus controller list and controller count.

Comment on lines +15 to +16
class="w-full sm:max-w-md px-3 py-2 bg-gray-100 border border-gray-300 rounded font-mono text-sm overflow-x-auto whitespace-nowrap cursor-text"
>
Comment on lines +3 to +4
<% encoded_url = CGI.escape(url) %>
<% encoded_title = CGI.escape(story.title) %>
Comment on lines +24 to +26
<%= link_to "mailto:?subject=#{encoded_title}&body=#{encoded_url}",
class: "text-gray-600 hover:text-gray-800",
title: "Share by email" do %>
Comment on lines +29 to +31
<%= link_to "https://www.facebook.com/sharer/sharer.php?u=#{encoded_url}",
target: "_blank", rel: "noopener noreferrer",
class: "text-blue-600 hover:text-blue-800", title: "Share on Facebook" do %>
Comment on lines +34 to +36
<%= link_to "https://twitter.com/intent/tweet?url=#{encoded_url}&text=#{encoded_title}",
target: "_blank", rel: "noopener noreferrer",
class: "text-sky-500 hover:text-sky-700", title: "Share on X" do %>
Comment on lines +39 to +41
<%= link_to "https://www.linkedin.com/sharing/share-offsite/?url=#{encoded_url}",
target: "_blank", rel: "noopener noreferrer",
class: "text-blue-700 hover:text-blue-900", title: "Share on LinkedIn" do %>
Comment on lines +44 to +46
<%= link_to "https://pinterest.com/pin/create/button/?url=#{encoded_url}&description=#{encoded_title}",
target: "_blank", rel: "noopener noreferrer",
class: "text-red-600 hover:text-red-800", title: "Share on Pinterest" do %>
Comment thread AGENTS.md
|---|---|
| `app/frontend/entrypoints/` | Vite entry points (application.js, application.css) |
| `app/frontend/javascript/controllers/` | Stimulus controllers (34) |
| `app/frontend/javascript/controllers/` | Stimulus controllers (35) |
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.

Story sharing: provide shareable story links (not just a number)

2 participants