Skip to content

Messages: Push (repost) and Quote - #152

Closed
Adron wants to merge 1 commit into
issue-127-wire-typesfrom
issue-31-push-quote
Closed

Adron wants to merge 1 commit into
issue-127-wire-typesfrom
issue-31-push-quote

Conversation

@Adron

@Adron Adron commented Sep 16, 2026

Copy link
Copy Markdown
Member

Stacked PR — base is issue-127-wire-types, not main

This is the first of three stacked PRs (#31 → #32 → #33), all of which touch
FeedView.xaml / FeedViewModel.cs / MessageItemViewModel.cs.

It is based on issue-127-wire-types (open PR #131) because it depends on
the Message wire fields that branch adds — PushedMessageId, PushedMessage,
PushCount, and the IsPushOrQuote / IsQuote helpers. Merge #131 first, then
this.

What this does

POST /api/messages takes a pushedMessageId. One field covers both product
behaviours: a Push is a repost with empty content, a Quote is the same
call with commentary. Both are always public per /help/messages.

Service — Services/InterlinedApiClient.cs is untouched (it is in open
PR #130), so the write path is a new overload in the Messages partial:

  • Models/NewMessage.cs — a request record for POST /api/messages. The
    positional overload had run out of room (reply, schedule, media, cross-post,
    and now push/quote and tags). Optional keys the caller never set are omitted
    rather than posted as explicit nulls.
  • PostMessageAsync(NewMessage, …), plus PushMessageAsync /
    QuoteMessageAsync which hard-code publiclyVisible: true — that is a product
    rule, not the user's preference (defaultPubliclyVisible is false on the
    test account, so this is a real difference).

ViewModel — PushCount, the IsPush / IsQuote split, the quoted-original
projection off the nested pushedMessage, and the Push / Quote commands. A
Posted event lets FeedViewModel re-fetch after publishing, since the write
envelopes are not parsed (this codebase's read-after-write discipline).

View — ↻ Pushed / ❝ Quoted badge, the embedded original (avatar,
clickable author → profile, time, body, images), pushCount beside digCount,
and an inline Quote composer whose amber banner is the "this will be public"
confirmation. Two incidental fixes fell out of it:

  • the card footer is now a WrapPanel — seven actions overflowed a narrow window;
  • a per-card status line was added, because MessageItemViewModel.ErrorMessage
    had no renderer at all before this, so a failed action (or the "Reported.
    Thanks…" acknowledgement) was silent.

One deliberate client-side decision

Pushing a bare push targets the original rather than the push, so the
result is a one-level quote that actually renders instead of a chain of empty
cards. Quoting a Quote still targets the quote, which carries its own
commentary. No nested pushedMessage appeared anywhere in an 80-row live
sample, so the server's own chaining behaviour is unverified — this is the
client picking the renderable option, and it is commented as such.

Live verification (read-only)

Sampled GET /api/messages?limit=80 against the test account:

  • 5 of 80 rows carry pushedMessageId plus a full nested pushedMessage (a
    complete message object, user included — no second fetch needed to render).
  • Correction to the issue's note: 2 of those 5 have non-empty content —
    real Quotes, not only bare reposts. So the content-based IsQuote split is
    exercised by live data, not just theoretical.
  • All 5 are publiclyVisible: true, consistent with the always-public rule.
  • pushCount was non-zero on 6 of 80 rows, so the counter has real values to show.

Left unexercised, on purpose

No write path was run. Posting, pushing or quoting from this account
publishes publicly and syndicates to Mastodon/Bluesky, so PushMessageAsync,
QuoteMessageAsync and PostMessageAsync(NewMessage) are built and
live-unverified — the usual pattern in this repo. The request body follows
the same field names the existing positional PostMessageAsync already posts
successfully, with pushedMessageId added.

Notes for other open branches

Views/MentionText.cs (PR #141) will need a small reconcile: this PR adds a
Visibility binding to the card-body TextBlock (so a bare Push collapses its
empty body) on the same element PR #141 replaces the Text binding on. The
right rail in MainWindow.xaml (PR #149) is untouched.

Builds clean in both -c Debug and -c Release.

Closes #31

🤖 Generated with Claude Code

POST /api/messages takes a pushedMessageId; one field covers both Push (bare
repost, empty content) and Quote (repost with commentary). Adds the write path,
the card actions, and the embedded original.

- Models/NewMessage.cs — a request record for POST /api/messages. The endpoint
  has grown enough optional fields (reply, schedule, media, push/quote) that the
  positional overload in InterlinedApiClient.cs was out of room; that file is
  untouched (it is in open PR #130), so this is a new overload in the Messages
  partial. Optional keys the caller never set are omitted rather than posted as
  explicit nulls.
- PushMessageAsync / QuoteMessageAsync hard-code publiclyVisible: true — pushes
  and quotes are always public per /help/messages, whatever the user's
  defaultPubliclyVisible preference says (it is false on the test account).
- MessageItemViewModel: PushCount, the IsPush/IsQuote split, the quoted-original
  projection off the nested pushedMessage, and Push/Quote commands. A Posted
  event lets the feed re-fetch after publishing — the write envelopes are not
  parsed, per this codebase's read-after-write discipline.
- Pushing a *bare push* targets the original rather than the push, so the result
  is a one-level quote that renders. Quoting a Quote still targets the quote,
  which carries its own commentary. Client-side call: no nested pushedMessage
  appeared anywhere in an 80-row live sample, so the server's chaining behaviour
  is unverified.
- FeedView.xaml: "↻ Pushed" / "❝ Quoted" badge, the embedded original card
  (avatar, clickable author, time, body, images), pushCount beside digCount, and
  an inline Quote composer whose amber banner is the "this will be public"
  confirmation. The footer is now a WrapPanel — seven actions overflowed a
  narrow window. A per-card status line was added too: ErrorMessage had no
  renderer at all before, so a failed Report was silent.

Live-verified read-only against the test account (80-message sample): 5 of 80
rows carry pushedMessageId + a full nested pushedMessage. Contrary to the note
on the issue, 2 of those 5 have non-empty content — real Quotes, not only bare
reposts — so the content-based IsQuote split is exercised by live data. All 5
are publiclyVisible: true, consistent with the always-public rule.

No write path was exercised: posting, pushing or quoting from this account
publishes publicly and syndicates to Mastodon/Bluesky.

Closes #31

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@Adron

Adron commented Sep 24, 2026

Copy link
Copy Markdown
Member Author

Auto-closed by GitHub when its base branch issue-127-wire-types was deleted on merge of #131 — my error for merging with --delete-branch while this PR still pointed at it. Recreated as #181 from the same branch and commits, now based on main. Nothing was lost; the diff is identical.

I've switched to merging without --delete-branch for the rest of this pass, and will clean up branches at the end once no PR depends on them.

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.

1 participant