Skip to content

bug(timeline): the Link action copies a login-walled URL, and Copy embed code is missing #38

Description

@Adron

Summary

The "Link" action added in PR #32 copies https://interlinedlist.com/messages/<id>. That is not the web app's shareable permalink — it is an authenticated route that redirects anonymous visitors to /login. Everyone a user shares a link with lands on a sign-in page instead of the message.

The web's shareable address is /user/<username>/status/<id>, and its Share menu offers two actions, of which we ship neither correctly:

  • Copy link → the public permalink
  • Get embed code → an HTML snippet for pasting into a blog/web page

Evidence (live, 2026-09-07)

GET https://interlinedlist.com/messages/d05faf17-…        → 200, final URL = /login
GET https://interlinedlist.com/user/hubcity/status/d05f…  → 200, serves the message (no auth)

/help/messages: "Every message has its own permanent page at an address like /user/<username>/status/<id> … Each message's action bar includes a Share button (the link icon) with two options: Copy linkGet embed code … Only public messages can be linked to or embedded for other people."

What we ship today

  • Packages/InterlinedDomain/Sources/InterlinedDomain/Models/MessagePermalink.swift — builds <base>/messages/<id>, documented as "matching the web app's own route". It does not.
  • Three call sites consume it: the message row's Link action, the "Push & Comment" body, and CreateIssueFromMessageViewModel's issue body — so a wrong URL is currently pasted into GitHub issues as well.

Division of labor

Domain — MessagePermalink

  • Change the shape to <base>/user/<username>/status/<id>. The author handle becomes a required input, so the API becomes url(forMessageID:authorUsername:base:).
  • Percent-encode the username with the same path-segment set already used for the id (drop / from urlPathAllowed).
  • Return nil when the author handle is missing or blank, so the UI hides the affordance rather than offering a broken link.
  • Add embedHTML(forMessageID:authorUsername:base:) only after the web's snippet is captured verbatim — do not invent the markup. Probe: open a public message on the web, click Share ▸ Get embed code, paste the clipboard into this thread, then implement to match.

Domain — Message

  • Confirm the author username is present on Message at every call site (timeline row, detail view, push-and-comment, create-issue). If a projection drops it, carry it.

App

  • MessageRowActions / MessageRowView: the Link action copies the corrected permalink; add a sibling Copy embed code action once the snippet is known.
  • Hide/disable both actions for a private message — the web states the link and embed only work for the owner. A copy action producing a link nobody else can open is worse than no action.
  • CreateIssueFromMessageViewModel and the Push & Comment body pick up the corrected URL for free; assert it in their tests.

Tests (BDD quartet)

  • happy — public message with a handle → /user/<handle>/status/<id>
  • invalid — blank/missing handle → nil, affordance hidden
  • upstream-failure — n/a (pure projection); instead assert the private-message path offers no link
  • boundary — handle and id needing percent-encoding; a base with a trailing slash

Acceptance criteria

  • Copying a link from a public message and opening it in a private browser window renders the message, not /login.
  • The URL in a GitHub issue created from a message resolves for someone who is not signed in.
  • Private messages expose no Link/Embed action.
  • Full E2E gate green (.claude/skills/swift-engineer/assets/e2e-gate-checklist.md).

Notes

Found during the 2026-09-07 parity sweep. Nothing regressed — the action shipped with the wrong shape in PR #32 — but it is user-visible on dev today, so it is first in the queue.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingparityWeb-parity gap with the InterlinedList web app

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions