W8: Fold the refreshed link metadata into the just-posted feed row #96
Copy link
Copy link
Closed
Labels
enhancementNew feature or requestNew feature or requestws:papercutsW8 — small parity papercutsW8 — small parity papercuts
Description
Split out of #91 (see PR #94), which wired POST /api/messages/{id}/metadata after publishing but
stopped short of using its response.
Where it stands
After #94, posting a message with a link fires the metadata refresh. The preview appears on the
next feed fetch — not on the row that was just inserted locally. That matches the web, so this is
a polish item, not a bug.
Why it wasn't done in #94
Two different shapes:
- The feed row renders
message.linkMetadata?.links→[LinkMetadataItem](nestedmetadataobject) refreshMessageMetadatareturns[MessageLinkPreview](flatimage/title)
Folding one into the other needs a conversion plus a new AppDataStore mutation to update an
already-inserted message in place. That was outside "wire the existing call", so it was called out
rather than smuggled in.
Build this
- A conversion from
[MessageLinkPreview]to[LinkMetadataItem]— pure and unit-testable, not
inline in a view. - An
AppDataStoremutation that replaces thelinkMetadataof one already-inserted message by id. -
ComposeViewapplies the refresh response through it, still fire-and-forget: a failure must
leave the row exactly as it is, with no error surfaced. - No extra request — reuse the response feat(messages): refresh link metadata after publishing #94 already receives and currently discards.
Acceptance criteria
- Posting a message with a link shows its preview without waiting for a feed refresh.
- A failed or empty metadata response leaves the row unchanged and silent.
- Unit tests for the shape conversion (including an entry with no image and no title) and for the
store mutation targeting the right message.
Files: Views/ComposeView.swift, Services/AppDataStore.swift, Models/Message.swift.
Metadata
Metadata
Assignees
Labels
enhancementNew feature or requestNew feature or requestws:papercutsW8 — small parity papercutsW8 — small parity papercuts
Activity
Adron commentedon Sep 16, 2026
Implemented in #103 (stacked on #94). Closing here — further work tracks in the PR.
Read before merging: the conversion is correct but the pipeline is inert
The two shapes convert cleanly, so #103 does what this issue asked. But nothing user-visible
changes yet, because of two problems on either side of it — neither introduced by #103, both
verified independently:
refreshMessageMetadatadecodes a shape the route never sends — filed as Bug: refreshMessageMetadata decodes a response shape the route never sends — always returns [] #104. It expects{"metadata":{"links":[…]}};app/api/messages/[id]/metadata/route.ts:105returnsserialize({ links })— top-level, with fullLinkMetadataItementries. Against the live API thedecode yields
[]every time. The existing test passes only because its fixture was written tothe assumed shape.
FeedViewcannot see an in-place row update — filed as Bug: FeedView cannot see in-place row updates — merges only unseen ids, keyed on count #105. It mirrorsstore.feedMessagesinto a private
@Stateand merges only unseen ids, keyed on.count(FeedView.swift:385).Replacing a row's
linkMetadatachanges content, not count.#103 is still worth landing: the conversion and the store mutation are exactly what both fixes will
consume, and they are tested. But the chain only becomes visible once #104 and #105 land.
Note this does not invalidate #94 — that PR fires the POST and ignores the response, and the POST
still makes the server fetch and persist the metadata, so "preview appears on the next feed fetch"
works today.