Add a "Send test notification" button to verify push delivery

Adds POST /api/notifications/test, which delivers a canned test push to every
registered device through the existing relay path (not persisted to the
notifications list), wired into the router and OpenAPI. In Settings, a "Send test
notification" button appears under the push toggle, only when push is enabled on
this device, so the full backend -> relay -> FCM -> APNs -> device path can be
verified on demand without waiting for a real device event.

Backend, API and widget tests added; clippy and dart analyze clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
rzuasti
2026-06-09 12:42:20 -04:00
co-authored by Claude Opus 4.8
parent c7c5b45ae8
commit 817ec5872c
7 changed files with 147 additions and 1 deletions
+33
View File
@@ -30,6 +30,7 @@ class _SettingsState extends State<Settings> {
late final PushService _pushService;
bool _pushEnabled = false;
bool _pushBusy = false;
bool _testBusy = false;
// Whether the backend delivers notifications via push. The per-device push
// toggle only makes sense then, so it stays hidden until this is confirmed.
bool _pushMethodActive = false;
@@ -136,6 +137,27 @@ class _SettingsState extends State<Settings> {
}
}
// Ask the backend to deliver a test push to every registered device, so the
// user can confirm push is working end to end without waiting for a real
// device event.
Future<void> _sendTestNotification() async {
setState(() => _testBusy = true);
try {
await BackendAPI.instance.sendTestNotification();
if (!mounted) return;
UISnackbars.showSuccess(
context,
'Test notification sent. It should arrive shortly.',
);
} catch (e) {
debugPrint('Failed to send test notification: $e');
if (!mounted) return;
UISnackbars.showError(context, 'Failed to send test notification');
} finally {
if (mounted) setState(() => _testBusy = false);
}
}
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
@@ -287,6 +309,17 @@ class _SettingsState extends State<Settings> {
value: _pushEnabled,
onChanged: _pushBusy ? null : _togglePush,
),
// Only useful once push is on for this device; sends a test push
// through the full backend → relay → device path.
if (_pushEnabled)
Align(
alignment: Alignment.centerLeft,
child: TextButton.icon(
onPressed: _testBusy ? null : _sendTestNotification,
icon: const Icon(Icons.notifications_active_outlined),
label: const Text('Send test notification'),
),
),
],
],
),