Skip to content

Reply, Favourite and Boost buttons on mention notifications - #31

Merged
masonasons merged 1 commit into
masonasons:mainfrom
serrebidev:push-notification-actions
Oct 20, 2026
Merged

masonasons merged 1 commit into
masonasons:mainfrom
serrebidev:push-notification-actions

Conversation

@serrebidev

@serrebidev serrebidev commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Why

FastSMRW's Android notifications have no buttons. FastSmMessagingService.show builds them with only a content intent. Screen-reader users who handle notifications from the shade, or from a launcher that exposes notification actions, can't reply to or react to a mention without opening the app. Other Mastodon clients offer these buttons.

What

Core (all logic stays here, per CLAUDE.md):

  • SocialAccount::owns_access_token(token) and SocialAccount::notification_status(id), implemented for Mastodon. notification_status calls GET /api/v1/notifications/:id and maps its status.
  • New command push_action: {access_token, notification_id, action: "favourite"|"boost"|"reply", text?} returns push_action_result {ok, action, notification_id}.
    • Mastodon's Web Push payload already carries access_token and notification_id, so the button acts as the account the push was sent to.
    • Reply builds a PostDraft the way a timeline reply does. It uses present::mention_prefix and keeps the post's visibility, so a DM stays a DM.
    • If the core was never started (the app is closed), push_action loads the accounts as start does but builds no timelines.
  • Test: test_push_action in tests/test_push.cpp covers:
    • picking the account by token
    • the favourite and reblog URLs
    • the reply body: in_reply_to_id, visibility=direct and the mention
    • failures for an unknown token, an empty reply, and a notification without a post

Android:

  • Pushes of type mention and status get Reply (RemoteInput), Favourite and Boost buttons.
  • New PushActionReceiver (not exported):
    • runs a short-lived FastSmCore for the press, sends push_action and waits up to 8 s inside goAsync
    • shows a toast ("Reply sent", "Boosted", "Favourited", or a "Couldn't …" message) that screen readers read
    • cancels the notification when the action worked
  • docs/changelog.txt: one line.

Not done: iOS parity

iOS already registers a category per push type with actions: [] (PushManager.swift). Adding UNNotificationActions that send the same push_action command would give parity. I can't build or test iOS here, so I left it for you rather than ship untested Swift. I can add it if you want it in this PR.

Testing

  • Android: assembleDebug builds, which compiles the core changes for both ABIs.
  • C++ unit tests: not run. I did not run build.bat test on Windows, so test_push_action is written but has never been compiled into the test binary or executed.
  • Device: Galaxy Tab S10+, Android 16, signed in to a real Mastodon account. A debug build has no google-services.json, so real pushes can't arrive. For the test only, I made the receiver exported in an uncommitted src/debug manifest and sent its broadcast with adb.
    • Wrong token: nothing changed on the server.
    • The account's real token, favouriting through a favourite notification about the account's own post: the server reported favourited: true about a second later. The favourite was then undone.
  • Not tested live:
    • the buttons on a real Firebase push
    • Reply against a server (it would have posted publicly; the unit test covers the request it builds)

Notes

  • Creating a core starts the miniaudio engine in SoundManager's constructor. During a button press that opens a silent output stream for about a second (Samsung logs "background playback would be muted"). Nothing is heard. A lazy engine start would avoid it if you'd prefer.
  • If the app is open, the receiver's core runs beside the app's own core in the same process. It reads config.json and never saves it; it does append a few lines to fastsm.log, as every core does.

🤖 Generated with Claude Code

Mastodon's Web Push payload carries the notification id and the
account's access token. A new core command, push_action, finds the
account by that token, looks up the notification's post
(GET /api/v1/notifications/:id) and favourites, boosts or replies to
it, with the same mentions and visibility as a reply from the timeline.
It loads the accounts itself when the core was never started, so it
works with the app closed.

On Android, mention and status pushes get the three buttons; Reply
takes typed text through RemoteInput. PushActionReceiver runs a
short-lived core for the press and toasts the outcome.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@masonasons
masonasons merged commit 74e598e into masonasons:main Oct 20, 2026
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.

2 participants