With accessibility semantics active, opening and leaving channels
retained disposed composer overlay states through Flutter’s semantics
traversal map. Those states kept departed route and message trees alive.
This upgrades the pinned Flutter SDK from 3.41.7 to 3.47.7, which
includes [the upstream
fix](3c96406cc4).
Includes SDK compatibility and formatter changes, preserves CocoaPods
integration, and gives create-channel radio lists a Material ink surface
required by the newer framework. Widget regressions verify both the
framework portal and the production channel/composer lifecycle: departed
routes, composer elements, and portal states are collectible while their
application’s semantics owner remains alive.
Related issue: no matching open issue or PR found.
### Validation
- [Current-head CI
passed](https://github.com/block/buzz/actions/runs/39085320351): all
mobile test shards, checks, Android APK, iOS release build, and native
simulator tests.
- Framework regression fails on Flutter 3.41.7 with three retained
states and passes on 3.47.7.
- Signed native Debug iOS Simulator build and paired-community
navigation replay: after 18 channel visits and forced GC, retained
disposed portal keys fell from 18 to 0 and route instances from 19 to 1.
- Review follow-up: all 365 focused channel/overlay tests, analyzer, and
complete mobile pre-push checks passed.
- Full local CI passed before the test-only review follow-up, including
Rust and desktop validation, frontend builds, analyzer checks, 3,499
mobile tests (four skipped), and the separate unconfigured-push test.
The replay used iOS 26.5 Simulator with accessibility semantics enabled,
CPU profiling disabled, and Debug/JIT builds. The before app was a
preinstalled emoji candidate; the after app uses this branch. Object
retention and the focused regression establish the fix; absolute heap
differences are not a controlled release-memory measurement. Android
native execution was not tested.
### Create-channel compatibility
The radio groups retain their layout after switching their container to
Material.
| Before (3.41.7) | After (3.47.7) |
| --- | --- |
| <img
src="https://raw.githubusercontent.com/block/buzz/42525e1bd9d9f264d0142c34ec61dbb6937ef758/pr-8419--01-before.png"
width="280" alt="Original create-channel radio groups"> | <img
src="https://raw.githubusercontent.com/block/buzz/42525e1bd9d9f264d0142c34ec61dbb6937ef758/pr-8419--02-after.png"
width="280" alt="Updated create-channel radio groups"> |
---------
Signed-off-by: Tom Brow <tomb@block.xyz>
Xcode 27 rejects generated CocoaPods targets with deployment versions
below iOS 15, even though the app already requires iOS 16. Raise lower
explicit pod deployment targets to the existing iOS 16 minimum during
installation, preserving targets that require a newer version.
### Validation
On main at 7852b1670, the ordinary unsigned Release build with exact
Xcode 27.0 failed on pod targets set to iOS 9–13. After this change and
a normal pod install, none of the 189 generated build configurations
specifies a target below iOS 16, and the same build passes
deployment-target validation without a command-line override.
The full build remains blocked by the independent swift-secp256k1 0.21.1
`UInt256.words` compiler ambiguity. This PR does not fix or change that
dependency. Xcode pin
[buzz-releases#110](https://github.com/squareup/buzz-releases/pull/110)
remains gated on a complete ordinary release build and its other
prerequisites.
Related: Wes’s [PR #8330](https://github.com/block/buzz/pull/8330)
independently includes a similar Podfile fix alongside DeviceHub
changes. This PR contains only the Podfile floor and its lockfile
checksum. Full local `just ci` passed; focused iOS validation above is
complete. The regenerated lockfile changes only the Podfile checksum;
`pod install --deployment` succeeds with CocoaPods 1.16.2.
---------
Signed-off-by: Tom Brow <tomb@block.xyz>
Apple Silicon simulator builds fail because MLImage and MLKit contain
device-labelled ARM64 objects.
Enable the Flutter ML Kit package's upstream compatibility helper, which
restores device labels for iPhone builds. This preserves the MLKit-based
functionality (avatar background removal) and complements the
notification-extension linker isolation in #7187.
Adds a complete iOS simulator build to CI to catch this build
regression.
---------
Signed-off-by: Tom Brow <tomb@block.xyz>
**Category:** new-feature
**User Impact:** Mobile users must confirm with Face ID, biometrics, or
their device passcode before sending their Buzz identity to Desktop.
**Problem:** A signed-in phone could send its full identity, including
the `nsec`, to a desktop without fresh local verification.
**Solution:** Require OS device authentication before opening the
identity-recovery scanner, retain that authorization only for the active
pairing session and short pairing window, and require fresh
authentication again if it expires before the identity payload is sent.
Normal app opening, identity import, and community removal remain
unchanged.
## Screencasts
| Enable Face ID | Use Face ID |
| --- | --- |
| 
| 
|
<details>
<summary>File changes</summary>
**Android and iOS integration**
- `mobile/android/app/build.gradle.kts` declares the AppCompat
dependency required by the biometric activity theme.
-
`mobile/android/app/src/main/kotlin/xyz/block/buzz/mobile/MainActivity.kt`
uses the activity type required by the system authentication prompt.
- `mobile/android/app/src/main/res/values/styles.xml` and
`mobile/android/app/src/main/res/values-night/styles.xml` use the
compatible launch theme.
- `mobile/ios/Podfile.lock` records the native local-authentication
dependency.
- `mobile/ios/Runner/Info.plist` explains why Buzz requests Face ID
access.
**Identity policy and pairing flow**
- `mobile/lib/shared/security/sensitive_action_authorizer.dart` wraps OS
authentication and maps platform errors to stable app-level outcomes.
- `mobile/lib/shared/community/community.dart` and
`mobile/lib/shared/community/community_storage.dart` persist the
sensitive-action policy.
- `mobile/lib/features/invites/invite_join_provider.dart` assigns the
explicit policy for invite-created communities.
- `mobile/lib/features/pairing/pairing_provider.dart` gates export,
binds grants to the active community/session, reauthenticates expired
grants, and clears grants on every terminal path.
- `mobile/lib/features/pairing/pairing_page.dart` lets users choose
biometric protection while importing an identity.
- `mobile/lib/features/settings/settings_page.dart` wires pairing into
settings.
- `mobile/lib/features/settings/settings_page/connection_section.dart`
authenticates before opening export recovery and bounds the
foreground-resume wait.
- `mobile/pubspec.yaml` and `mobile/pubspec.lock` add and lock
`local_auth`.
**Coverage**
- `mobile/test/shared/security/sensitive_action_authorizer_test.dart`
covers native result mapping, unsupported devices, and single-flight
behavior.
- `mobile/test/shared/community/community_test.dart` and
`mobile/test/shared/community/community_storage_test.dart` cover policy
defaults and persistence.
- `mobile/test/features/invites/invite_join_provider_test.dart` covers
the invite policy.
- `mobile/test/features/pairing/pairing_page_test.dart` covers import
protection controls.
- `mobile/test/features/pairing/pairing_provider_test.dart` covers
export/import authorization, stale/reset/concurrent guards, malformed
payload cleanup, and no-export failure paths.
- `mobile/test/features/settings/connection_section_test.dart` covers
the tap gate, lifecycle resume, and timeout behavior.
</details>
## Reproduction steps
1. Pair an identity into the mobile app.
2. Open Settings and choose “Send identity to desktop.”
3. Verify Face ID, biometrics, or the device passcode is required before
the recovery scanner opens.
4. Cancel device authentication and verify the scanner does not open and
no identity transfer begins.
5. Authenticate, scan a Desktop recovery code, confirm the SAS, and
verify the identity transfer completes.
## Validation
At `be5620f5f10aa6cc16e86a4f01f102f3d9aeef9b`:
- `cd mobile && ../bin/flutter analyze` — no issues
- `cd mobile && ../bin/flutter test` — 1,368 tests passed
- `cd mobile/android && JAVA_HOME=$(/usr/libexec/java_home -v 21)
./gradlew app:assembleDebug` — debug APK assembled successfully
---------
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
## What
- group uploaded photos into full-width message carousels
- add a fullscreen viewer with pinch zoom, double-tap reset, swipe-down
dismissal, a centered filmstrip, and image actions
- preload nearby display-sized images for smoother swiping and keep each
upload as its own avatar-backed message
## Validation
- `just mobile-check`
- `flutter test test/features/channels/message_content_test.dart`
- iOS 26.5 simulator gesture pass
---------
Signed-off-by: kenny lopez <klopez4212@gmail.com>