mirror of
https://github.com/block/buzz.git
synced 2026-11-12 20:31:12 +00:00
Push subscription setup ran synchronously inside the root widget build, so an invalid subscription could replace the entire app with Flutter's error surface. Push initialization and the cold-start native notification read could also prevent the app from rendering. Keep push work independent of the app subtree, observe asynchronous persistence failures, and retain the original errors and stack traces. Setup retries use bounded backoff; Settings shows a failure and offers manual retry without resetting navigation. Retry preserves a healthy revocation worker, and community-removal callbacks follow a recovered worker so lease cleanup continues after setup recovery. Related: [#8608](https://github.com/block/buzz/pull/8608) fixes the specific unrelated-mute input that exposed this startup coupling. ### Validation Added Flutter journeys cover invalid subscriptions, asynchronous storage failure, dependency initialization failure, retry exhaustion and manual recovery, navigation preservation, accessible retry actions, and pending/failed native startup reads with notification-target recovery. Both original rendering regressions failed before the fix. Community-removal coverage also verifies cleanup after a worker initialization failure and after retrying healthy setup; that regression failed before the worker fix. The full mobile suite passed (3,782 tests; 19 existing skips), as did the four unconfigured-build tests and static analysis. The corrected head also passed the full pre-push mobile checks and tests, plus 42 focused push and community tests. Rendered widget evidence uses mocked native platform channels; no device APNs delivery test was performed. Rendered Flutter widget evidence with synthetic data and mocked native platform views. ### Setup failure The app remains usable and Settings offers a retry after push initialization fails.  ### Recovery Retry clears the error without replacing the navigator or leaving Settings.  <!-- buzz-review-completed --> --------- Signed-off-by: Tom Brow <tomb@block.xyz>
79 lines
2.6 KiB
Dart
79 lines
2.6 KiB
Dart
part of '../settings_page.dart';
|
|
|
|
class _NotificationsSection extends ConsumerWidget {
|
|
const _NotificationsSection();
|
|
|
|
@override
|
|
Widget build(BuildContext context, WidgetRef ref) {
|
|
if (defaultTargetPlatform != TargetPlatform.iOS) {
|
|
return const SizedBox.shrink();
|
|
}
|
|
final community = ref.watch(activeCommunityProvider).value;
|
|
if (community == null) return const SizedBox.shrink();
|
|
final setup = ref.watch(buzzPushSetupStatusProvider);
|
|
final capability = ref.watch(currentRelayPushDescriptorProvider);
|
|
final hasCapability =
|
|
!capability.isLoading &&
|
|
!capability.hasError &&
|
|
capability.value != null;
|
|
final optOutPending =
|
|
community.pushSubscriptionState.pendingTombstoneGeneration != null;
|
|
if (!hasCapability &&
|
|
!community.pushNotificationsEnabled &&
|
|
!optOutPending &&
|
|
!setup.hasError) {
|
|
return const SizedBox.shrink();
|
|
}
|
|
final canToggle = hasCapability || community.pushNotificationsEnabled;
|
|
|
|
void setEnabled(bool enabled) {
|
|
unawaited(
|
|
ref
|
|
.read(communityListProvider.notifier)
|
|
.setPushNotificationsEnabled(community.id, enabled),
|
|
);
|
|
}
|
|
|
|
return AppListCard(
|
|
verticalPadding: Grid.twelve,
|
|
children: [
|
|
if (setup.hasError)
|
|
AppListRow(
|
|
key: const ValueKey('push-setup-retry'),
|
|
title: 'Notification setup failed',
|
|
subtitle: 'You can keep using Buzz. Tap to retry notifications.',
|
|
onTap: () => retryBuzzPushSetup(ref),
|
|
),
|
|
MergeSemantics(
|
|
key: const ValueKey('push-notifications-setting'),
|
|
child: Stack(
|
|
alignment: Alignment.centerRight,
|
|
children: [
|
|
AppListRow(
|
|
key: const ValueKey('push-notifications-enabled'),
|
|
title: 'Notifications',
|
|
// Reserve horizontal room without letting the switch's 48dp
|
|
// layout height expand this single-line row.
|
|
trailing: const SizedBox(width: 60),
|
|
onTap: canToggle
|
|
? () => setEnabled(!community.pushNotificationsEnabled)
|
|
: null,
|
|
),
|
|
Positioned.fill(
|
|
right: Grid.xs,
|
|
child: Align(
|
|
alignment: Alignment.centerRight,
|
|
child: Switch.adaptive(
|
|
value: community.pushNotificationsEnabled,
|
|
onChanged: canToggle ? setEnabled : null,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|