feat(messages): refresh link metadata after publishing - #94
Merged
Merged
Conversation
ComposeView now fires POST /api/messages/{id}/metadata after a successful
publish, matching the web composer, so a new post's OpenGraph previews
populate without waiting for something else to backfill them.
The call is guarded on the posted content actually containing a link and is
fire-and-forget: it is never awaited on the publish path and every failure is
swallowed, so it cannot surface an error or delay the success alert. The edit
path deliberately does not call it — no route accepts a content edit today
(see #76).
URL detection is extracted from the composer's live-preview property into
firstDetectedHTTPURL(in:) so the preview card and the publish guard share one
answer and the guard is directly testable.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017bss5MgZa7Jvj2m9zdaUd1
This was referenced Sep 16, 2026
Closed
…adata-after-publish
Adron
added a commit
that referenced
this pull request
Sep 17, 2026
One conflict, in APIClient.swift, resolved by taking this branch's version. Main carries the "No caller yet, deliberately" note #90 added to refreshMessageMetadata; that became stale the moment #94 wired the call from ComposeView, and this branch replaces it with a comment describing the route's actual response shape. Keeping both would have restated a claim the code now contradicts. Verified after resolving: the stale note is gone, the decode reads top-level `links` as required, and MessageLinkPreview has no remaining references. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017bss5MgZa7Jvj2m9zdaUd1
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.
Summary
Closes #91.
APIClient.refreshMessageMetadata(messageId:)was implemented and unit-tested but had nocaller, so a freshly published message's OpenGraph link previews stayed empty until something else
backfilled them.
ComposeViewnow firesPOST /api/messages/{id}/metadataafter a successfulpublish, the same way the web composer does (
components/messages/MessageInput.tsx:511).The call is guarded on the posted content actually containing a link, and it is fire-and-forget: it
is never awaited on the publish path and every failure is swallowed, so it can neither surface an
error nor delay the success alert or the dismiss. A 401 from this route is not escalated either —
the post that just succeeded proves the session is live.
The edit path deliberately does not call it: no route accepts a content edit today
(
PATCH /api/messages/{id}is the reschedule route, see #76). That is recorded as a comment on thenew helper.
What's included
ComposeView.postMessage()calls the newrefreshLinkMetadata(for:)after a successful publish,only when
firstDetectedHTTPURL(in: text) != nil. A link-free message issues no request.firstDetectedURLproperty into a file-levelfirstDetectedHTTPURL(in:)— the same detector the live preview card already used, now shared bythe publish guard and directly testable. (Same shape as
feedTruncatedinFeedView.swift.)ComposeLinkDetectionTests(9 tests) covering the detector and the publish guard, registeredin
project.pbxproj.refreshMessageMetadatareferenced in the issue is not onmain(it landed on a different branch), so the function's existing one-line doc comment is untouched.
Not included, deliberately: the response's links are not folded back into
AppDataStore's insertedfeed message. The refresh response is
[MessageLinkPreview]while the feed row rendersmessage.linkMetadata?.links([LinkMetadataItem]), so wiring that up means a new store mutationplus a shape conversion — out of scope here, and the web has the same behaviour (the preview lands on
the next feed fetch).
Testing
9140C403-1629-4BEC-8675-11757BDEB22C, serialized, E2E skipped:1156 tests, 0 failures.
ComposeLinkDetectionTests: 9 tests, 0 failures.http,https, first-of-several,mailtorejected.requestHistoryempty); linked content issuesexactly one
POSTto/api/messages/{id}/metadata; a 500 is swallowed without throwing.xcodebuild build: BUILD SUCCEEDED, no new warnings.and a real OpenGraph fetch.
🤖 Generated with Claude Code