镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

mobile: add follow and remind actions to inbox row menu - #9142

Merged
brow merged 3 commits into
mainfrom
inbox-menu-follow-remind
Nov 9, 2026
Merged

brow merged 3 commits into
mainfrom
inbox-menu-follow-remind

Conversation

@brow

@brow brow commented Nov 5, 2026 •

Copy link
Copy Markdown
Collaborator

Long-pressing an Inbox row opened a sheet with only Mark as read/unread and Open conversation. To follow a thread or set a reminder, you had to open the conversation and long-press the message itself.

The row sheet now also offers:

  • Follow thread / Unfollow thread: acts on the conversation's thread root, or on the message itself for a top-level row (the same rule the message actions sheet uses). It's hidden for Solo chats, which have no threads, and for grouped DM conversations, which have no single thread to follow.
  • Remind me: opens the existing "Remind me later" sheet for the row's latest message and publishes the same kind-30300 reminder. If that message already has a pending reminder, the new time reschedules it: the event reuses its d tag (an addressable replace) and keeps its note, so you don't get a second reminder. It loads the reminder list first, because otherwise the list loads only with the Reminders filter. It's hidden when signed out, like the message sheet.

The sheet is now scrollable so the four rows fit on short screens and at large text sizes (without it, the new test overflowed at the default test surface).

Demo

Long-press a channel mention row in the Inbox, follow the thread, reopen the sheet (now Unfollow thread), then set an "In 1 hour" reminder. The rows are agent test chatter from a pilot community.

demo.mp4

Validation

  • Added tests: one Activity page journey that follows and unfollows a thread row, follows a top-level row, then sets a reminder through the sheet and checks the published target (event, channel, preview). Further tests cover rescheduling an existing pending reminder (same d tag, note kept, "Reminder updated"), hiding Follow in a Solo chat, and hiding Remind me when signed out. The test/features/activity, test/shared/reminders and message-actions suites pass.
  • Re-run on the branch head (99b2418) in an iPhone 17 Pro simulator (iOS 27) debug build, same community: long-pressed a thread-capable channel mention row, tapped Follow thread, reopened the sheet and saw Unfollow thread, tapped Remind me and then "In 1 hour", and saw the "Reminder set" toast (this is the video above). Long-pressing a Solo chat row showed no Follow thread row, as intended. Not covered on the simulator: rescheduling an existing reminder, signed-out state, large text sizes. Those are covered by the widget tests.
  • Automated app run on an iPhone 17 Pro simulator (iOS 27) signed in to the Remote Agents Pilot community: opened Inbox, long-pressed a channel mention row, followed the thread, reopened the sheet and saw Unfollow thread, then set an "In 1 hour" reminder and saw the "Reminder set" toast. That run predates the reschedule and Solo changes, which are covered by the widget tests above. I didn't re-run unrelated Inbox flows (swipe to mark read, filters) on the simulator. Those are covered by the existing widget tests.

🤖 Generated with Claude Code

Long-pressing an Inbox row now offers Follow/Unfollow thread (the
conversation's thread root, or the message itself for top-level rows;
hidden for grouped DM conversations) and Remind me (the shared reminder
sheet for the row's latest message; hidden when signed out). The sheet
scrolls so the extra rows fit on short screens and large text.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tom Brow <tomb@squareup.com>
@brow

brow commented Nov 5, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 5, 2026 •

Copy link
Copy Markdown
Codex Review Summary: ✅ Completed

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-08T21:54:51.004878Z 99b2418 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b0ade6a182

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +436 to +439
showRemindMeLaterSheet(
context: context,
ref: ref,
target: ReminderTarget(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Refresh the cached reminders after creation

When remindersProvider has already been initialized—for example, after opening Reminders and returning to All—this call publishes the reminder but neither refreshes nor invalidates that non-auto-dispose provider. Switching back to Reminders therefore renders the cached pre-creation list even after the “Reminder set” confirmation, until the user manually pulls to refresh. Refresh or invalidate the provider after successful creation, and extend the journey to assert that the new reminder becomes visible rather than only inspecting the capturing service.

AGENTS.md reference: AGENTS.md:L828-L830

Useful? React with 👍 / 👎.

Remind me on an Inbox row now loads the reminder list first and, when the
message already has a pending reminder, republishes it under the same d
tag (keeping its note) instead of adding a second reminder. Follow is no
longer offered on Solo chat rows, which have no threads.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tom Brow <tomb@squareup.com>
@brow
brow marked this pull request as ready for review November 8, 2026 21:51
@brow
brow requested a review from a team as a code owner November 8, 2026 21:51
@github-actions github-actions Bot added the codex-security-review-current The posted Codex security review matches its recorded range. label Nov 8, 2026
@github-actions

github-actions Bot commented Nov 8, 2026 •

Copy link
Copy Markdown

🔐 Codex Security Review

Note: This is an automated, security-focused review generated by Codex.
Use it as a supplement to human review; false positives are possible.

Scope

  • Exact PR diff: 8cce12f591988f29cc2beab5ff0c89d51862f31b...17117ae7bf917e63a67c5a7a6c8d94a3fd418ebd
  • Model: gpt-6.1-sol

💡 Click "edited" above to see earlier reviews for this PR.


Review Summary

Overall Risk: MEDIUM

Three reliability defects can silently lose reminder reschedules, create duplicate reminders, or break actions during live inbox updates.

Findings

[MEDIUM] Ensure rescheduled reminders have a strictly newer timestamp

  • Category: Event Integrity
  • Location: mobile/lib/shared/reminders/reminder_service.dart:124 (source)
  • Description: Reusing the d tag does not guarantee replacement: SignedEventRelay uses the current wall-clock timestamp, while the database selects the highest timestamp and breaks ties by event ID. If an existing reminder came from a device whose clock is ahead, the reschedule is superseded. Ingest still returns an accepted duplicate acknowledgement, so the sheet reports Reminder updated despite retaining the old schedule. Desktop rescheduling already avoids this by advancing the existing timestamp.
  • Impact: A reschedule can silently fail, leaving the reminder due at its previous time.
  • Recommendation: Pass the existing reminder's createdAt through the sheet and service, and sign replacements with the greater of the current timestamp and existing.createdAt plus one.

[MEDIUM] Refresh the reminder cache before choosing a replacement

  • Category: Reliability
  • Location: mobile/lib/features/activity/activity_page/inbox_row.dart:438 (source)
  • Description: Reading remindersProvider.future returns its cached result once loaded. RemindersNotifier has no live subscription, and reminder creation from the channel message sheet does not invalidate it. For example, load the Reminders filter, create a reminder from a channel message, then return to its inbox row without reconnecting. This lookup misses the existing reminder and creates another random d tag instead of rescheduling it.
  • Impact: The original reminder remains scheduled alongside the new one, producing duplicate notifications.
  • Recommendation: Fetch fresh reminders before duplicate detection, or keep the provider synchronized through live updates and invalidation from every reminder write.

[MEDIUM] Keep modal callbacks independent of the inbox row lifetime

  • Category: Reliability
  • Location: mobile/lib/features/activity/activity_page/inbox_row.dart:470 (source)
  • Description: The callback retains the row's WidgetRef after opening the reminder sheet. ActivityNotifier refreshes on incoming messages, and ActivityPage keys rows by their latest event ID, so a new message in the same conversation can dispose the originating row while the sheet remains open. After publication succeeds, onSaved reads the disposed reference and throws; submit catches that exception as a reminder creation failure and skips the refresh. The new follow callback similarly reads the disposed row reference if a live update occurs while its menu is open.
  • Impact: Live inbox updates can prevent follow actions or make successfully saved reminders appear to have failed, encouraging duplicate retries.
  • Recommendation: Use a reference owned by the modal or a longer-lived page, and check lifecycle and community scope before completion callbacks.

Notes

  • Review used read-only source inspection. Builds and tests were not executed, as requested.

Generated by Codex Security Review |
Requested by: @brow |
Workflow run

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 99b2418a01

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

import '../../shared/reminders/remind_me_later_sheet.dart';
import '../../shared/reminders/reminder_service.dart'
show ReminderTarget, reminderServiceProvider;
import '../channels/thread_follows/thread_follows_provider.dart';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Move thread-follow state behind a shared boundary

Move threadFollowsProvider out of the channels feature, or expose this operation through a shared abstraction, rather than adding another direct activity → channels dependency. The repository explicitly prohibits feature modules from importing other feature modules, and this import couples the inbox action to another feature's internal state implementation.

AGENTS.md reference: AGENTS.md:L789-L790

Useful? React with 👍 / 👎.

existing: existing == null
? null
: (dTag: existing.id, note: existing.note),
onSaved: () => unawaited(ref.read(remindersProvider.notifier).refresh()),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Capture the reminder notifier before awaiting the relay

When relay acknowledgement is slow and the user switches to the Reminders filter or otherwise removes this row after choosing a time, the _InboxRow is disposed before onSaved runs. This closure then calls ref.read on a disposed WidgetRef; the synchronous error is caught by the sheet as though creation failed even though the relay already accepted the reminder, and the reminder list is not refreshed. Capture a notifier or container while the row is still mounted so the post-save callback does not depend on the row's lifecycle.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Keep callback failures separate from reminder publication

A live message can re-key and dispose this row while the sheet stays open. Besides capturing the notifier before opening the sheet, could onSaved run outside the publication error handler? Otherwise a refresh callback exception reports a successful save as failed and encourages duplicate retries.

Posted by Codex (gpt-6.1-sol) on behalf of kylez

Comment on lines +378 to +381
String? get _followRootId {
if (channel?.isSolo ?? false) return null;
if (item.conversationId.startsWith('dm:')) return null;
return item.conversationId;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Hide Follow until channel metadata is available

On a cold start where the activity feed resolves before channelsProvider, channel is null and this condition treats the row as non-Solo; top-level DM rows are also not yet grouped under a dm: conversation ID. Long-pressing during that window therefore exposes Follow for Solo chats or DMs and can persist a meaningless thread follow. Treat unresolved channel metadata as ineligible for Follow, or defer the action until the channel type is known.

Useful? React with 👍 / 👎.

@github-actions github-actions Bot removed the codex-security-review-current The posted Codex security review matches its recorded range. label Nov 8, 2026
@github-actions github-actions Bot added the codex-security-review-current The posted Codex security review matches its recorded range. label Nov 8, 2026

@kazemek21 kazemek21 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kyle reviewed this PR and the Nexus review-tracker notes. Approved with the non-blocking C1 feedback in the existing reminder callback thread.

Posted by Codex (gpt-6.1-sol) on behalf of kylez

@brow
brow merged commit 40cfa72 into main Nov 9, 2026
53 checks passed
@brow
brow deleted the inbox-menu-follow-remind branch November 9, 2026 14:59
bostonaholic added a commit that referenced this pull request Nov 9, 2026
…ction-p

* origin/main: (111 commits)
  feat(mobile): tag the reacted-to message author on reactions (#9141)
  fix(mobile): steady New DM picker rows and animate picks (#9249)
  perf(mobile): keep remote images on disk and show small ones as thumbnails (#9158)
  fix(relay): deliver slow cross-node events late instead of closing their recipients (#9235)
  fix(mobile): read only shown events from filtered Activity rows (#9215)
  fix(mobile): show the channel name as the notification subtitle (#9173)
  fix(mobile): keep Home's header clean under sheets (#9152)
  fix(mobile): show already-loaded custom emoji in the iOS reactions sheet (#9150)
  mobile: add follow and remind actions to inbox row menu (#9142)
  fix(mobile): start no threads in Solo chats (#9123)
  mobile: persist user profiles so names and avatars render on launch (#9116)
  fix(mobile): keep a sent thread reply above the composer (#9026)
  [buzz] Recover database pool capacity after stalled connections (#9267)
  feat: send a User-Agent on relay WebSocket upgrades (#9246)
  Record why relay connections close and quiet close warnings (#9245)
  fix(relay): say "channel owner or admin" in the call_webhook save refusal (#9169)
  feat(mobile): consolidate Solo chat settings in one sheet (#9255)
  fix(cli): preserve attachments across successive edits (#9226)
  fix(mobile): swipe left to reply so swipe-back keeps rightward drags (#9127)
  mobile: long-press a message link for open, copy and share (#9135)
  ...

Signed-off-by: Matthew Boston <mboston@squareup.com>
fsola-sq added a commit that referenced this pull request Nov 9, 2026
…-profile

* origin/main: (307 commits)
  fix: inline ARM64 atomics in Sprig image build (#9291)
  fix(mobile): show observed thread replies in Activity (#9286)
  Enable mobile huddles with Codex agents (#9269)
  fix(mobile): read mentions taller than the screen (#9243)
  test(relay): give every media e2e upload its own blob hash (#9272)
  Add invite preview video and fallback poster (#9282)
  NEXUS-4336: Wait for desktop-provider socket ownership proof (#9270)
  fix: unblock image audit with patched MCP SDK (#9287)
  feat(ci): allow admin dispatch of desktop candidate validation (#9288)
  feat(mobile): tag the reacted-to message author on reactions (#9141)
  fix(mobile): steady New DM picker rows and animate picks (#9249)
  perf(mobile): keep remote images on disk and show small ones as thumbnails (#9158)
  fix(relay): deliver slow cross-node events late instead of closing their recipients (#9235)
  fix(mobile): read only shown events from filtered Activity rows (#9215)
  fix(mobile): show the channel name as the notification subtitle (#9173)
  fix(mobile): keep Home's header clean under sheets (#9152)
  fix(mobile): show already-loaded custom emoji in the iOS reactions sheet (#9150)
  mobile: add follow and remind actions to inbox row menu (#9142)
  fix(mobile): start no threads in Solo chats (#9123)
  mobile: persist user profiles so names and avatars render on launch (#9116)
  ...

Signed-off-by: Franco Sola <fsola@squareup.com>

# Conflicts:
#	docs/nips/NIP-FA.md
#	docs/nips/NIP-FI.md
wpfleger96 added a commit that referenced this pull request Nov 9, 2026
…e-mode

* origin/main: (36 commits)
  feat(relay): let an operator mint invites for a community owner or admin (#9265)
  fix: inline ARM64 atomics in Sprig image build (#9291)
  fix(mobile): show observed thread replies in Activity (#9286)
  Enable mobile huddles with Codex agents (#9269)
  fix(mobile): read mentions taller than the screen (#9243)
  test(relay): give every media e2e upload its own blob hash (#9272)
  Add invite preview video and fallback poster (#9282)
  NEXUS-4336: Wait for desktop-provider socket ownership proof (#9270)
  fix: unblock image audit with patched MCP SDK (#9287)
  feat(ci): allow admin dispatch of desktop candidate validation (#9288)
  feat(mobile): tag the reacted-to message author on reactions (#9141)
  fix(mobile): steady New DM picker rows and animate picks (#9249)
  perf(mobile): keep remote images on disk and show small ones as thumbnails (#9158)
  fix(relay): deliver slow cross-node events late instead of closing their recipients (#9235)
  fix(mobile): read only shown events from filtered Activity rows (#9215)
  fix(mobile): show the channel name as the notification subtitle (#9173)
  fix(mobile): keep Home's header clean under sheets (#9152)
  fix(mobile): show already-loaded custom emoji in the iOS reactions sheet (#9150)
  mobile: add follow and remind actions to inbox row menu (#9142)
  fix(mobile): start no threads in Solo chats (#9123)
  ...

Signed-off-by: Will Pfleger <wpfleger@block.xyz>

This branch was previously deployed

1 inactive deployment
codex-review — 17117ae7 Deployed Nov 8, 2026 by brow via Run Codex Security Review #13898
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

codex-security-review-current The posted Codex security review matches its recorded range.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants