Skip to content

fix(chat): fall back to the iOS edit and delete windows when the server sends none - #1486

Merged
bmc08gt merged 1 commit into
code/cashfrom
fix/message-window-no-fallback
Sep 18, 2026
Merged

bmc08gt merged 1 commit into
code/cashfrom
fix/message-window-no-fallback

Conversation

@bmc08gt

@bmc08gt bmc08gt commented Sep 18, 2026

Copy link
Copy Markdown
Collaborator

message_edit_window and message_delete_window are message-typed, so the server can leave them unset — and it does. Android read that as no limit and kept Edit and Delete on the menu forever; iOS substitutes 15 minutes and 48 hours. The same message offered different rows depending on which app you opened it in.

This matches iOS. MessagePolicy.fromFlags substitutes FallbackEditWindow / FallbackDeleteWindow for anything the flags leave unset, and Default — the policy in force before flags are read — is now those two windows rather than unbounded. Every upstream state collapses to the same input: flags not yet fetched, a failed fetch, and unset window fields all arrive as null and all get the fallback, so there is no second path to keep in step.

The cost is that a message old enough can lose Edit or Delete where the server would in fact have taken the request. That is the trade iOS already made, and the opposite failure is worse: offering an affordance the server answers CANNOT_EDIT or CANNOT_DELETE.

The values are a guess

The proto documents what the two fields mean but never what an absent one implies, so 15 minutes and 48 hours are a product choice iOS made in code-payments/code-ios-app#724 — on the stated grounds that Android already used them, which was not true. Android had no such constant. The comment asserting the match is corrected in code-payments/code-ios-app#797, along with the design spec, which argued for the unbounded reading Android had implemented.

Nothing enforces the pair now that both sides carry it, so a test on each side asserts the literal values and names its counterpart; a failure is the prompt to change the other repo in the same release.

Whether real users ever reach this branch is still unknown — the cached-flag reads that could have answered it were all staff accounts.

Also

The MessagePolicy constructor no longer defaults either window. Absence is a real input, since it decides whether the fallback applies, so it is worth stating at each call site rather than inheriting. Two reducer tests that relied on the old unbounded default now build an explicit unbounded policy, because their bubbles are timestamped in 1970 and the fallback windows would otherwise strip Edit and Delete before the assertion could say anything about the reducer.

…er sends none

`message_edit_window` and `message_delete_window` are message-typed, so the
server can leave them unset, and it does. Android read that as no limit and kept
Edit and Delete on the menu forever; iOS substitutes 15 minutes and 48 hours. The
same message therefore offered different rows depending on the platform.

Match iOS. `MessagePolicy.fromFlags` substitutes `FallbackEditWindow` /
`FallbackDeleteWindow` for anything the flags leave unset, and `Default` — the
policy in force before flags are read — is now those two windows rather than
unbounded. Every upstream state collapses to the same input: flags not fetched,
a failed fetch, and unset window fields all arrive as null and all get the
fallback.

The cost is that a message old enough can lose Edit or Delete where the server
would have taken the request. That is the trade iOS already made, and the worse
failure is the opposite one: an affordance the server answers `CANNOT_EDIT` or
`CANNOT_DELETE`.

The values are a product choice, not one the contract supplies — the proto
documents what the fields mean but never what an absent field implies. Nothing
enforces the match with iOS, so a test on each side asserts the literal values
and names its counterpart.

The constructor no longer defaults either window. Absence decides whether the
fallback applies, so it is worth stating at the call site.
@bmc08gt bmc08gt self-assigned this Sep 18, 2026
@github-actions github-actions Bot added the type: fix Bug fix label Sep 18, 2026
@bmc08gt
bmc08gt merged commit 9a64241 into code/cash Sep 18, 2026
3 checks passed
@bmc08gt
bmc08gt deleted the fix/message-window-no-fallback branch September 23, 2026 16:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type: fix Bug fix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant