Skip to content

feat: PushIdentityReconciled and the push soft prompt - #39

Merged
anilcancakir merged 3 commits into
masterfrom
feature/push-reconciled-and-prompt
Sep 25, 2026
Merged

anilcancakir merged 3 commits into
masterfrom
feature/push-reconciled-and-prompt

Conversation

@anilcancakir

Copy link
Copy Markdown
Member

feat: emit PushIdentityReconciled per identity pass and ship the push soft prompt

NotificationManager.onPushIdentityReconciled emits (intent, converged, error)
for every reconcile pass that had a driver, after the pass, only when the
intent did not move under it, so a host that reports device state per person
can follow the identity the package now declares.

PushPrompt, PushPromptHost and PushOffNotice move here from an app: four
presentations of pushPromptAdvice, a decline timestamp under a host-supplied
vault key, re-reads on the driver's permission and identity streams and on a
driver attached after mount, and an onOpenPreferences callback instead of a
starter route. Copy through notifications.push_prompt.* keys, documented.

No version bump.

Part of the "framework-level support" round (plan 2 of 3): code that uptizm carried because a plugin lacked a seam moves into the owning package. Companion PRs: fluttersdk/magic, fluttersdk/magic_starter, fluttersdk/magic_notifications, fluttersdk/magic_deeplink, fluttersdk/wind, fluttersdk/magic_sentry, and the uptizm adoption.

This PR compiles and tests against its siblings' current default branches; nothing here depends on another PR in the round being merged first. No version bump and no publish.

… soft prompt

NotificationManager.onPushIdentityReconciled emits (intent, converged, error)
for every reconcile pass that had a driver, after the pass, only when the
intent did not move under it, so a host that reports device state per person
can follow the identity the package now declares.

PushPrompt, PushPromptHost and PushOffNotice move here from an app: four
presentations of pushPromptAdvice, a decline timestamp under a host-supplied
vault key, re-reads on the driver's permission and identity streams and on a
driver attached after mount, and an onOpenPreferences callback instead of a
starter route. Copy through notifications.push_prompt.* keys, documented.

No version bump.
@codecov

codecov Bot commented Sep 25, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 92.43697% with 18 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
lib/src/ui/components/push_prompt/push_prompt.dart 92.17% 18 Missing ⚠️

📢 Thoughts on this report? Let us know!

@kodizm

kodizm Bot commented Sep 25, 2026

Copy link
Copy Markdown

Note

Kodizm (AI-generated). May contain mistakes; verify before acting.

Looks correct and ready to merge; two minor points below.

The new emit in _runPushIdentityPass runs after the try/catch. It reads the _pushIdentityConverged / _pushIdentityError values that the pass itself just set, and it skips both the driver-less early return and a pass whose intent changed during it, which is what the docs say. The translation keys the widgets use match the list in doc/basics/preferences.md. Vault and advice failures are caught and logged instead of escaping as unhandled async errors.

Minor

lib/src/ui/components/push_prompt/push_prompt.dart:471: _read() calls can run concurrently and the last one to finish wins (correctness, narrow). If a permission or identity event fires _read(), and that call reads the vault before _decline() writes it but gets its advice back after _decline()'s _apply(at), it applies declinedAt: null again. The row goes back to the full ask until the next re-read. A generation counter in _apply, or re-reading the vault inside it, would close this. I found the race by reading the code, not by reproducing it.

README.md: CLAUDE.md's post-change checklist asks for a README update when features or API change. The README shows NotificationDropdown but says nothing about the new public PushPrompt / PushPromptHost / PushOffNotice widgets or onPushIdentityReconciled. The docs under doc/ do cover them.

Tests

test/push_identity_reconciled_test.dart covers the reconcile emit cases, and test/ui/components/push_prompt_test.dart (about 1,050 lines) covers the widgets. I skimmed the test files but did not read them line by line. I also did not open push_prompt.recipe.dart or push_prompt.preview.dart in detail.

CI

  • Lint & Test: success
  • codecov/patch: success (92.40% of the diff covered)
  • Auto-merge low-risk Dependabot PRs: skipped

@anilcancakir

Copy link
Copy Markdown
Member Author

@kodizm review. Both minors fixed: a generation counter drops a stale read that lands after a decline (new widget test, red first), and the README documents the prompt widgets and onPushIdentityReconciled.

@kodizm

kodizm Bot commented Sep 25, 2026

Copy link
Copy Markdown

Note

Kodizm (AI-generated). May contain mistakes; verify before acting.

Both earlier minors are fixed and this is ready to merge. One narrow ordering is still open, noted below as optional.

Since the last review, commit c867cba adds _generation to PushPromptHost, a new race widget test, and a README section. The README covers PushPrompt, PushPromptHost, PushOffNotice and onPushIdentityReconciled, and links to the docs. The counter covers the case I reported, where a read starts before a decline and answers after it. The new test holds that read open and checks it is dropped.

Minor

lib/src/ui/components/push_prompt/push_prompt.dart:581: The counter goes up before Vault.put has finished, so the opposite order is still open (correctness, narrow). If a permission or identity event fires _read() while the write is still running, that read gets the newer generation. It can then read the vault before the write lands and apply declinedAt: null, and the decline's own _apply is dropped. The row goes back to the full ask until the next re-read. Whether the read sees the old value depends on how the vault backend orders a read against a write that is still running, which I have not checked. Raising _generation only after _persistDeclinedAt succeeds would close this. It would also stop a failed write from dropping a read that is still in flight for no reason.

Tests

The new widget test a read overtaken by a decline covers the fix. test/push_identity_reconciled_test.dart and the rest of push_prompt_test.dart still cover the original change.

CI

  • Lint & Test: success
  • codecov/patch: success (92.46% of the diff covered)
  • Auto-merge low-risk Dependabot PRs: skipped

@anilcancakir

Copy link
Copy Markdown
Member Author

@kodizm review. The optional ordering is fixed too: the decline takes its generation only after the vault write lands, so a read that starts during the write is the stale one, and a failed write drops nothing (new widget test holding the write open, red first).

@kodizm

kodizm Bot commented Sep 25, 2026

Copy link
Copy Markdown

Note

Kodizm (AI-generated). May contain mistakes; verify before acting.

The ordering fix is correct and I have no findings left. Wait for CI to finish before merging.

Since the last review, commit 8856a69 changes _decline() so it takes ++_generation only after _persistDeclinedAt succeeds (lib/src/ui/components/push_prompt/push_prompt.dart:589). I checked the three cases:

  • Read starts during the write: it gets an older generation, so it is dropped once the decline applies.
  • Read starts after the write lands: it reads the stored decline, so it agrees with the decline whichever one applies last.
  • Write fails: _decline() returns before touching the counter, so no read that is still in flight gets dropped.

The increment runs synchronously right after the await, so no read can slip in between. This closes both orderings I raised before.

Tests

The new widget test a read that starts while the decline is still writing does not drop the decline holds put open with _HeldPutVaultService, fires a permission re-read, and then checks that the decline is still on screen. That covers this change.

CI

  • Lint & Test: CI still running, no result yet
  • Auto-merge low-risk Dependabot PRs: skipped

@anilcancakir
anilcancakir merged commit 66ff449 into master Sep 25, 2026
4 checks passed
@anilcancakir
anilcancakir deleted the feature/push-reconciled-and-prompt branch September 27, 2026 15:47
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