mirror of
https://github.com/kuhyx/diet-guard.git
synced 2026-11-11 20:33:54 +01:00
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WzxdWYndZhdUrRGFEjBazQ
64 lines
2.2 KiB
Dart
64 lines
2.2 KiB
Dart
/// Browser-Notifications-API [NotificationBackend] for the desktop app.
|
|
///
|
|
/// A browser notification only exists while the page that raised it is open,
|
|
/// so this is a genuinely weaker surface than Android's: closing the desktop
|
|
/// window drops every pending reminder. That is accepted -- the PC's real
|
|
/// backstop is `diet-guard-gate.timer`, which locks the screen rather than
|
|
/// asking politely.
|
|
library;
|
|
|
|
import 'dart:js_interop';
|
|
|
|
import 'package:diet_guard_app/services/notification_backend.dart';
|
|
import 'package:web/web.dart' as web;
|
|
|
|
/// [NotificationBackend] over the browser's `Notification` API.
|
|
class WebNotificationBackend implements NotificationBackend {
|
|
/// Live notifications by slot, so [cancel] can close the right one. The
|
|
/// `tag` also makes the browser replace rather than stack a re-shown slot;
|
|
/// the map is what makes an explicit close possible.
|
|
final _shown = <int, web.Notification>{};
|
|
|
|
bool _granted = false;
|
|
|
|
@override
|
|
Future<void> initialize() async {}
|
|
|
|
@override
|
|
Future<bool?> requestPermission() async {
|
|
if (web.Notification.permission == 'granted') {
|
|
_granted = true;
|
|
return true;
|
|
}
|
|
if (web.Notification.permission == 'denied') return false;
|
|
final result = await web.Notification.requestPermission().toDart;
|
|
return _granted = result.toDart == 'granted';
|
|
}
|
|
|
|
@override
|
|
Future<void> show(int slot, String title) async {
|
|
// Posting without permission throws; a silent no-op matches this
|
|
// service's degrade-quietly stance everywhere else.
|
|
if (!_granted) return;
|
|
_shown[slot] = web.Notification(
|
|
title,
|
|
web.NotificationOptions(tag: 'diet_guard_slot_$slot'),
|
|
);
|
|
}
|
|
|
|
@override
|
|
Future<void> cancel(int slot) async {
|
|
_shown.remove(slot)?.close();
|
|
}
|
|
|
|
/// The page's own live notifications. Complete for this surface: a browser
|
|
/// notification cannot outlive the page that raised it in any way this
|
|
/// page could still close, so nothing posted by an earlier load (or an
|
|
/// older, hour-keyed build) is reachable to orphan.
|
|
@override
|
|
Future<Set<int>> activeIds() async => _shown.keys.toSet();
|
|
}
|
|
|
|
/// Opens the platform notification backend.
|
|
NotificationBackend openNotificationBackend() => WebNotificationBackend();
|